add cpp client generator

This commit is contained in:
wing328
2018-03-28 00:48:46 +08:00
parent 0b61de9cd5
commit ff50ed187c
5 changed files with 507 additions and 10 deletions

View File

@@ -273,7 +273,7 @@ public class CppRestClientCodegen extends AbstractCppCodegen {
*/
@Override
public String getTypeDeclaration(Schema p) {
String swaggerType = getSchemaType(p);
String openAPIType = getSchemaType(p);
if (p instanceof ArraySchema) {
ArraySchema ap = (ArraySchema) p;
@@ -287,11 +287,11 @@ public class CppRestClientCodegen extends AbstractCppCodegen {
}
if (p instanceof StringSchema || p instanceof DateSchema
|| p instanceof DateTimeSchema || p instanceof FileSchema
|| languageSpecificPrimitives.contains(swaggerType)) {
return toModelName(swaggerType);
|| languageSpecificPrimitives.contains(openAPIType)) {
return toModelName(openAPIType);
}
return "std::shared_ptr<" + swaggerType + ">";
return "std::shared_ptr<" + openAPIType + ">";
}
@Override
@@ -305,12 +305,12 @@ public class CppRestClientCodegen extends AbstractCppCodegen {
} else if (p instanceof DateTimeSchema) {
return "utility::datetime()";
} else if (p instanceof NumberSchema) {
if(SchemaTypeUtil.FLOAT_FORMAT.equals(p.getFormat())) {
if (SchemaTypeUtil.FLOAT_FORMAT.equals(p.getFormat())) {
return "0.0f";
}
return "0.0";
} else if (p instanceof IntegerSchema) {
if(SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) {
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) {
return "0L";
}
return "0";
@@ -354,14 +354,14 @@ public class CppRestClientCodegen extends AbstractCppCodegen {
*/
@Override
public String getSchemaType(Schema p) {
String swaggerType = super.getSchemaType(p);
String openAPIType = super.getSchemaType(p);
String type = null;
if (typeMapping.containsKey(swaggerType)) {
type = typeMapping.get(swaggerType);
if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type))
return toModelName(type);
} else
type = swaggerType;
type = openAPIType;
return toModelName(type);
}

View File

@@ -0,0 +1,425 @@
package org.openapitools.codegen.languages;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Qt5CPPClientCodegen extends AbstractCppCodegen implements CodegenConfig {
public static final String CPP_NAMESPACE = "cppNamespace";
public static final String CPP_NAMESPACE_DESC = "C++ namespace (convention: name::space::for::api).";
public static final String OPTIONAL_PROJECT_FILE_DESC = "Generate client.pri.";
protected final String PREFIX = "SWG";
protected Set<String> foundationClasses = new HashSet<String>();
// source folder where to write the files
protected String sourceFolder = "client";
protected String apiVersion = "1.0.0";
protected Map<String, String> namespaces = new HashMap<String, String>();
protected Set<String> systemIncludes = new HashSet<String>();
protected String cppNamespace = "Swagger";
protected boolean optionalProjectFileFlag = true;
public Qt5CPPClientCodegen() {
super();
// set the output folder here
outputFolder = "generated-code/qt5cpp";
// set modelNamePrefix as default for QT5CPP
if (modelNamePrefix == "") {
modelNamePrefix = PREFIX;
}
/*
* Models. You can write model files using the modelTemplateFiles map.
* if you want to create one template for file, you can do so here.
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
* a different extension
*/
modelTemplateFiles.put(
"model-header.mustache",
".h");
modelTemplateFiles.put(
"model-body.mustache",
".cpp");
/*
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
* as with models, add multiple entries with different extensions for multiple files per
* class
*/
apiTemplateFiles.put(
"api-header.mustache", // the template to use
".h"); // the extension for each file to write
apiTemplateFiles.put(
"api-body.mustache", // the template to use
".cpp"); // the extension for each file to write
/*
* Template Location. This is the location which templates will be read from. The generator
* will use the resource stream to attempt to read the templates.
*/
embeddedTemplateDir = templateDir = "qt5cpp";
// CLI options
addOption(CPP_NAMESPACE, CPP_NAMESPACE_DESC, this.cppNamespace);
addSwitch(CodegenConstants.OPTIONAL_PROJECT_FILE, OPTIONAL_PROJECT_FILE_DESC, this.optionalProjectFileFlag);
/*
* Additional Properties. These values can be passed to the templates and
* are available in models, apis, and supporting files
*/
additionalProperties.put("apiVersion", apiVersion);
additionalProperties().put("prefix", PREFIX);
// Write defaults namespace in properties so that it can be accessible in templates.
// At this point command line has not been parsed so if value is given
// in command line it will superseed this content
additionalProperties.put("cppNamespace", cppNamespace);
/*
* Language Specific Primitives. These types will not trigger imports by
* the client generator
*/
languageSpecificPrimitives = new HashSet<String>(
Arrays.asList(
"bool",
"qint32",
"qint64",
"float",
"double")
);
supportingFiles.add(new SupportingFile("helpers-header.mustache", sourceFolder, PREFIX + "Helpers.h"));
supportingFiles.add(new SupportingFile("helpers-body.mustache", sourceFolder, PREFIX + "Helpers.cpp"));
supportingFiles.add(new SupportingFile("HttpRequest.h.mustache", sourceFolder, PREFIX + "HttpRequest.h"));
supportingFiles.add(new SupportingFile("HttpRequest.cpp.mustache", sourceFolder, PREFIX + "HttpRequest.cpp"));
supportingFiles.add(new SupportingFile("modelFactory.mustache", sourceFolder, PREFIX + "ModelFactory.h"));
supportingFiles.add(new SupportingFile("object.mustache", sourceFolder, PREFIX + "Object.h"));
supportingFiles.add(new SupportingFile("QObjectWrapper.h.mustache", sourceFolder, PREFIX + "QObjectWrapper.h"));
if (optionalProjectFileFlag) {
supportingFiles.add(new SupportingFile("Project.mustache", sourceFolder, "client.pri"));
}
super.typeMapping = new HashMap<String, String>();
typeMapping.put("date", "QDate");
typeMapping.put("DateTime", "QDateTime");
typeMapping.put("string", "QString");
typeMapping.put("integer", "qint32");
typeMapping.put("long", "qint64");
typeMapping.put("boolean", "bool");
typeMapping.put("array", "QList");
typeMapping.put("map", "QMap");
typeMapping.put("file", "SWGHttpRequestInputFileElement");
typeMapping.put("object", PREFIX + "Object");
//TODO binary should be mapped to byte array
// mapped to String as a workaround
typeMapping.put("binary", "QString");
typeMapping.put("ByteArray", "QByteArray");
// UUID support - possible enhancement : use QUuid instead of QString.
// beware though that Serialisation/deserialisation of QUuid does not
// come out of the box and will need to be sorted out (at least imply
// modifications on multiple templates)
typeMapping.put("UUID", "QString");
importMapping = new HashMap<String, String>();
importMapping.put("SWGHttpRequestInputFileElement", "#include \"" + PREFIX + "HttpRequest.h\"");
namespaces = new HashMap<String, String>();
foundationClasses.add("QString");
systemIncludes.add("QString");
systemIncludes.add("QList");
systemIncludes.add("QMap");
systemIncludes.add("QDate");
systemIncludes.add("QDateTime");
systemIncludes.add("QByteArray");
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey("cppNamespace")) {
cppNamespace = (String) additionalProperties.get("cppNamespace");
}
additionalProperties.put("cppNamespaceDeclarations", cppNamespace.split("\\::"));
if (additionalProperties.containsKey("modelNamePrefix")) {
supportingFiles.clear();
supportingFiles.add(new SupportingFile("helpers-header.mustache", sourceFolder, modelNamePrefix + "Helpers.h"));
supportingFiles.add(new SupportingFile("helpers-body.mustache", sourceFolder, modelNamePrefix + "Helpers.cpp"));
supportingFiles.add(new SupportingFile("HttpRequest.h.mustache", sourceFolder, modelNamePrefix + "HttpRequest.h"));
supportingFiles.add(new SupportingFile("HttpRequest.cpp.mustache", sourceFolder, modelNamePrefix + "HttpRequest.cpp"));
supportingFiles.add(new SupportingFile("modelFactory.mustache", sourceFolder, modelNamePrefix + "ModelFactory.h"));
supportingFiles.add(new SupportingFile("object.mustache", sourceFolder, modelNamePrefix + "Object.h"));
supportingFiles.add(new SupportingFile("QObjectWrapper.h.mustache", sourceFolder, modelNamePrefix + "QObjectWrapper.h"));
typeMapping.put("object", modelNamePrefix + "Object");
typeMapping.put("file", modelNamePrefix + "HttpRequestInputFileElement");
importMapping.put("SWGHttpRequestInputFileElement", "#include \"" + modelNamePrefix + "HttpRequest.h\"");
additionalProperties().put("prefix", modelNamePrefix);
}
if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_FILE)) {
setOptionalProjectFileFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_PROJECT_FILE));
} else {
additionalProperties.put(CodegenConstants.OPTIONAL_PROJECT_FILE, optionalProjectFileFlag);
}
}
/**
* Configures the type of generator.
*
* @return the CodegenType for this generator
* @see org.openapitools.codegen.CodegenType
*/
@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
}
/**
* Configures a friendly name for the generator. This will be used by the generator
* to select the library with the -l flag.
*
* @return the friendly name for the generator
*/
@Override
public String getName() {
return "qt5cpp";
}
/**
* Returns human-friendly help for the generator. Provide the consumer with help
* tips, parameters here
*
* @return A string value for the help message
*/
@Override
public String getHelp() {
return "Generates a Qt5 C++ client library.";
}
@Override
public String toModelImport(String name) {
if (namespaces.containsKey(name)) {
return "using " + namespaces.get(name) + ";";
} else if (systemIncludes.contains(name)) {
return "#include <" + name + ">";
}
String folder = modelPackage().replace("::", File.separator);
if (!folder.isEmpty())
folder += File.separator;
return "#include \"" + folder + name + ".h\"";
}
/**
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
* those terms here. This logic is only called if a variable matches the reserved words
*
* @return the escaped term
*/
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "_" + name;
}
/**
* Location to write model files. You can use the modelPackage() as defined when the class is
* instantiated
*/
@Override
public String modelFileFolder() {
return outputFolder + "/" + sourceFolder + "/" + modelPackage().replace("::", File.separator);
}
/**
* Location to write api files. You can use the apiPackage() as defined when the class is
* instantiated
*/
@Override
public String apiFileFolder() {
return outputFolder + "/" + sourceFolder + "/" + apiPackage().replace("::", File.separator);
}
@Override
public String toModelFilename(String name) {
return modelNamePrefix + initialCaps(name);
}
@Override
public String toApiFilename(String name) {
return modelNamePrefix + initialCaps(name) + "Api";
}
/**
* Optional - type declaration. This is a String which is used by the templates to instantiate your
* types. There is typically special handling for different property types
*
* @return a string value used as the `dataType` field for model templates, `returnType` for api templates
*/
@Override
public String getTypeDeclaration(Schema p) {
String openAPIType = getSchemaType(p);
if (p instanceof ArraySchema) {
ArraySchema ap = (ArraySchema) p;
Schema inner = ap.getItems();
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">*";
} else if (p instanceof MapSchema) {
MapSchema mp = (MapSchema) p;
Schema inner = (Schema) mp.getAdditionalProperties();
return getSchemaType(p) + "<QString, " + getTypeDeclaration(inner) + ">*";
}
if (foundationClasses.contains(openAPIType)) {
return openAPIType + "*";
} else if (languageSpecificPrimitives.contains(openAPIType)) {
return toModelName(openAPIType);
} else {
return openAPIType + "*";
}
}
@Override
public String toDefaultValue(Schema p) {
if (p instanceof StringSchema) {
return "new QString(\"\")";
} else if (p instanceof BooleanSchema) {
return "false";
} else if (p instanceof DateSchema) {
return "NULL";
} else if (p instanceof DateTimeSchema) {
return "NULL";
} else if (p instanceof NumberSchema) {
if (SchemaTypeUtil.FLOAT_FORMAT.equals(p.getFormat())) {
return "0.0f";
}
return "0.0";
} else if (p instanceof IntegerSchema) {
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) {
return "0L";
}
return "0";
} else if (p instanceof MapSchema) {
MapSchema mp = (MapSchema) p;
Schema inner = (Schema) mp.getAdditionalProperties();
return "new QMap<QString, " + getTypeDeclaration(inner) + ">()";
} else if (p instanceof ArraySchema) {
ArraySchema ap = (ArraySchema) p;
Schema inner = ap.getItems();
return "new QList<" + getTypeDeclaration(inner) + ">()";
}
// else
if (!StringUtils.isEmpty(p.get$ref())) {
return "new " + toModelName(p.get$ref()) + "()";
}
return "NULL";
}
/**
* Optional - swagger type conversion. This is used to map swagger types in a `Schema` into
* either language specific types via `typeMapping` or into complex models if there is not a mapping.
*
* @return a string value of the type or complex model for this property
*/
@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
String type = null;
if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type)) {
return toModelName(type);
}
if (foundationClasses.contains(type)) {
return type;
}
} else {
type = openAPIType;
}
return toModelName(type);
}
@Override
public String toModelName(String type) {
if (typeMapping.keySet().contains(type) ||
typeMapping.values().contains(type) ||
importMapping.values().contains(type) ||
defaultIncludes.contains(type) ||
languageSpecificPrimitives.contains(type)) {
return type;
} else {
return modelNamePrefix + Character.toUpperCase(type.charAt(0)) + type.substring(1);
}
}
@Override
public String toVarName(String name) {
// sanitize name
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
// if it's all uppper case, convert to lower case
if (name.matches("^[A-Z_]*$")) {
name = name.toLowerCase();
}
// camelize (lower first character) the variable name
// petId => pet_id
name = underscore(name);
// 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) {
return toVarName(name);
}
@Override
public String toApiName(String type) {
return modelNamePrefix + Character.toUpperCase(type.charAt(0)) + type.substring(1) + "Api";
}
@Override
public String escapeQuotationMark(String input) {
// remove " to avoid code injection
return input.replace("\"", "");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
public void setOptionalProjectFileFlag(boolean flag) {
this.optionalProjectFileFlag = flag;
}
}

