Skip to content

Commit 624d846

Browse files
authored
[cpp-qt5] improvements for server and client (OpenAPITools#1284)
- Remove pointer usage in generated models - Use const reference wherever possible - Reuse same super class for Qt5 client and server - Support primitive return types for Qt5 client - Add const reference to API calls
1 parent 045becc commit 624d846

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1780
-3015
lines changed

bin/cpp-qt5-petstore.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ fi
2727

2828
# if you've executed sbt assembly previously it will use that instead.
2929
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
30-
ags="generate -t modules/openapi-generator/src/main/resources/cpp-qt5-client -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g cpp-qt5 -o samples/client/petstore/cpp-qt5 $@"
30+
ags="generate -t modules/openapi-generator/src/main/resources/cpp-qt5-client -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g cpp-qt5-client -o samples/client/petstore/cpp-qt5 $@"
3131

3232
java $JAVA_OPTS -jar $executable $ags

bin/openapi3/cpp-qt5-petstore.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ fi
2727

2828
# if you've executed sbt assembly previously it will use that instead.
2929
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
30-
ags="generate -t modules/openapi-generator/src/main/resources/cpp-qt5-client -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g cpp-qt5 -o samples/client/petstore/cpp-qt5 $@"
30+
ags="generate -t modules/openapi-generator/src/main/resources/cpp-qt5-client -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g cpp-qt5-client -o samples/client/petstore/cpp-qt5 $@"
3131

3232
java $JAVA_OPTS -jar $executable $ags

bin/security/cpp-qt5-petstore.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ fi
2727

2828
# if you've executed sbt assembly previously it will use that instead.
2929
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
30-
ags="generate -t modules/openapi-generator/src/main/resources/cpp-qt5-client -i modules/openapi-generator/src/test/resources/2_0/petstore-security-test.yaml -g cpp-qt5 -o samples/client/petstore-security-test/cpp-qt5 $@"
30+
ags="generate -t modules/openapi-generator/src/main/resources/cpp-qt5-client -i modules/openapi-generator/src/test/resources/2_0/petstore-security-test.yaml -g cpp-qt5-client -o samples/client/petstore-security-test/cpp-qt5 $@"
3131

3232
java $JAVA_OPTS -jar $executable $ags

bin/windows/cpp-qt5-petstore.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ If Not Exist %executable% (
55
)
66

77
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
8-
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g cpp-qt5 -o samples\client\petstore\cpp-qt5
8+
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g cpp-qt5-client -o samples\client\petstore\cpp-qt5
99

1010
java %JAVA_OPTS% -jar %executable% %ags%
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
package org.openapitools.codegen.languages;
2+
3+
import java.io.File;
4+
import java.util.ArrayList;
5+
import java.util.Arrays;
6+
import java.util.HashMap;
7+
import java.util.HashSet;
8+
import java.util.List;
9+
import java.util.Locale;
10+
import java.util.Map;
11+
import java.util.Set;
12+
13+
import org.apache.commons.lang3.StringUtils;
14+
import org.openapitools.codegen.CodegenConfig;
15+
import org.openapitools.codegen.CodegenOperation;
16+
import org.openapitools.codegen.CodegenParameter;
17+
import org.openapitools.codegen.utils.ModelUtils;
18+
19+
import io.swagger.v3.oas.models.media.ArraySchema;
20+
import io.swagger.v3.oas.models.media.Schema;
21+
import io.swagger.v3.parser.util.SchemaTypeUtil;
22+
23+
public class CppQt5AbstractCodegen extends AbstractCppCodegen implements CodegenConfig {
24+
25+
protected final String PREFIX = "OAI";
26+
protected String apiVersion = "1.0.0";
27+
protected static final String CPP_NAMESPACE = "cppNamespace";
28+
protected static final String CPP_NAMESPACE_DESC = "C++ namespace (convention: name::space::for::api).";
29+
protected Set<String> foundationClasses = new HashSet<String>();
30+
protected String cppNamespace = "OpenAPI";
31+
protected Map<String, String> namespaces = new HashMap<String, String>();
32+
protected Set<String> systemIncludes = new HashSet<String>();
33+
34+
protected Set<String> nonFrameworkPrimitives = new HashSet<String>();
35+
36+
public CppQt5AbstractCodegen() {
37+
super();
38+
// set modelNamePrefix as default for QHttpEngine Server
39+
if (StringUtils.isEmpty(modelNamePrefix)) {
40+
modelNamePrefix = PREFIX;
41+
}
42+
// CLI options
43+
addOption(CPP_NAMESPACE, CPP_NAMESPACE_DESC, this.cppNamespace);
44+
45+
/*
46+
* Additional Properties. These values can be passed to the templates and
47+
* are available in models, apis, and supporting files
48+
*/
49+
additionalProperties.put("apiVersion", apiVersion);
50+
additionalProperties().put("prefix", PREFIX);
51+
52+
// Write defaults namespace in properties so that it can be accessible in templates.
53+
// At this point command line has not been parsed so if value is given
54+
// in command line it will supersede this content
55+
additionalProperties.put("cppNamespace", cppNamespace);
56+
// CLI options
57+
addOption(CPP_NAMESPACE, CPP_NAMESPACE_DESC, this.cppNamespace);
58+
/*
59+
* Language Specific Primitives. These types will not trigger imports by
60+
* the client generator
61+
*/
62+
languageSpecificPrimitives = new HashSet<String>(
63+
Arrays.asList(
64+
"bool",
65+
"qint32",
66+
"qint64",
67+
"float",
68+
"double")
69+
);
70+
nonFrameworkPrimitives.addAll(languageSpecificPrimitives);
71+
72+
foundationClasses.addAll(
73+
Arrays.asList(
74+
"QString",
75+
"QDate",
76+
"QDateTime",
77+
"QByteArray")
78+
);
79+
languageSpecificPrimitives.addAll(foundationClasses);
80+
super.typeMapping = new HashMap<String, String>();
81+
82+
typeMapping.put("date", "QDate");
83+
typeMapping.put("DateTime", "QDateTime");
84+
typeMapping.put("string", "QString");
85+
typeMapping.put("integer", "qint32");
86+
typeMapping.put("long", "qint64");
87+
typeMapping.put("boolean", "bool");
88+
typeMapping.put("array", "QList");
89+
typeMapping.put("map", "QMap");
90+
typeMapping.put("object", PREFIX + "Object");
91+
// mapped as "file" type for OAS 3.0
92+
typeMapping.put("ByteArray", "QByteArray");
93+
// UUID support - possible enhancement : use QUuid instead of QString.
94+
// beware though that Serialization/de-serialization of QUuid does not
95+
// come out of the box and will need to be sorted out (at least imply
96+
// modifications on multiple templates)
97+
typeMapping.put("UUID", "QString");
98+
typeMapping.put("file", "QIODevice");
99+
typeMapping.put("binary", "QIODevice");
100+
importMapping = new HashMap<String, String>();
101+
namespaces = new HashMap<String, String>();
102+
103+
systemIncludes.add("QString");
104+
systemIncludes.add("QList");
105+
systemIncludes.add("QMap");
106+
systemIncludes.add("QDate");
107+
systemIncludes.add("QDateTime");
108+
systemIncludes.add("QByteArray");
109+
systemIncludes.add("QIODevice");
110+
}
111+
@Override
112+
public void processOpts() {
113+
super.processOpts();
114+
115+
if (additionalProperties.containsKey("cppNamespace")) {
116+
cppNamespace = (String) additionalProperties.get("cppNamespace");
117+
}
118+
119+
additionalProperties.put("cppNamespaceDeclarations", cppNamespace.split("\\::"));
120+
if (additionalProperties.containsKey("modelNamePrefix")) {
121+
typeMapping.put("object", modelNamePrefix + "Object");
122+
additionalProperties().put("prefix", modelNamePrefix);
123+
}
124+
}
125+
126+
@Override
127+
public String toModelImport(String name) {
128+
if( name.isEmpty() ) {
129+
return null;
130+
}
131+
132+
if (namespaces.containsKey(name)) {
133+
return "using " + namespaces.get(name) + ";";
134+
} else if (systemIncludes.contains(name)) {
135+
return "#include <" + name + ">";
136+
} else if(importMapping.containsKey(name)){
137+
return importMapping.get(name);
138+
}
139+
140+
String folder = modelPackage().replace("::", File.separator);
141+
if (!folder.isEmpty())
142+
folder += File.separator;
143+
144+
return "#include \"" + folder + name + ".h\"";
145+
}
146+
147+
/**
148+
* Optional - type declaration. This is a String which is used by the templates to instantiate your
149+
* types. There is typically special handling for different property types
150+
*
151+
* @return a string value used as the `dataType` field for model templates, `returnType` for api templates
152+
*/
153+
@Override
154+
@SuppressWarnings("rawtypes")
155+
public String getTypeDeclaration(Schema p) {
156+
String openAPIType = getSchemaType(p);
157+
158+
if (ModelUtils.isArraySchema(p)) {
159+
ArraySchema ap = (ArraySchema) p;
160+
Schema inner = ap.getItems();
161+
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">";
162+
} else if (ModelUtils.isMapSchema(p)) {
163+
Schema inner = ModelUtils.getAdditionalProperties(p);
164+
return getSchemaType(p) + "<QString, " + getTypeDeclaration(inner) + ">";
165+
} else if (ModelUtils.isBinarySchema(p)) {
166+
return getSchemaType(p) + "*";
167+
} else if (ModelUtils.isFileSchema(p)) {
168+
return getSchemaType(p) + "*";
169+
}
170+
if (foundationClasses.contains(openAPIType)) {
171+
return openAPIType;
172+
} else if (languageSpecificPrimitives.contains(openAPIType)) {
173+
return toModelName(openAPIType);
174+
} else {
175+
return openAPIType;
176+
}
177+
}
178+
179+
@Override
180+
@SuppressWarnings("rawtypes")
181+
public String toDefaultValue(Schema p) {
182+
if (ModelUtils.isBooleanSchema(p)) {
183+
return "false";
184+
} else if (ModelUtils.isDateSchema(p)) {
185+
return "NULL";
186+
} else if (ModelUtils.isDateTimeSchema(p)) {
187+
return "NULL";
188+
} else if (ModelUtils.isNumberSchema(p)) {
189+
if (SchemaTypeUtil.FLOAT_FORMAT.equals(p.getFormat())) {
190+
return "0.0f";
191+
}
192+
return "0.0";
193+
} else if (ModelUtils.isIntegerSchema(p)) {
194+
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) {
195+
return "0L";
196+
}
197+
return "0";
198+
} else if (ModelUtils.isMapSchema(p)) {
199+
Schema inner = ModelUtils.getAdditionalProperties(p);
200+
return "QMap<QString, " + getTypeDeclaration(inner) + ">()";
201+
} else if (ModelUtils.isArraySchema(p)) {
202+
ArraySchema ap = (ArraySchema) p;
203+
Schema inner = ap.getItems();
204+
return "QList<" + getTypeDeclaration(inner) + ">()";
205+
} else if (ModelUtils.isStringSchema(p)) {
206+
return "QString(\"\")";
207+
} else if (!StringUtils.isEmpty(p.get$ref())) {
208+
return toModelName(ModelUtils.getSimpleRef(p.get$ref())) + "()";
209+
}
210+
return "NULL";
211+
}
212+
213+
@Override
214+
public String toModelFilename(String name) {
215+
return modelNamePrefix + initialCaps(name);
216+
}
217+
218+
/**
219+
* Optional - OpenAPI type conversion. This is used to map OpenAPI types in a `Schema` into
220+
* either language specific types via `typeMapping` or into complex models if there is not a mapping.
221+
*
222+
* @return a string value of the type or complex model for this property
223+
*/
224+
@Override
225+
@SuppressWarnings("rawtypes")
226+
public String getSchemaType(Schema p) {
227+
String openAPIType = super.getSchemaType(p);
228+
229+
String type = null;
230+
if (typeMapping.containsKey(openAPIType)) {
231+
type = typeMapping.get(openAPIType);
232+
if (languageSpecificPrimitives.contains(type)) {
233+
return toModelName(type);
234+
}
235+
if (foundationClasses.contains(type)) {
236+
return type;
237+
}
238+
} else {
239+
type = openAPIType;
240+
}
241+
return toModelName(type);
242+
}
243+
244+
@Override
245+
public String toVarName(String name) {
246+
// sanitize name
247+
String varName = name;
248+
varName = sanitizeName(name);
249+
250+
// if it's all uppper case, convert to lower case
251+
if (varName.matches("^[A-Z_]*$")) {
252+
varName = varName.toLowerCase(Locale.ROOT);
253+
}
254+
255+
// camelize (lower first character) the variable name
256+
// petId => pet_id
257+
varName = org.openapitools.codegen.utils.StringUtils.underscore(varName);
258+
259+
// for reserved word or word starting with number, append _
260+
if (isReservedWord(varName) || varName.matches("^\\d.*")) {
261+
varName = escapeReservedWord(varName);
262+
}
263+
264+
return varName;
265+
}
266+
267+
@Override
268+
public String toParamName(String name) {
269+
return toVarName(name);
270+
}
271+
272+
@Override
273+
public String getTypeDeclaration(String str) {
274+
return str;
275+
}
276+
277+
@Override
278+
protected boolean needToImport(String type) {
279+
return StringUtils.isNotBlank(type) && !defaultIncludes.contains(type)
280+
&& !nonFrameworkPrimitives.contains(type);
281+
}
282+
283+
284+
@Override
285+
@SuppressWarnings("unchecked")
286+
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
287+
Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
288+
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
289+
290+
List<Map<String, String>> imports = (List<Map<String, String>>) objs.get("imports");
291+
for (CodegenOperation operation : operations) {
292+
// Check all return parameter baseType if there is a necessity to include, include it if not
293+
// already done
294+
if (operation.returnBaseType != null && needToImport(operation.returnBaseType)) {
295+
if(!isIncluded(operation.returnBaseType, imports)) {
296+
imports.add(createMapping("import", operation.returnBaseType));
297+
}
298+
}
299+
List<CodegenParameter> params = new ArrayList<CodegenParameter>();
300+
if (operation.allParams != null)params.addAll(operation.allParams);
301+
302+
// Check all parameter baseType if there is a necessity to include, include it if not
303+
// already done
304+
for(CodegenParameter param : params) {
305+
if(param.isPrimitiveType && needToImport(param.baseType)) {
306+
if(!isIncluded(param.baseType, imports)) {
307+
imports.add(createMapping("import", param.baseType));
308+
}
309+
}
310+
}
311+
if (operation.pathParams != null) {
312+
// We use QString to pass path params, add it to include
313+
if(!isIncluded("QString", imports)) {
314+
imports.add(createMapping("import", "QString"));
315+
}
316+
}
317+
}
318+
if(isIncluded("QMap", imports)) {
319+
// Maps uses QString as key
320+
if(!isIncluded("QString", imports)) {
321+
imports.add(createMapping("import", "QString"));
322+
}
323+
}
324+
return objs;
325+
}
326+
327+
private Map<String, String> createMapping(String key, String value) {
328+
Map<String, String> customImport = new HashMap<String, String>();
329+
customImport.put(key, toModelImport(value));
330+
return customImport;
331+
}
332+
333+
private boolean isIncluded(String type, List<Map<String, String>> imports) {
334+
boolean included = false;
335+
String inclStr = toModelImport(type);
336+
for (Map<String, String> importItem : imports) {
337+
if(importItem.containsValue(inclStr)) {
338+
included = true;
339+
break;
340+
}
341+
}
342+
return included;
343+
}
344+
}

0 commit comments

Comments
 (0)