diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java new file mode 100644 index 00000000000..697bdefef8f --- /dev/null +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -0,0 +1,256 @@ +package com.wordnik.swagger.codegen.languages; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.wordnik.swagger.codegen.*; +import com.wordnik.swagger.models.Model; +import com.wordnik.swagger.models.Operation; +import com.wordnik.swagger.models.parameters.HeaderParameter; +import com.wordnik.swagger.models.parameters.Parameter; +import com.wordnik.swagger.models.properties.*; +import org.apache.commons.lang.StringUtils; + +import javax.annotation.Nullable; +import java.util.*; +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { + private static final Pattern PATH_PARAM_PATTERN = Pattern.compile("\\{[a-zA-Z_]+\\}"); + protected String sourceFolder = "Classes/Swaggers"; + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "swift"; + } + + public String getHelp() { + return "Generates a swift client library."; + } + + public SwiftGenerator() { + super(); + outputFolder = "generated-code/swift"; + modelTemplateFiles.put("model.mustache", ".swift"); + apiTemplateFiles.put("api.mustache", ".swift"); + templateDir = "swift"; + apiPackage = "/APIs"; + modelPackage = "/Models"; + + // Inject application name + String appName = System.getProperty("appName"); + if (appName == null) { + appName = "SwaggerClient"; + } + additionalProperties.put("projectName", appName); + + // Inject base url override + String basePathOverride = System.getProperty("basePathOverride"); + if (basePathOverride != null) { + additionalProperties.put("basePathOverride", basePathOverride); + } + + // Make all the variable optional + String suppressRequired = System.getProperty("suppressRequired"); + if (suppressRequired != null) { + additionalProperties.put("suppressRequired", suppressRequired); + } + + sourceFolder = appName + "/" + sourceFolder; + + supportingFiles.add(new SupportingFile("Cartfile.mustache", "", "Cartfile")); + supportingFiles.add(new SupportingFile("APIHelper.mustache", sourceFolder, "APIHelper.swift")); + supportingFiles.add(new SupportingFile("AlamofireImplementations.mustache", sourceFolder, "AlamofireImplementations.swift")); + supportingFiles.add(new SupportingFile("Extensions.mustache", sourceFolder, "Extensions.swift")); + supportingFiles.add(new SupportingFile("Models.mustache", sourceFolder, "Models.swift")); + supportingFiles.add(new SupportingFile("APIs.mustache", sourceFolder, "APIs.swift")); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "Int", + "Float", + "Double", + "Bool", + "Void", + "String", + "Character") + ); + defaultIncludes = new HashSet( + Arrays.asList( + "NSDate", + "Array", + "Dictionary", + "Set", + "Any", + "Empty", + "AnyObject") + ); + reservedWords = new HashSet( + Arrays.asList( + "class", "break", "as", "associativity", "deinit", "case", "dynamicType", "convenience", "enum", "continue", + "false", "dynamic", "extension", "default", "is", "didSet", "func", "do", "nil", "final", "import", "else", + "self", "get", "init", "fallthrough", "Self", "infix", "internal", "for", "super", "inout", "let", "if", + "true", "lazy", "operator", "in", "COLUMN", "left", "private", "return", "FILE", "mutating", "protocol", + "switch", "FUNCTION", "none", "public", "where", "LINE", "nonmutating", "static", "while", "optional", + "struct", "override", "subscript", "postfix", "typealias", "precedence", "var", "prefix", "Protocol", + "required", "right", "set", "Type", "unowned", "weak") + ); + + typeMapping = new HashMap(); + typeMapping.put("array", "Array"); + typeMapping.put("List", "Array"); + typeMapping.put("map", "Dictionary"); + typeMapping.put("date", "NSDate"); + typeMapping.put("Date", "NSDate"); + typeMapping.put("DateTime", "NSDate"); + typeMapping.put("boolean", "Bool"); + typeMapping.put("string", "String"); + typeMapping.put("char", "Character"); + typeMapping.put("short", "Int"); + typeMapping.put("int", "Int"); + typeMapping.put("long", "Int"); + typeMapping.put("integer", "Int"); + typeMapping.put("Integer", "Int"); + typeMapping.put("float", "Float"); + typeMapping.put("number", "Double"); + typeMapping.put("double", "Double"); + typeMapping.put("object", "AnyObject"); + typeMapping.put("file", "NSData"); + + importMapping = new HashMap(); + } + + @Override + public String escapeReservedWord(String name) { + return "_" + name; // add an underscore to the name + } + + @Override + public String modelFileFolder() { + return outputFolder + "/" + sourceFolder + modelPackage().replace('.', File.separatorChar); + } + + @Override + public String apiFileFolder() { + return outputFolder + "/" + sourceFolder + apiPackage().replace('.', File.separatorChar); + } + + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "[" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return "[String:" + getTypeDeclaration(inner) + "]"; + } + return super.getTypeDeclaration(p); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return toModelName(type); + } else + type = swaggerType; + return toModelName(type); + } + + @Override + public String toDefaultValue(Property p) { + // nil + return null; + } + + @Override + public String toInstantiationType(Property p) { + if (p instanceof MapProperty) { + MapProperty ap = (MapProperty) p; + String inner = getSwaggerType(ap.getAdditionalProperties()); + return "[String:" + inner + "]"; + } else if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + String inner = getSwaggerType(ap.getItems()); + return "[" + inner + "]"; + } + return null; + } + + @Override + public CodegenProperty fromProperty(String name, Property p) { + CodegenProperty codegenProperty = super.fromProperty(name, p); + if (codegenProperty.isEnum) { + List> swiftEnums = new ArrayList>(); + List values = (List) codegenProperty.allowableValues.get("values"); + for (String value : values) { + Map map = new HashMap(); + map.put("enum", StringUtils.capitalize(value)); + map.put("raw", value); + swiftEnums.add(map); + } + codegenProperty.allowableValues.put("values", swiftEnums); + codegenProperty.datatypeWithEnum = + StringUtils.left(codegenProperty.datatypeWithEnum, codegenProperty.datatypeWithEnum.length() - "Enum".length()); + } + return codegenProperty; + } + + @Override + public String toApiName(String name) { + if(name.length() == 0) + return "DefaultAPI"; + return initialCaps(name) + "API"; + } + + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, Map definitions) { + path = normalizePath(path); + List parameters = operation.getParameters(); + parameters = Lists.newArrayList(Iterators.filter(parameters.iterator(), new Predicate() { + @Override + public boolean apply(@Nullable Parameter parameter) { + return !(parameter instanceof HeaderParameter); + } + })); + operation.setParameters(parameters); + return super.fromOperation(path, httpMethod, operation, definitions); + } + + private static String normalizePath(String path) { + StringBuilder builder = new StringBuilder(); + + int cursor = 0; + Matcher matcher = PATH_PARAM_PATTERN.matcher(path); + boolean found = matcher.find(); + while (found) { + String stringBeforeMatch = path.substring(cursor, matcher.start()); + builder.append(stringBeforeMatch); + + String group = matcher.group().substring(1, matcher.group().length() - 1); + group = camelize(group, true); + builder + .append("{") + .append(group) + .append("}"); + + cursor = matcher.end(); + found = matcher.find(); + } + + String stringAfterMatch = path.substring(cursor); + builder.append(stringAfterMatch); + + return builder.toString(); + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index 31c055b54dc..a62c2f2c9b8 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -121,8 +121,8 @@ class Decoders { Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject) -> {{{classname}}} in let sourceDictionary = source as! [NSObject:AnyObject] var instance = {{classname}}(){{#vars}}{{#isEnum}} - instance.{{name}} = (sourceDictionary["{{name}}"] as? String).map { {{classname}}.{{datatypeWithEnum}}(rawValue: $0)! }{{#required}}!{{/required}} {{/isEnum}}{{^isEnum}} - instance.{{name}} = Decoders.decode{{^required}}Optional{{/required}}(clazz: {{{baseType}}}.self, source: sourceDictionary["{{name}}"]{{#required}}!{{/required}}){{/isEnum}}{{/vars}} + instance.{{name}} = (sourceDictionary["{{name}}"] as? String).map { {{classname}}.{{datatypeWithEnum}}(rawValue: $0)! }{{^supressRequired}}{{#required}}!{{/required}}{{/supressRequired}} {{/isEnum}}{{^isEnum}} + instance.{{name}} = Decoders.decode{{#suppressRequired}}Optional{{/suppressRequired}}{{^suppressRequired}}{{^required}}Optional{{/required}}{{/suppressRequired}}(clazz: {{{baseType}}}.self, source: sourceDictionary["{{name}}"]{{^supressRequired}}{{#required}}!{{/required}}{{/supressRequired}}){{/isEnum}}{{/vars}} return instance }{{/model}} {{/models}} diff --git a/modules/swagger-codegen/src/main/resources/swift/api.mustache b/modules/swagger-codegen/src/main/resources/swift/api.mustache index 8c40c3f5be7..abfea5e10fb 100644 --- a/modules/swagger-codegen/src/main/resources/swift/api.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/api.mustache @@ -30,12 +30,12 @@ extension {{projectName}}API { :returns: Promise> {{description}} */ - func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> { + func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{{dataType}}}{{#supressRequired}}?{{/supressRequired}}{{^supressRequired}}{{^required}}?{{/required}}{{/supressRequired}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> { {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{^secondaryParam}}var{{/secondaryParam}}{{/pathParams}} path = "{{path}}"{{#pathParams}} path = path.stringByReplacingOccurrencesOfString("{{=<% %>=}}{<%paramName%>}<%={{ }}=%>", withString: "\({{paramName}})", options: .LiteralSearch, range: nil){{/pathParams}} let url = {{projectName}}API.basePath + path {{#bodyParam}} - let parameters = {{paramName}}{{^required}}?{{/required}}.encodeToJSON() as? [String:AnyObject]{{/bodyParam}}{{^bodyParam}} + let parameters = {{paramName}}{{#supressRequired}}?{{/supressRequired}}{{^supressRequired}}{{^required}}?{{/required}}{{/supressRequired}}.encodeToJSON() as? [String:AnyObject]{{/bodyParam}}{{^bodyParam}} let nillableParameters: [String:AnyObject?] = {{^queryParams}}[:]{{/queryParams}}{{#queryParams}}{{^secondaryParam}}[{{/secondaryParam}} "{{paramName}}": {{paramName}}{{#hasMore}},{{/hasMore}}{{^hasMore}} ]{{/hasMore}}{{/queryParams}} diff --git a/modules/swagger-codegen/src/main/resources/swift/model.mustache b/modules/swagger-codegen/src/main/resources/swift/model.mustache index 06f10146cf6..6de26a5a1a9 100644 --- a/modules/swagger-codegen/src/main/resources/swift/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -17,17 +17,17 @@ class {{classname}}: JSONEncodable { } {{/isEnum}}{{/vars}} {{#vars}}{{#isEnum}}{{#description}}/** {{description}} */ - {{/description}}var {{name}}: {{{datatypeWithEnum}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}}{{^isEnum}}{{#description}}/** {{description}} */ - {{/description}}var {{name}}: {{{datatype}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}} + {{/description}}var {{name}}: {{{datatypeWithEnum}}}{{#suppressRequired}}?{{/suppressRequired}}{{^suppressRequired}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{/suppressRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}}{{^isEnum}}{{#description}}/** {{description}} */ + {{/description}}var {{name}}: {{{datatype}}}{{#suppressRequired}}?{{/suppressRequired}}{{^suppressRequired}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{/suppressRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}} {{/vars}} // MARK: JSONEncodable func encodeToJSON() -> AnyObject { var nillableDictionary = [String:AnyObject?](){{#vars}}{{#isNotContainer}}{{#isPrimitiveType}}{{^isEnum}} nillableDictionary["{{name}}"] = self.{{name}}{{/isEnum}}{{/isPrimitiveType}}{{#isEnum}} - nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.rawValue{{/isEnum}}{{^isPrimitiveType}} - nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encodeToJSON(){{/isPrimitiveType}}{{/isNotContainer}}{{#isContainer}} - nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encodeToJSON(){{/isContainer}}{{/vars}} + nillableDictionary["{{name}}"] = self.{{name}}{{#supressRequired}}?{{/supressRequired}}{{^supressRequired}}{{^required}}?{{/required}}{{/supressRequired}}.rawValue{{/isEnum}}{{^isPrimitiveType}} + nillableDictionary["{{name}}"] = self.{{name}}{{#supressRequired}}?{{/supressRequired}}{{^supressRequired}}{{^required}}?{{/required}}{{/supressRequired}}.encodeToJSON(){{/isPrimitiveType}}{{/isNotContainer}}{{#isContainer}} + nillableDictionary["{{name}}"] = self.{{name}}{{#supressRequired}}?{{/supressRequired}}{{^supressRequired}}{{^required}}?{{/required}}{{/supressRequired}}.encodeToJSON(){{/isContainer}}{{/vars}} let dictionary: [String:AnyObject] = APIHelper.rejectNil(nillableDictionary) ?? [:] return dictionary }