View File

@@ -13,6 +13,7 @@ org.openapitools.codegen.languages.ObjcClientCodegen
org.openapitools.codegen.languages.PhpClientCodegen
org.openapitools.codegen.languages.PowerShellClientCodegen
org.openapitools.codegen.languages.PythonClientCodegen
org.openapitools.codegen.languages.Qt5CPPClientCodegen
org.openapitools.codegen.languages.RClientCodegen
org.openapitools.codegen.languages.Rails5ServerCodegen
org.openapitools.codegen.languages.RubyClientCodegen

View File

@@ -0,0 +1,43 @@
QT += network
HEADERS += \
# Models
{{#models}}
{{#model}}
$${PWD}/{{classname}}.h \
{{/model}}
{{/models}}
# APIs
{{#apiInfo}}
{{#apis}}
{{#operations}}
$${PWD}/{{classname}}.h \
{{/operations}}
{{/apis}}
{{/apiInfo}}
# Others
$${PWD}/{{prefix}}Helpers.h \
$${PWD}/{{prefix}}HttpRequest.h \
$${PWD}/{{prefix}}ModelFactory.h \
$${PWD}/{{prefix}}Object.h \
$${PWD}/{{prefix}}QObjectWrapper.h
SOURCES += \
# Models
{{#models}}
{{#model}}
$${PWD}/{{classname}}.cpp \
{{/model}}
{{/models}}
# APIs
{{#apiInfo}}
{{#apis}}
{{#operations}}
$${PWD}/{{classname}}.cpp \
{{/operations}}
{{/apis}}
{{/apiInfo}}
# Others
$${PWD}/{{prefix}}Helpers.cpp \
$${PWD}/{{prefix}}HttpRequest.cpp

View File

@@ -0,0 +1,28 @@
{{>licenseInfo}}
#ifndef {{prefix}}_QOBJECT_WRAPPER_H
#define {{prefix}}_QOBJECT_WRAPPER_H
#include <QObject>
{{#cppNamespaceDeclarations}}
namespace {{this}} {
{{/cppNamespaceDeclarations}}
template <typename ObjectPtrT>
class {{prefix}}QObjectWrapper : public QObject {
public:
{{prefix}}QObjectWrapper(ObjectPtrT ptr){
data = ptr;
}
~{{prefix}}QObjectWrapper(){
delete data;
}
private :
ObjectPtrT data;
};
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}
#endif // {{prefix}}_QOBJECT_WRAPPER_H