org.openapitools.codegen.languages.AbstractCSharpCodegen Maven / Gradle / Ivy
/*
* 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
*
* https://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 org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache.Lambda;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import io.swagger.v3.oas.models.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.templating.mustache.*;
import org.openapitools.codegen.templating.mustache.CopyLambda.CopyContent;
import org.openapitools.codegen.templating.mustache.CopyLambda.WhiteSpaceStrategy;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
public abstract class AbstractCSharpCodegen extends DefaultCodegen {
protected boolean optionalAssemblyInfoFlag = true;
protected boolean optionalEmitDefaultValuesFlag = false;
protected boolean conditionalSerialization = false;
protected boolean optionalProjectFileFlag = true;
@Setter protected boolean optionalMethodArgumentFlag = true;
protected boolean useDateTimeOffsetFlag = false;
protected boolean useDateTimeForDateFlag = false;
protected boolean useCollection = false;
@Setter protected boolean returnICollection = false;
@Setter protected boolean netCoreProjectFileFlag = false;
protected boolean nullReferenceTypesFlag = false;
protected boolean useSourceGeneration = false;
protected String modelPropertyNaming = CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.PascalCase.name();
@Setter protected String licenseUrl = "http://localhost";
@Setter protected String licenseName = "NoLicense";
@Setter protected String packageVersion = "1.0.0";
@Setter protected String packageName = "Org.OpenAPITools";
@Setter protected String packageTitle = "OpenAPI Library";
@Setter protected String packageProductName = "OpenAPILibrary";
@Setter protected String packageDescription = "A library generated from a OpenAPI doc";
@Setter protected String packageCompany = "OpenAPI";
@Setter protected String packageCopyright = "No Copyright";
@Setter protected String packageAuthors = "OpenAPI";
public static final String DATE_FORMAT = "dateFormat";
@Setter protected String dateFormat = "yyyy'-'MM'-'dd";
public static final String DATETIME_FORMAT = "dateTimeFormat";
@Setter protected String dateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK";
@Getter @Setter
protected String interfacePrefix = "I";
@Setter protected String enumNameSuffix = "Enum";
@Setter protected String enumValueSuffix = "Enum";
@Setter protected String sourceFolder = "src";
protected static final String invalidParameterNamePrefix = "var";
protected static final String invalidPropertyNamePrefix = "Var";
@Getter protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.PascalCase;
// TODO: Add option for test folder output location. Nice to allow e.g. ./test instead of ./src.
// This would require updating relative paths (e.g. path to main project file in test project file)
@Setter protected String testFolder = sourceFolder;
protected Set collectionTypes;
protected Set mapTypes;
// true if support nullable type
@Getter @Setter
protected boolean supportNullable = Boolean.FALSE;
@Setter protected Boolean zeroBasedEnums = null;
protected static final String zeroBasedEnumVendorExtension = "x-zero-based-enum";
private final Logger LOGGER = LoggerFactory.getLogger(AbstractCSharpCodegen.class);
// special property keywords not allowed as these are the function names in the model files
protected Set propertySpecialKeywords = new HashSet<>(Arrays.asList("ToString", "ToJson", "GetHashCode", "Equals", "ShouldSerializeToString"));
// A cache to efficiently lookup schema `toModelName()` based on the schema Key
private final Map schemaKeyToModelNameCache = new HashMap<>();
public AbstractCSharpCodegen() {
super();
supportsInheritance = true;
// C# does not use import mapping
importMapping.clear();
outputFolder = "generated-code" + File.separator + this.getName();
embeddedTemplateDir = templateDir = this.getName();
collectionTypes = new HashSet<>(
Arrays.asList(
"IList", "List",
"ICollection", "Collection",
"IEnumerable")
);
mapTypes = new HashSet<>(
Arrays.asList("IDictionary", "Dictionary")
);
// NOTE: C# uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons.
reservedWords.addAll(
Arrays.asList(
// set "client" as a reserved word to avoid conflicts with Org.OpenAPITools.Client
// this is a workaround and can be removed if c# api client is updated to use
// fully qualified name
"Client", "client", "parameter", "Configuration", "Version", "Environment",
"TimeZone", "OperatingSystem",
// local variable names in API methods (endpoints)
"localVarPath", "localVarPathParams", "localVarQueryParams", "localVarHeaderParams",
"localVarFormParams", "localVarFileParams", "localVarStatusCode", "localVarResponse",
"localVarPostBody", "localVarHttpHeaderAccepts", "localVarHttpHeaderAccept",
"localVarHttpContentTypes", "localVarHttpContentType",
"localVarStatusCode",
// C# reserved words
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked",
"class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else",
"enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for",
"foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
"long", "namespace", "new", "null", "object", "operator", "out", "override", "params",
"private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed",
"short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "system", "this", "throw",
"true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
"virtual", "void", "volatile", "while")
);
// TODO: Either include fully qualified names here or handle in DefaultCodegen via lastIndexOf(".") search
languageSpecificPrimitives = new HashSet<>(
Arrays.asList(
"String",
"string",
"bool?",
"bool",
"double?",
"double",
"decimal?",
"decimal",
"int?",
"int",
"uint",
"uint?",
"long?",
"long",
"ulong",
"ulong?",
"float?",
"float",
"byte[]",
"ICollection",
"Collection",
"List",
"Dictionary",
"DateTime?",
"DateTime",
"DateTimeOffset?",
"DateTimeOffset",
"DateOnly?",
"DateOnly",
"Boolean",
"Double",
"Decimal",
"Int32",
"Int64",
"Float",
"Guid?",
"Guid",
"System.IO.Stream", // not really a primitive, we include it to avoid model import
"Object")
);
instantiationTypes.put("array", "List");
instantiationTypes.put("list", "List");
instantiationTypes.put("map", "Dictionary");
}
public void setUseCollection(boolean useCollection) {
this.useCollection = useCollection;
if (useCollection) {
instantiationTypes.put("array", "Collection");
instantiationTypes.put("list", "Collection");
}
this.setTypeMapping();
}
public void useDateTimeOffset(boolean flag) {
this.useDateTimeOffsetFlag = flag;
this.setTypeMapping();
}
public void useDateTimeForDate(boolean flag) {
this.useDateTimeForDateFlag = flag;
this.setTypeMapping();
}
@Override
protected void addParentFromContainer(CodegenModel model, Schema schema) {
// we do not want to inherit simply because additionalProperties is true
// do nothing here
}
@Override
public void processOpts() {
super.processOpts();
if (StringUtils.isEmpty(System.getenv("CSHARP_POST_PROCESS_FILE"))) {
LOGGER.info("Environment variable CSHARP_POST_PROCESS_FILE not defined so the C# code may not be properly formatted by uncrustify (0.66 or later) or other code formatter. To define it, try `export CSHARP_POST_PROCESS_FILE=\"/usr/local/bin/uncrustify --no-backup\" && export UNCRUSTIFY_CONFIG=/path/to/uncrustify-rules.cfg` (Linux/Mac). Note: replace /path/to with the location of uncrustify-rules.cfg");
LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
} else if (!this.isEnablePostProcessFile()) {
LOGGER.info("Warning: Environment variable 'CSHARP_POST_PROCESS_FILE' is set but file post-processing is not enabled. To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
// License info
if (additionalProperties.containsKey(CodegenConstants.LICENSE_URL)) {
setLicenseUrl((String) additionalProperties.get(CodegenConstants.LICENSE_URL));
} else {
additionalProperties.put(CodegenConstants.LICENSE_URL, this.licenseUrl);
}
if (additionalProperties.containsKey(CodegenConstants.LICENSE_NAME)) {
setLicenseName((String) additionalProperties.get(CodegenConstants.LICENSE_NAME));
} else {
additionalProperties.put(CodegenConstants.LICENSE_NAME, this.licenseName);
}
// {{packageVersion}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
}
// {{sourceFolder}}
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
// TODO: Move to its own option when a parameter for 'testFolder' is added.
setTestFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
} else {
additionalProperties.put(CodegenConstants.SOURCE_FOLDER, this.sourceFolder);
}
// {{packageName}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
}
if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
LOGGER.warn(String.format(Locale.ROOT, "%s is not used by C# generators. Please use %s",
CodegenConstants.INVOKER_PACKAGE, CodegenConstants.PACKAGE_NAME));
}
// {{packageTitle}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_TITLE)) {
setPackageTitle((String) additionalProperties.get(CodegenConstants.PACKAGE_TITLE));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_TITLE, packageTitle);
}
// {{packageProductName}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_PRODUCTNAME)) {
setPackageProductName((String) additionalProperties.get(CodegenConstants.PACKAGE_PRODUCTNAME));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_PRODUCTNAME, packageProductName);
}
// {{dateFormat}}
if (additionalProperties.containsKey(DATE_FORMAT)) {
setDateFormat((String) additionalProperties.get(DATE_FORMAT));
} else {
additionalProperties.put(DATE_FORMAT, this.dateFormat);
}
// {{dateTimeFormat}}
if (additionalProperties.containsKey(DATETIME_FORMAT)) {
setDateTimeFormat((String) additionalProperties.get(DATETIME_FORMAT));
} else {
additionalProperties.put(DATETIME_FORMAT, this.dateTimeFormat);
}
// {{packageDescription}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_DESCRIPTION)) {
setPackageDescription((String) additionalProperties.get(CodegenConstants.PACKAGE_DESCRIPTION));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_DESCRIPTION, packageDescription);
}
// {{packageCompany}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_COMPANY)) {
setPackageCompany((String) additionalProperties.get(CodegenConstants.PACKAGE_COMPANY));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_COMPANY, packageCompany);
}
// {{packageCopyright}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_COPYRIGHT)) {
setPackageCopyright((String) additionalProperties.get(CodegenConstants.PACKAGE_COPYRIGHT));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_COPYRIGHT, packageCopyright);
}
// {{packageAuthors}}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_AUTHORS)) {
setPackageAuthors((String) additionalProperties.get(CodegenConstants.PACKAGE_AUTHORS));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_AUTHORS, packageAuthors);
}
// {{useDateTimeOffset}}
if (additionalProperties.containsKey(CodegenConstants.USE_DATETIME_OFFSET)) {
useDateTimeOffset(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_DATETIME_OFFSET));
} else {
additionalProperties.put(CodegenConstants.USE_DATETIME_OFFSET, useDateTimeOffsetFlag);
}
// {{useDateTimeForDate}}
if (additionalProperties.containsKey(CodegenConstants.USE_DATETIME_FOR_DATE)) {
useDateTimeForDate(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_DATETIME_FOR_DATE));
} else {
additionalProperties.put(CodegenConstants.USE_DATETIME_FOR_DATE, useDateTimeForDateFlag);
}
if (additionalProperties.containsKey(CodegenConstants.USE_COLLECTION)) {
setUseCollection(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_COLLECTION));
} else {
additionalProperties.put(CodegenConstants.USE_COLLECTION, useCollection);
}
if (additionalProperties.containsKey(CodegenConstants.RETURN_ICOLLECTION)) {
setReturnICollection(convertPropertyToBooleanAndWriteBack(CodegenConstants.RETURN_ICOLLECTION));
} else {
additionalProperties.put(CodegenConstants.RETURN_ICOLLECTION, returnICollection);
}
if (additionalProperties.containsKey(CodegenConstants.NETCORE_PROJECT_FILE)) {
setNetCoreProjectFileFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.NETCORE_PROJECT_FILE));
} else {
additionalProperties.put(CodegenConstants.NETCORE_PROJECT_FILE, netCoreProjectFileFlag);
}
if (additionalProperties.containsKey(CodegenConstants.NULLABLE_REFERENCE_TYPES)) {
setNullableReferenceTypes(convertPropertyToBooleanAndWriteBack(CodegenConstants.NULLABLE_REFERENCE_TYPES));
}
String zeroBasedEnums = "zeroBasedEnums";
if (additionalProperties.containsKey(zeroBasedEnums)) {
setZeroBasedEnums(convertPropertyToBooleanAndWriteBack(zeroBasedEnums));
}
if (additionalProperties.containsKey(CodegenConstants.INTERFACE_PREFIX)) {
String useInterfacePrefix = additionalProperties.get(CodegenConstants.INTERFACE_PREFIX).toString();
if ("false".equals(useInterfacePrefix.toLowerCase(Locale.ROOT))) {
setInterfacePrefix("");
} else if (!"true".equals(useInterfacePrefix.toLowerCase(Locale.ROOT))) {
// NOTE: if user passes "true" explicitly, we use the default I- prefix. The other supported case here is a custom prefix.
setInterfacePrefix(sanitizeName(useInterfacePrefix));
}
}
if (additionalProperties().containsKey(CodegenConstants.ENUM_NAME_SUFFIX)) {
setEnumNameSuffix(additionalProperties.get(CodegenConstants.ENUM_NAME_SUFFIX).toString());
}
if (additionalProperties().containsKey(CodegenConstants.ENUM_VALUE_SUFFIX)) {
setEnumValueSuffix(additionalProperties.get(CodegenConstants.ENUM_VALUE_SUFFIX).toString());
}
if (additionalProperties.containsKey(CodegenConstants.ENUM_PROPERTY_NAMING)) {
setEnumPropertyNaming((String) additionalProperties.get(CodegenConstants.ENUM_PROPERTY_NAMING));
}
// This either updates additionalProperties with the above fixes, or sets the default if the option was not specified.
additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);
// add lambda for mustache templates
additionalProperties.put("lambdaCref", new Mustache.Lambda() {
@Override
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
String content = fragment.execute();
content = content.trim().replace("<", "{");
content = content.replace(">", "}");
content = content.replace("{string}", "{String}");
writer.write(content);
}
});
this.setTypeMapping();
}
@Override
protected ImmutableMap.Builder addMustacheLambdas() {
final CopyContent copyContent = new CopyContent();
return super.addMustacheLambdas()
.put("camelcase_sanitize_param", new CamelCaseAndSanitizeLambda().generator(this).escapeAsParamName(true))
.put("required", new RequiredParameterLambda())
.put("optional", new OptionalParameterLambda().generator(this))
.put("joinWithComma", new JoinWithCommaLambda())
.put("joinWithAmpersand", new JoinWithCommaLambda(true, " ", " && "))
.put("joinLinesWithComma", new JoinWithCommaLambda(false, "\n", ",\n"))
.put("joinConditions", new JoinWithCommaLambda(true, " ", " && "))
.put("trimLineBreaks", new TrimLineBreaksLambda())
.put("trimTrailingWithNewLine", new TrimTrailingWhiteSpaceLambda(true))
.put("trimTrailing", new TrimTrailingWhiteSpaceLambda(false))
.put("first", new FirstLambda(" "))
.put("firstDot", new FirstLambda("\\."))
.put("indent1", new IndentedLambda(4, " ", false, true))
.put("indentAll1", new IndentedLambda(4, " ", true, true))
.put("indent3", new IndentedLambda(12, " ", false, true))
.put("indent4", new IndentedLambda(16, " ", false, true))
.put("copy", new CopyLambda(copyContent, WhiteSpaceStrategy.None, WhiteSpaceStrategy.None))
.put("copyText", new CopyLambda(copyContent, WhiteSpaceStrategy.Strip, WhiteSpaceStrategy.StripLineBreakIfPresent))
.put("paste", new PasteLambda(copyContent, false))
.put("pasteOnce", new PasteLambda(copyContent, true))
.put("uniqueLines", new UniqueLambda("\n", false))
.put("unique", new UniqueLambda("\n", true))
.put("camel_case", new CamelCaseLambda())
.put("escape_reserved_word", new EscapeKeywordLambda(this::escapeKeyword))
.put("alphabet_or_underscore", new ReplaceAllLambda("[^A-Za-z]", "_"));
}
@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
super.postProcessModelProperty(model, property);
if (property.isInnerEnum && property.items != null) {
// format maps of inner enums to include the classname eg: Dictionary
property.datatypeWithEnum = property.datatypeWithEnum.replace(property.items.datatypeWithEnum, model.classname + "." + property.items.datatypeWithEnum);
property.dataType = property.datatypeWithEnum;
}
if (property.isEnum && !property.vendorExtensions.containsKey(AbstractCSharpCodegen.zeroBasedEnumVendorExtension)) {
if (Boolean.TRUE.equals(this.zeroBasedEnums)) {
property.vendorExtensions.put(AbstractCSharpCodegen.zeroBasedEnumVendorExtension, true);
} else if (!Boolean.FALSE.equals(this.zeroBasedEnums)) {
if (property.allowableValues.containsKey("values")) {
final List> allowableValues = (List>) property.allowableValues.get("values");
boolean isZeroBased = String.valueOf(allowableValues.get(0)).toLowerCase(Locale.ROOT).equals("unknown");
property.vendorExtensions.put(AbstractCSharpCodegen.zeroBasedEnumVendorExtension, isZeroBased);
}
}
}
if (property.isMap || property.isContainer) {
// maps of enums will be marked both isMap and isEnum, correct that now
property.isEnum = false;
property.isInnerEnum = false;
property.isString = false;
}
Double maximum = asDouble(property.maximum);
if (property.dataType.equals("int") && maximum != null) {
if ((!property.exclusiveMaximum && asInteger(property.maximum) == null) || (property.exclusiveMaximum && asInteger((maximum + 1) + "") == null)) {
property.dataType = "long";
property.datatypeWithEnum = "long";
}
}
Double minimum = asDouble(property.minimum);
if (property.dataType.equals("int") && minimum != null) {
if ((!property.exclusiveMinimum && asInteger(property.minimum) == null) || (property.exclusiveMinimum && asInteger((minimum - 1) + "") == null)) {
property.dataType = "long";
property.datatypeWithEnum = "long";
}
}
}
/** If the value can be parsed as a double, returns the value, otherwise returns null */
public static Double asDouble(String strNum) {
if (strNum == null) {
return null;
}
try {
return Double.parseDouble(strNum);
} catch (NumberFormatException nfe) {
return null;
}
}
/** If the value can be parsed as an integer, returns the value, otherwise returns null */
public static Integer asInteger(String strNum) {
if (strNum == null) {
return null;
}
try {
return Integer.parseInt(strNum);
} catch (NumberFormatException nfe) {
return null;
}
}
@Override
public ModelsMap postProcessModels(ModelsMap objs) {
for (ModelMap mo : objs.getModels()) {
CodegenModel cm = mo.getModel();
if (cm.getComposedSchemas() != null) {
List oneOf = cm.getComposedSchemas().getOneOf();
if (oneOf != null) {
Set dataTypeSet = new HashSet<>();
for (CodegenProperty oneOfProperty : oneOf) {
if (dataTypeSet.contains(oneOfProperty.dataType)) {
// add "x-duplicated-data-type" to indicate if the dataType already occurs before
// in other sub-schemas of allOf/anyOf/oneOf
oneOfProperty.vendorExtensions.putIfAbsent("x-composed-data-type", true);
} else {
dataTypeSet.add(oneOfProperty.dataType);
}
}
}
List anyOf = cm.getComposedSchemas().getAnyOf();
if (anyOf != null) {
Set dataTypeSet = new HashSet<>();
for (CodegenProperty anyOfProperty : anyOf) {
if (dataTypeSet.contains(anyOfProperty.dataType)) {
// add "x-duplicated-data-type" to indicate if the dataType already occurs before
// in other sub-schemas of allOf/anyOf/oneOf
anyOfProperty.vendorExtensions.putIfAbsent("x-composed-data-type", true);
} else {
dataTypeSet.add(anyOfProperty.dataType);
}
}
}
}
if (cm.isEnum && !cm.vendorExtensions.containsKey(AbstractCSharpCodegen.zeroBasedEnumVendorExtension)) {
if (Boolean.TRUE.equals(this.zeroBasedEnums)) {
cm.vendorExtensions.put(AbstractCSharpCodegen.zeroBasedEnumVendorExtension, true);
} else if (!Boolean.FALSE.equals(this.zeroBasedEnums)) {
if (cm.allowableValues.containsKey("values")) {
final List> allowableValues = (List>) cm.allowableValues.get("values");
boolean isZeroBased = String.valueOf(allowableValues.get(0)).toLowerCase(Locale.ROOT).equals("unknown");
cm.vendorExtensions.put(AbstractCSharpCodegen.zeroBasedEnumVendorExtension, isZeroBased);
}
}
}
}
// process enum in models
return postProcessModelsEnum(objs);
}
/**
* Invoked by {@link DefaultGenerator} after all models have been post-processed, allowing for a last pass of codegen-specific model cleanup.
*
* @param objs Current state of codegen object model.
* @return An in-place modified state of the codegen object model.
*/
@Override
public Map postProcessAllModels(Map objs) {
final Map processed = super.postProcessAllModels(objs);
Map enumRefs = new HashMap<>();
for (Map.Entry entry : processed.entrySet()) {
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), processed);
if (model == null) {
continue;
}
// if we don't call setHasDiscriminatorWithNonEmptyMapping then hasDiscriminatorWithNonEmptyMapping will be false, and we need it in the JsonConverter
// the checks on oneOf and anyOf must be there or else hasDiscriminatorWithNonEmptyMapping will be true for GrandparentAnimal.
// GrandparentAnimal has a discriminator, but no oneOf nor anyOf
// modules\openapi-generator\src\test\resources\3_0\csharp\petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml
model.setHasDiscriminatorWithNonEmptyMapping(
((model.anyOf != null && model.anyOf.size() > 0) || (model.oneOf != null && model.oneOf.size() > 0)) &&
model.discriminator != null &&
model.discriminator.getMappedModels() != null &&
model.discriminator.getMappedModels().size() > 0);
if (model.isEnum) {
enumRefs.put(model.getClassname(), model);
}
}
for (Map.Entry entry : objs.entrySet()) {
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), objs);
if (model == null) {
continue;
}
model.vendorExtensions.put("x-model-is-mutable", modelIsMutable(model, null));
CodegenComposedSchemas composedSchemas = model.getComposedSchemas();
if (composedSchemas != null) {
List allOf = composedSchemas.getAllOf();
if (allOf != null) {
for (CodegenProperty property : allOf) {
property.name = patchPropertyName(model, camelize(property.baseType));
patchPropertyVendorExtensions(property);
}
}
List anyOf = composedSchemas.getAnyOf();
if (anyOf != null) {
removePropertiesDeclaredInComposedTypes(objs, model, anyOf);
for (CodegenProperty property : anyOf) {
property.name = patchPropertyName(model, camelize(property.baseType));
property.isNullable = true;
patchPropertyVendorExtensions(property);
property.vendorExtensions.put("x-base-name", model.name.substring(model.name.lastIndexOf('_') + 1));
}
}
List oneOf = composedSchemas.getOneOf();
if (oneOf != null) {
removePropertiesDeclaredInComposedTypes(objs, model, oneOf);
for (CodegenProperty property : oneOf) {
property.name = patchPropertyName(model, camelize(property.baseType));
property.isNullable = true;
patchPropertyVendorExtensions(property);
property.vendorExtensions.put("x-base-name", model.name.substring(model.name.lastIndexOf('_') + 1));
}
}
}
// https://github.com/OpenAPITools/openapi-generator/issues/12324
// TODO: why do these collections contain different instances?
// fixing allVars should suffice instead of patching every collection
for (CodegenProperty property : model.allVars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.vars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.readWriteVars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.optionalVars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.parentVars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.requiredVars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.readOnlyVars) {
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.nonNullableVars) {
patchProperty(enumRefs, model, property);
}
}
return processed;
}
/**
* Returns true if the model contains any properties with a public setter
* If true, the model's constructor accessor should be made public to ensure end users
* can instantiate the object. If false, then the model is only ever given
* to us by the server, so we do not need a public constructor
*/
private boolean modelIsMutable(CodegenModel model, Set processed) {
if (processed == null) {
processed = new HashSet();
}
boolean isMutable = model.allVars.stream().anyMatch(v -> !v.isReadOnly);
if (!isMutable && !processed.contains(model.classname) && model.getDiscriminator() != null && model.getDiscriminator().getMappedModels() != null) {
processed.add(model.classname);
isMutable = modelIsMutable(model, processed);
}
return isMutable;
}
protected void removePropertiesDeclaredInComposedTypes(Map objs, CodegenModel model, List composedProperties) {
}
private String patchPropertyName(CodegenModel model, String value) {
String name = escapeReservedWord(model, value);
if (name.startsWith(AbstractCSharpCodegen.invalidParameterNamePrefix)) {
name = AbstractCSharpCodegen.invalidPropertyNamePrefix + name.substring(AbstractCSharpCodegen.invalidParameterNamePrefix.length());
}
return name;
}
private void patchPropertyVendorExtensions(CodegenProperty property) {
boolean isValueType = isValueType(property);
property.vendorExtensions.put("x-is-value-type", isValueType);
property.vendorExtensions.put("x-is-reference-type", !isValueType);
property.vendorExtensions.put("x-is-nullable-type", this.getNullableReferencesTypes() || isValueType);
}
protected void patchProperty(Map enumRefs, CodegenModel model, CodegenProperty property) {
if (enumRefs.containsKey(property.dataType)) {
// Handle any enum properties referred to by $ref.
// This is different in C# than most other generators, because enums in C# are compiled to integral types,
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(property.dataType);
property.allowableValues = refModel.allowableValues;
property.isEnum = true;
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
property.isPrimitiveType = true;
}
patchPropertyVendorExtensions(property);
property.name = patchPropertyName(model, property.name);
String[] nestedTypes = { "List", "Collection", "ICollection", "Dictionary" };
Arrays.stream(nestedTypes).forEach(nestedType -> {
// fix incorrect data types for maps of maps
if (property.datatypeWithEnum.contains(", " + nestedType + ">") && property.items != null) {
property.datatypeWithEnum = property.datatypeWithEnum.replace(", " + nestedType + ">", ", " + property.items.datatypeWithEnum + ">");
property.dataType = property.datatypeWithEnum;
}
if (property.datatypeWithEnum.contains("<" + nestedType + ">") && property.items != null) {
property.datatypeWithEnum = property.datatypeWithEnum.replace("<" + nestedType + ">", "<" + property.items.datatypeWithEnum + ">");
property.dataType = property.datatypeWithEnum;
}
});
// HOTFIX: https://github.com/OpenAPITools/openapi-generator/issues/14944
if (property.datatypeWithEnum.equals("decimal")) {
property.isDecimal = true;
}
}
@Override
protected List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy