diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1fc2667
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+# IntelliJ
+.idea/
+out/
+
+# Editor-based Rest Client
+.idea/httpRequests
+### Java template
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a91300a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,82 @@
+# OpenAPI Generator for the minderaswift4generator library
+
+## Overview
+This is a boiler-plate project to generate your own project derived from an OpenAPI specification.
+Its goal is to get you started with the basic plumbing so you can put in your own logic.
+It won't work without your changes applied.
+
+## What's OpenAPI
+The goal of OpenAPI is to define a standard, language-agnostic interface to REST APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.
+When properly described with OpenAPI, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.
+Similar to what interfaces have done for lower-level programming, OpenAPI removes the guesswork in calling the service.
+
+Check out [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) for additional information about the OpenAPI project, including additional libraries with support for other languages and more.
+
+## How do I use this?
+At this point, you've likely generated a client setup. It will include something along these lines:
+
+```
+.
+|- README.md // this file
+|- pom.xml // build script
+|-- src
+|--- main
+|---- java
+|----- com/github/Mindera.MinderaSwift4GeneratorGenerator.java // generator file
+|---- resources
+|----- minderaswift4generator // template files
+|----- META-INF
+|------ services
+|------- org.openapitools.codegen.CodegenConfig
+```
+
+You _will_ need to make changes in at least the following:
+
+`MinderaSwift4GeneratorGenerator.java`
+
+Templates in this folder:
+
+`src/main/resources/minderaswift4generator`
+
+Once modified, you can run this:
+
+```
+mvn package
+```
+
+In your generator project. A single jar file will be produced in `target`. You can now use that with [OpenAPI Generator](https://openapi-generator.tech):
+
+For mac/linux:
+```
+java -cp /path/to/openapi-generator-cli.jar:/path/to/your.jar org.openapitools.codegen.OpenAPIGenerator generate -g minderaswift4generator -i /path/to/openapi.yaml -o ./test
+```
+(Do not forget to replace the values `/path/to/openapi-generator-cli.jar`, `/path/to/your.jar` and `/path/to/openapi.yaml` in the previous command)
+
+For Windows users, you will need to use `;` instead of `:` in the classpath, e.g.
+```
+java -cp /path/to/openapi-generator-cli.jar;/path/to/your.jar org.openapitools.codegen.OpenAPIGenerator generate -g minderaswift4generator -i /path/to/openapi.yaml -o ./test
+```
+
+Now your templates are available to the client generator and you can write output values
+
+## But how do I modify this?
+The `MinderaSwift4GeneratorGenerator.java` has comments in it--lots of comments. There is no good substitute
+for reading the code more, though. See how the `MinderaSwift4GeneratorGenerator` implements `CodegenConfig`.
+That class has the signature of all values that can be overridden.
+
+For the templates themselves, you have a number of values available to you for generation.
+You can execute the `java` command from above while passing different debug flags to show
+the object you have available during client generation:
+
+```
+# The following additional debug options are available for all codegen targets:
+# -DdebugOpenAPI prints the OpenAPI Specification as interpreted by the codegen
+# -DdebugModels prints models passed to the template engine
+# -DdebugOperations prints operations passed to the template engine
+# -DdebugSupportingFiles prints additional data passed to the template engine
+
+java -DdebugOperations -cp /path/to/openapi-generator-cli.jar:/path/to/your.jar org.openapitools.codegen.OpenAPIGenerator generate -g minderaswift4generator -i /path/to/openapi.yaml -o ./test
+```
+
+Will, for example, output the debug info for operations.
+You can use this info in the `api.mustache` file.
\ No newline at end of file
diff --git a/mindera-openapi-generator.iml b/mindera-openapi-generator.iml
new file mode 100644
index 0000000..ff1b2a8
--- /dev/null
+++ b/mindera-openapi-generator.iml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..ef3816d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,144 @@
+
+ 4.0.0
+ com.github.mindera
+ mindera-openapi-generator
+ jar
+ mindera-openapi-generator
+ 1.0.0
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.0.0-M1
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ 2.2.0
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.12
+
+
+
+ loggerPath
+ conf/log4j.properties
+
+
+ -Xms512m -Xmx1500m
+ methods
+ pertest
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.2
+
+
+
+ jar
+ test-jar
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.0.0
+
+
+ add_sources
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+ add_test_sources
+ generate-test-sources
+
+ add-test-source
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.1
+
+
+ 1.8
+
+
+
+
+
+
+ org.openapitools
+ openapi-generator
+ ${openapi-generator-version}
+ provided
+
+
+ org.openapitools
+ openapi-generator
+ ${openapi-generator-version}
+ tests
+ test
+
+
+ org.testng
+ testng
+ ${testng-version}
+ test
+
+
+ org.jmockit
+ jmockit
+ ${jmockit-version}
+ test
+
+
+
+ 1.43
+ 6.9.6
+ UTF-8
+ 3.3.4
+ 1.0.0
+ 4.8.1
+
+
diff --git a/src/main/java/com/mindera/openapi/generator/Swift4Generator.java b/src/main/java/com/mindera/openapi/generator/Swift4Generator.java
new file mode 100644
index 0000000..65d9c2f
--- /dev/null
+++ b/src/main/java/com/mindera/openapi/generator/Swift4Generator.java
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
+ * Copyright 2018 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mindera.openapi.generator;
+
+import io.swagger.v3.oas.models.media.ArraySchema;
+import io.swagger.v3.oas.models.media.Schema;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.text.WordUtils;
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.utils.ModelUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class Swift4Generator extends DefaultCodegen implements CodegenConfig {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Swift4Generator.class);
+
+ public static final String PROJECT_NAME = "projectName";
+ public static final String RESPONSE_AS = "responseAs";
+ public static final String UNWRAP_REQUIRED = "unwrapRequired";
+ public static final String OBJC_COMPATIBLE = "objcCompatible";
+ public static final String POD_SOURCE = "podSource";
+ public static final String POD_AUTHORS = "podAuthors";
+ public static final String POD_SOCIAL_MEDIA_URL = "podSocialMediaURL";
+ public static final String POD_DOCSET_URL = "podDocsetURL";
+ public static final String POD_LICENSE = "podLicense";
+ public static final String POD_HOMEPAGE = "podHomepage";
+ public static final String POD_SUMMARY = "podSummary";
+ public static final String POD_DESCRIPTION = "podDescription";
+ public static final String POD_SCREENSHOTS = "podScreenshots";
+ public static final String POD_DOCUMENTATION_URL = "podDocumentationURL";
+ public static final String SWIFT_USE_API_NAMESPACE = "swiftUseApiNamespace";
+ public static final String DEFAULT_POD_AUTHORS = "OpenAPI Generator";
+ public static final String LENIENT_TYPE_CAST = "lenientTypeCast";
+ protected static final String LIBRARY_PROMISE_KIT = "PromiseKit";
+ protected static final String LIBRARY_RX_SWIFT = "RxSwift";
+ protected static final String[] RESPONSE_LIBRARIES = {LIBRARY_PROMISE_KIT, LIBRARY_RX_SWIFT};
+ protected String projectName = "OpenAPIClient";
+ protected boolean unwrapRequired;
+ protected boolean objcCompatible = false;
+ protected boolean lenientTypeCast = false;
+ protected boolean swiftUseApiNamespace;
+ protected String[] responseAs = new String[0];
+ protected String sourceFolder = "Classes" + File.separator + "OpenAPIs";
+
+ /**
+ * Constructor for the swift4 language codegen module.
+ */
+ public Swift4Generator() {
+ super();
+ outputFolder = "generated-code" + File.separator + "swift";
+ modelTemplateFiles.put("model.mustache", ".swift");
+ apiTemplateFiles.put("api.mustache", ".swift");
+ embeddedTemplateDir = templateDir = "MinderaSwift4";
+ apiPackage = File.separator + "APIs";
+ modelPackage = File.separator + "Models";
+
+ languageSpecificPrimitives = new HashSet<>(
+ Arrays.asList(
+ "Int",
+ "Int32",
+ "Int64",
+ "Float",
+ "Double",
+ "Bool",
+ "Void",
+ "String",
+ "Character",
+ "AnyObject",
+ "Any")
+ );
+ defaultIncludes = new HashSet<>(
+ Arrays.asList(
+ "Data",
+ "Date",
+ "URL", // for file
+ "UUID",
+ "Array",
+ "Dictionary",
+ "Set",
+ "Any",
+ "Empty",
+ "AnyObject",
+ "Any")
+ );
+ reservedWords = new HashSet<>(
+ Arrays.asList(
+ // name used by swift client
+ "ErrorResponse", "Response",
+
+ // Added for Objective-C compatibility
+ "NSArray", "NSURL", "CGFloat", "NSSet", "NSString", "NSInteger", "NSUInteger",
+ "NSError", "NSDictionary",
+
+ //
+ // Swift keywords. This list is taken from here:
+ // https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID410
+ //
+ // Keywords used in declarations
+ "associatedtype", "class", "deinit", "enum", "extension", "fileprivate", "func", "import", "init",
+ "inout", "internal", "let", "open", "operator", "private", "protocol", "public", "static", "struct",
+ "subscript", "typealias", "var",
+ // Keywords uses in statements
+ "break", "case", "continue", "default", "defer", "do", "else", "fallthrough", "for", "guard", "if",
+ "in", "repeat", "return", "switch", "where", "while",
+ // Keywords used in expressions and types
+ "as", "Any", "catch", "false", "is", "nil", "rethrows", "super", "self", "Self", "throw", "throws", "true", "try",
+ // Keywords used in patterns
+ "_",
+ // Keywords that begin with a number sign
+ "#available", "#colorLiteral", "#column", "#else", "#elseif", "#endif", "#file", "#fileLiteral", "#function", "#if",
+ "#imageLiteral", "#line", "#selector", "#sourceLocation",
+ // Keywords reserved in particular contexts
+ "associativity", "convenience", "dynamic", "didSet", "final", "get", "infix", "indirect", "lazy", "left",
+ "mutating", "none", "nonmutating", "optional", "override", "postfix", "precedence", "prefix", "Protocol",
+ "required", "right", "set", "Type", "unowned", "weak", "willSet",
+
+ //
+ // Swift Standard Library types
+ // https://developer.apple.com/documentation/swift
+ //
+ // Numbers and Basic Values
+ "Bool", "Int", "Double", "Float", "Range", "ClosedRange", "Error", "Optional",
+ // Special-Use Numeric Types
+ "UInt", "UInt8", "UInt16", "UInt32", "UInt64", "Int8", "Int16", "Int32", "Int64", "Float80", "Float32", "Float64",
+ // Strings and Text
+ "String", "Character", "Unicode", "StaticString",
+ // Collections
+ "Array", "Dictionary", "Set", "OptionSet", "CountableRange", "CountableClosedRange",
+
+ // The following are commonly-used Foundation types
+ "URL", "Data", "Codable", "Encodable", "Decodable",
+
+ // The following are other words we want to reserve
+ "Void", "AnyObject", "Class", "dynamicType", "COLUMN", "FILE", "FUNCTION", "LINE"
+ )
+ );
+
+ typeMapping = new HashMap<>();
+ typeMapping.put("array", "Array");
+ typeMapping.put("List", "Array");
+ typeMapping.put("map", "Dictionary");
+ typeMapping.put("date", "DateWithoutTime");
+ typeMapping.put("Date", "DateWithoutTime");
+ typeMapping.put("DateTime", "Date");
+ typeMapping.put("boolean", "Bool");
+ typeMapping.put("string", "String");
+ typeMapping.put("char", "Character");
+ typeMapping.put("short", "Int");
+ typeMapping.put("int", "Int");
+ typeMapping.put("long", "Int64");
+ typeMapping.put("integer", "Int");
+ typeMapping.put("Integer", "Int");
+ typeMapping.put("float", "Float");
+ typeMapping.put("number", "Double");
+ typeMapping.put("double", "Double");
+ typeMapping.put("object", "Any");
+ typeMapping.put("file", "URL");
+ typeMapping.put("binary", "URL");
+ typeMapping.put("ByteArray", "Data");
+ typeMapping.put("UUID", "UUID");
+
+ importMapping = new HashMap<>();
+
+ cliOptions.add(new CliOption(PROJECT_NAME, "Project name in Xcode"));
+ cliOptions.add(new CliOption(RESPONSE_AS,
+ "Optionally use libraries to manage response. Currently "
+ + StringUtils.join(RESPONSE_LIBRARIES, ", ")
+ + " are available."));
+ cliOptions.add(new CliOption(UNWRAP_REQUIRED,
+ "Treat 'required' properties in response as non-optional "
+ + "(which would crash the app if api returns null as opposed "
+ + "to required option specified in json schema"));
+ cliOptions.add(new CliOption(OBJC_COMPATIBLE,
+ "Add additional properties and methods for Objective-C "
+ + "compatibility (default: false)"));
+ cliOptions.add(new CliOption(POD_SOURCE, "Source information used for Podspec"));
+ cliOptions.add(new CliOption(CodegenConstants.POD_VERSION, "Version used for Podspec"));
+ cliOptions.add(new CliOption(POD_AUTHORS, "Authors used for Podspec"));
+ cliOptions.add(new CliOption(POD_SOCIAL_MEDIA_URL, "Social Media URL used for Podspec"));
+ cliOptions.add(new CliOption(POD_DOCSET_URL, "Docset URL used for Podspec"));
+ cliOptions.add(new CliOption(POD_LICENSE, "License used for Podspec"));
+ cliOptions.add(new CliOption(POD_HOMEPAGE, "Homepage used for Podspec"));
+ cliOptions.add(new CliOption(POD_SUMMARY, "Summary used for Podspec"));
+ cliOptions.add(new CliOption(POD_DESCRIPTION, "Description used for Podspec"));
+ cliOptions.add(new CliOption(POD_SCREENSHOTS, "Screenshots used for Podspec"));
+ cliOptions.add(new CliOption(POD_DOCUMENTATION_URL,
+ "Documentation URL used for Podspec"));
+ cliOptions.add(new CliOption(SWIFT_USE_API_NAMESPACE,
+ "Flag to make all the API classes inner-class "
+ + "of {{projectName}}API"));
+ cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
+ CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC)
+ .defaultValue(Boolean.TRUE.toString()));
+ cliOptions.add(new CliOption(LENIENT_TYPE_CAST,
+ "Accept and cast values for simple types (string->bool, "
+ + "string->int, int->string)")
+ .defaultValue(Boolean.FALSE.toString()));
+ }
+
+ private static CodegenModel reconcileProperties(CodegenModel codegenModel,
+ CodegenModel parentCodegenModel) {
+ // To support inheritance in this generator, we will analyze
+ // the parent and child models, look for properties that match, and remove
+ // them from the child models and leave them in the parent.
+ // Because the child models extend the parents, the properties
+ // will be available via the parent.
+
+ // Get the properties for the parent and child models
+ final List parentModelCodegenProperties = parentCodegenModel.vars;
+ List codegenProperties = codegenModel.vars;
+ codegenModel.allVars = new ArrayList(codegenProperties);
+ codegenModel.parentVars = parentCodegenModel.allVars;
+
+ // Iterate over all of the parent model properties
+ boolean removedChildProperty = false;
+
+ for (CodegenProperty parentModelCodegenProperty : parentModelCodegenProperties) {
+ // Now that we have found a prop in the parent class,
+ // and search the child class for the same prop.
+ Iterator iterator = codegenProperties.iterator();
+ while (iterator.hasNext()) {
+ CodegenProperty codegenProperty = iterator.next();
+ if (codegenProperty.baseName.equals(parentModelCodegenProperty.baseName)) {
+ // We found a property in the child class that is
+ // a duplicate of the one in the parent, so remove it.
+ iterator.remove();
+ removedChildProperty = true;
+ }
+ }
+ }
+
+ if (removedChildProperty) {
+ // If we removed an entry from this model's vars, we need to ensure hasMore is updated
+ int count = 0;
+ int numVars = codegenProperties.size();
+ for (CodegenProperty codegenProperty : codegenProperties) {
+ count += 1;
+ codegenProperty.hasMore = count < numVars;
+ }
+ codegenModel.vars = codegenProperties;
+ }
+
+
+ return codegenModel;
+ }
+
+ @Override
+ public CodegenType getTag() {
+ return CodegenType.CLIENT;
+ }
+
+ @Override
+ public String getName() {
+ return "mindera-swift4";
+ }
+
+ @Override
+ public String getHelp() {
+ return "Generates a Swift 4.x client library for Minders.";
+ }
+
+ @Override
+ protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel,
+ Schema schema) {
+
+ final Schema additionalProperties = ModelUtils.getAdditionalProperties(schema);
+
+ if (additionalProperties != null) {
+ codegenModel.additionalPropertiesType = getSchemaType(additionalProperties);
+ }
+ }
+
+ @Override
+ public void processOpts() {
+ super.processOpts();
+
+ if (StringUtils.isEmpty(System.getenv("SWIFT_POST_PROCESS_FILE"))) {
+ LOGGER.info("Environment variable SWIFT_POST_PROCESS_FILE not defined so the Swift code may not be properly formatted. To define it, try 'export SWIFT_POST_PROCESS_FILE=/usr/local/bin/swiftformat' (Linux/Mac)");
+ }
+
+ // Setup project name
+ if (additionalProperties.containsKey(PROJECT_NAME)) {
+ setProjectName((String) additionalProperties.get(PROJECT_NAME));
+ } else {
+ additionalProperties.put(PROJECT_NAME, projectName);
+ }
+ sourceFolder = projectName + File.separator + sourceFolder;
+
+ // Setup unwrapRequired option, which makes all the
+ // properties with "required" non-optional
+ if (additionalProperties.containsKey(UNWRAP_REQUIRED)) {
+ setUnwrapRequired(convertPropertyToBooleanAndWriteBack(UNWRAP_REQUIRED));
+ }
+ additionalProperties.put(UNWRAP_REQUIRED, unwrapRequired);
+
+ // Setup objcCompatible option, which adds additional properties
+ // and methods for Objective-C compatibility
+ if (additionalProperties.containsKey(OBJC_COMPATIBLE)) {
+ setObjcCompatible(convertPropertyToBooleanAndWriteBack(OBJC_COMPATIBLE));
+ }
+ additionalProperties.put(OBJC_COMPATIBLE, objcCompatible);
+
+ // Setup unwrapRequired option, which makes all the properties with "required" non-optional
+ if (additionalProperties.containsKey(RESPONSE_AS)) {
+ Object responseAsObject = additionalProperties.get(RESPONSE_AS);
+ if (responseAsObject instanceof String) {
+ setResponseAs(((String) responseAsObject).split(","));
+ } else {
+ setResponseAs((String[]) responseAsObject);
+ }
+ }
+ additionalProperties.put(RESPONSE_AS, responseAs);
+ if (ArrayUtils.contains(responseAs, LIBRARY_PROMISE_KIT)) {
+ additionalProperties.put("usePromiseKit", true);
+ }
+ if (ArrayUtils.contains(responseAs, LIBRARY_RX_SWIFT)) {
+ additionalProperties.put("useRxSwift", true);
+ }
+
+ // Setup swiftUseApiNamespace option, which makes all the API
+ // classes inner-class of {{projectName}}API
+ if (additionalProperties.containsKey(SWIFT_USE_API_NAMESPACE)) {
+ setSwiftUseApiNamespace(convertPropertyToBooleanAndWriteBack(SWIFT_USE_API_NAMESPACE));
+ }
+
+ if (!additionalProperties.containsKey(POD_AUTHORS)) {
+ additionalProperties.put(POD_AUTHORS, DEFAULT_POD_AUTHORS);
+ }
+
+ setLenientTypeCast(convertPropertyToBooleanAndWriteBack(LENIENT_TYPE_CAST));
+
+ supportingFiles.add(new SupportingFile("Podspec.mustache",
+ "",
+ projectName + ".podspec"));
+ 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("Configuration.mustache",
+ sourceFolder,
+ "Configuration.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"));
+ supportingFiles.add(new SupportingFile("CodableHelper.mustache",
+ sourceFolder,
+ "CodableHelper.swift"));
+ supportingFiles.add(new SupportingFile("JSONEncodableEncoding.mustache",
+ sourceFolder,
+ "JSONEncodableEncoding.swift"));
+ supportingFiles.add(new SupportingFile("JSONEncodingHelper.mustache",
+ sourceFolder,
+ "JSONEncodingHelper.swift"));
+ supportingFiles.add(new SupportingFile("git_push.sh.mustache",
+ "",
+ "git_push.sh"));
+ supportingFiles.add(new SupportingFile("gitignore.mustache",
+ "",
+ ".gitignore"));
+
+ }
+
+ @Override
+ protected boolean isReservedWord(String word) {
+ return word != null && reservedWords.contains(word); //don't lowercase as super does
+ }
+
+ @Override
+ public String escapeReservedWord(String name) {
+ if (this.reservedWordsMappings().containsKey(name)) {
+ return this.reservedWordsMappings().get(name);
+ }
+ return "_" + name; // add an underscore to the name
+ }
+
+ @Override
+ public String modelFileFolder() {
+ return outputFolder + File.separator + sourceFolder
+ + modelPackage().replace('.', File.separatorChar);
+ }
+
+ @Override
+ public String apiFileFolder() {
+ return outputFolder + File.separator + sourceFolder
+ + apiPackage().replace('.', File.separatorChar);
+ }
+
+ @Override
+ public String getTypeDeclaration(Schema p) {
+ if (ModelUtils.isArraySchema(p)) {
+ ArraySchema ap = (ArraySchema) p;
+ Schema inner = ap.getItems();
+ return "[" + getTypeDeclaration(inner) + "]";
+ } else if (ModelUtils.isMapSchema(p)) {
+ Schema inner = ModelUtils.getAdditionalProperties(p);
+ return "[String:" + getTypeDeclaration(inner) + "]";
+ }
+ return getSchemaType(p);
+ }
+
+ @Override
+ public String getSchemaType(Schema p) {
+ String openAPIType = super.getSchemaType(p);
+ String type;
+ if (typeMapping.containsKey(openAPIType)) {
+ type = typeMapping.get(openAPIType);
+ if (languageSpecificPrimitives.contains(type) || defaultIncludes.contains(type)) {
+ return type;
+ }
+ } else {
+ type = openAPIType;
+ }
+ return toModelName(type);
+ }
+
+ @Override
+ public boolean isDataTypeFile(String dataType) {
+ return dataType != null && dataType.equals("URL");
+ }
+
+ @Override
+ public boolean isDataTypeBinary(final String dataType) {
+ return dataType != null && dataType.equals("Data");
+ }
+
+ /**
+ * Output the proper model name (capitalized).
+ *
+ * @param name the name of the model
+ * @return capitalized model name
+ */
+ @Override
+ public String toModelName(String name) {
+ // FIXME parameter should not be assigned. Also declare it as "final"
+ name = sanitizeName(name);
+
+ if (!StringUtils.isEmpty(modelNameSuffix)) { // set model suffix
+ name = name + "_" + modelNameSuffix;
+ }
+
+ if (!StringUtils.isEmpty(modelNamePrefix)) { // set model prefix
+ name = modelNamePrefix + "_" + name;
+ }
+
+ // camelize the model name
+ // phone_number => PhoneNumber
+ name = org.openapitools.codegen.utils.StringUtils.camelize(name);
+
+ // model name cannot use reserved keyword, e.g. return
+ if (isReservedWord(name)) {
+ String modelName = "Model" + name;
+ LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to "
+ + modelName);
+ return modelName;
+ }
+
+ // model name starts with number
+ if (name.matches("^\\d.*")) {
+ // e.g. 200Response => Model200Response (after camelize)
+ String modelName = "Model" + name;
+ LOGGER.warn(name
+ + " (model name starts with number) cannot be used as model name."
+ + " Renamed to " + modelName);
+ return modelName;
+ }
+
+ return name;
+ }
+
+ /**
+ * Return the capitalized file name of the model.
+ *
+ * @param name the model name
+ * @return the file name of the model
+ */
+ @Override
+ public String toModelFilename(String name) {
+ // should be the same as the model name
+ return toModelName(name);
+ }
+
+ @Override
+ public String toDefaultValue(Schema p) {
+ if (p.getEnum() != null && !p.getEnum().isEmpty()) {
+ if (p.getDefault() != null) {
+ return "." + escapeText((String) p.getDefault());
+ }
+ }
+ if (ModelUtils.isIntegerSchema(p) || ModelUtils.isNumberSchema(p) || ModelUtils.isBooleanSchema(p)) {
+ if (p.getDefault() != null) {
+ return p.getDefault().toString();
+ }
+ } else if (ModelUtils.isStringSchema(p)) {
+ if (p.getDefault() != null) {
+ return "\"" + escapeText((String) p.getDefault()) + "\"";
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toInstantiationType(Schema p) {
+ if (ModelUtils.isMapSchema(p)) {
+ return getSchemaType(ModelUtils.getAdditionalProperties(p));
+ } else if (ModelUtils.isArraySchema(p)) {
+ ArraySchema ap = (ArraySchema) p;
+ String inner = getSchemaType(ap.getItems());
+ return "[" + inner + "]";
+ }
+ return null;
+ }
+
+ @Override
+ public String toApiName(String name) {
+ if (name.length() == 0) {
+ return "DefaultAPI";
+ }
+ return initialCaps(name) + "API";
+ }
+
+ @Override
+ public String toOperationId(String operationId) {
+ operationId = org.openapitools.codegen.utils.StringUtils.camelize(sanitizeName(operationId), true);
+
+ // Throw exception if method name is empty.
+ // This should not happen but keep the check just in case
+ if (StringUtils.isEmpty(operationId)) {
+ throw new RuntimeException("Empty method name (operationId) not allowed");
+ }
+
+ // method name cannot use reserved keyword, e.g. return
+ if (isReservedWord(operationId)) {
+ String newOperationId = org.openapitools.codegen.utils.StringUtils.camelize(("call_" + operationId), true);
+ LOGGER.warn(operationId + " (reserved word) cannot be used as method name."
+ + " Renamed to " + newOperationId);
+ return newOperationId;
+ }
+
+ // operationId starts with a number
+ if (operationId.matches("^\\d.*")) {
+ LOGGER.warn(operationId + " (starting with a number) cannot be used as method name. Renamed to " + org.openapitools.codegen.utils.StringUtils.camelize(sanitizeName("call_" + operationId), true));
+ operationId = org.openapitools.codegen.utils.StringUtils.camelize(sanitizeName("call_" + operationId), true);
+ }
+
+
+ return operationId;
+ }
+
+ @Override
+ public String toVarName(String name) {
+ // sanitize name
+ name = sanitizeName(name);
+
+ // if it's all uppper case, do nothing
+ if (name.matches("^[A-Z_]*$")) {
+ return name;
+ }
+
+ // camelize the variable name
+ // pet_id => petId
+ name = org.openapitools.codegen.utils.StringUtils.camelize(name, true);
+
+ // for reserved word or word starting with number, append _
+ if (isReservedWord(name) || name.matches("^\\d.*")) {
+ name = escapeReservedWord(name);
+ }
+
+ return name;
+ }
+
+ @Override
+ public String toParamName(String name) {
+ // sanitize name
+ name = sanitizeName(name);
+
+ // replace - with _ e.g. created-at => created_at
+ name = name.replaceAll("-", "_");
+
+ // if it's all uppper case, do nothing
+ if (name.matches("^[A-Z_]*$")) {
+ return name;
+ }
+
+ // org.openapitools.codegen.utils.StringUtils.camelize(lower) the variable name
+ // pet_id => petId
+ name = org.openapitools.codegen.utils.StringUtils.camelize(name, true);
+
+ // for reserved word or word starting with number, append _
+ if (isReservedWord(name) || name.matches("^\\d.*")) {
+ name = escapeReservedWord(name);
+ }
+
+ return name;
+ }
+
+ @Override
+ public CodegenModel fromModel(String name, Schema model, Map allDefinitions) {
+ CodegenModel codegenModel = super.fromModel(name, model, allDefinitions);
+ if (codegenModel.description != null) {
+ codegenModel.imports.add("ApiModel");
+ }
+ if (allDefinitions != null) {
+ String parentSchema = codegenModel.parentSchema;
+
+ // multilevel inheritance: reconcile properties of all the parents
+ while (parentSchema != null) {
+ final Schema parentModel = allDefinitions.get(parentSchema);
+ final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent,
+ parentModel,
+ allDefinitions);
+ codegenModel = Swift4Generator.reconcileProperties(codegenModel, parentCodegenModel);
+
+ // get the next parent
+ parentSchema = parentCodegenModel.parentSchema;
+ }
+ }
+
+ return codegenModel;
+ }
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ public void setUnwrapRequired(boolean unwrapRequired) {
+ this.unwrapRequired = unwrapRequired;
+ }
+
+ public void setObjcCompatible(boolean objcCompatible) {
+ this.objcCompatible = objcCompatible;
+ }
+
+ public void setLenientTypeCast(boolean lenientTypeCast) {
+ this.lenientTypeCast = lenientTypeCast;
+ }
+
+ public void setResponseAs(String[] responseAs) {
+ this.responseAs = responseAs;
+ }
+
+ public void setSwiftUseApiNamespace(boolean swiftUseApiNamespace) {
+ this.swiftUseApiNamespace = swiftUseApiNamespace;
+ }
+
+ @Override
+ public String toEnumValue(String value, String datatype) {
+ // for string, array of string
+ if ("String".equals(datatype) || "[String]".equals(datatype) || "[String:String]".equals(datatype)) {
+ return "\"" + String.valueOf(value) + "\"";
+ } else {
+ return String.valueOf(value);
+ }
+ }
+
+ @Override
+ public String toEnumDefaultValue(String value, String datatype) {
+ return datatype + "_" + value;
+ }
+
+ @Override
+ public String toEnumVarName(String name, String datatype) {
+ if (name.length() == 0) {
+ return "empty";
+ }
+
+ Pattern startWithNumberPattern = Pattern.compile("^\\d+");
+ Matcher startWithNumberMatcher = startWithNumberPattern.matcher(name);
+ if (startWithNumberMatcher.find()) {
+ String startingNumbers = startWithNumberMatcher.group(0);
+ String nameWithoutStartingNumbers = name.substring(startingNumbers.length());
+
+ return "_" + startingNumbers + org.openapitools.codegen.utils.StringUtils.camelize(nameWithoutStartingNumbers, true);
+ }
+
+ // for symbol, e.g. $, #
+ if (getSymbolName(name) != null) {
+ return org.openapitools.codegen.utils.StringUtils.camelize(WordUtils.capitalizeFully(getSymbolName(name).toUpperCase(Locale.ROOT)), true);
+ }
+
+ // Camelize only when we have a structure defined below
+ Boolean camelized = false;
+ if (name.matches("[A-Z][a-z0-9]+[a-zA-Z0-9]*")) {
+ name = org.openapitools.codegen.utils.StringUtils.camelize(name, true);
+ camelized = true;
+ }
+
+ // Reserved Name
+ String nameLowercase = StringUtils.lowerCase(name);
+ if (isReservedWord(nameLowercase)) {
+ return escapeReservedWord(nameLowercase);
+ }
+
+ // Check for numerical conversions
+ if ("Int".equals(datatype) || "Int32".equals(datatype) || "Int64".equals(datatype)
+ || "Float".equals(datatype) || "Double".equals(datatype)) {
+ String varName = "number" + org.openapitools.codegen.utils.StringUtils.camelize(name);
+ varName = varName.replaceAll("-", "minus");
+ varName = varName.replaceAll("\\+", "plus");
+ varName = varName.replaceAll("\\.", "dot");
+ return varName;
+ }
+
+ // If we have already camelized the word, don't progress
+ // any further
+ if (camelized) {
+ return name;
+ }
+
+ char[] separators = {'-', '_', ' ', ':', '(', ')'};
+ return org.openapitools.codegen.utils.StringUtils.camelize(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators)
+ .replaceAll("[-_ :\\(\\)]", ""),
+ true);
+ }
+
+ @Override
+ public String toEnumName(CodegenProperty property) {
+ String enumName = toModelName(property.name);
+
+ // Ensure that the enum type doesn't match a reserved word or
+ // the variable name doesn't match the generated enum type or the
+ // Swift compiler will generate an error
+ if (isReservedWord(property.datatypeWithEnum)
+ || toVarName(property.name).equals(property.datatypeWithEnum)) {
+ enumName = property.datatypeWithEnum + "Enum";
+ }
+
+ // TODO: toModelName already does something for names starting with number,
+ // so this code is probably never called
+ if (enumName.matches("\\d.*")) { // starts with number
+ return "_" + enumName;
+ } else {
+ return enumName;
+ }
+ }
+
+ @Override
+ public Map postProcessModels(Map objs) {
+ Map postProcessedModelsEnum = postProcessModelsEnum(objs);
+
+ // We iterate through the list of models, and also iterate through each of the
+ // properties for each model. For each property, if:
+ //
+ // CodegenProperty.name != CodegenProperty.baseName
+ //
+ // then we set
+ //
+ // CodegenProperty.vendorExtensions["x-codegen-escaped-property-name"] = true
+ //
+ // Also, if any property in the model has x-codegen-escaped-property-name=true, then we mark:
+ //
+ // CodegenModel.vendorExtensions["x-codegen-has-escaped-property-names"] = true
+ //
+ List