org.openapitools.codegen.languages.AbstractJavaCodegen 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.base.Strings;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import java.time.LocalDate;
import java.time.ZoneId;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.features.*;
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.Pattern;
import static org.openapitools.codegen.utils.OnceLogger.once;
import static org.openapitools.codegen.utils.StringUtils.*;
public abstract class AbstractJavaCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractJavaCodegen.class);
private static final String ARTIFACT_VERSION_DEFAULT_VALUE = "1.0.0";
public static final String FULL_JAVA_UTIL = "fullJavaUtil";
public static final String DEFAULT_LIBRARY = "";
public static final String DATE_LIBRARY = "dateLibrary";
public static final String JAVA8_MODE = "java8";
public static final String SUPPORT_ASYNC = "supportAsync";
public static final String WITH_XML = "withXml";
public static final String SUPPORT_JAVA6 = "supportJava6";
public static final String DISABLE_HTML_ESCAPING = "disableHtmlEscaping";
public static final String BOOLEAN_GETTER_PREFIX = "booleanGetterPrefix";
public static final String ADDITIONAL_MODEL_TYPE_ANNOTATIONS = "additionalModelTypeAnnotations";
protected String dateLibrary = "threetenbp";
protected boolean supportAsync = false;
protected boolean java8Mode = false;
protected boolean withXml = false;
protected String invokerPackage = "org.openapitools";
protected String groupId = "org.openapitools";
protected String artifactId = "openapi-java";
protected String artifactVersion = null;
protected String artifactUrl = "https://github.com/openapitools/openapi-generator";
protected String artifactDescription = "OpenAPI Java";
protected String developerName = "OpenAPI-Generator Contributors";
protected String developerEmail = "[email protected]";
protected String developerOrganization = "OpenAPITools.org";
protected String developerOrganizationUrl = "http://openapitools.org";
protected String scmConnection = "scm:git:[email protected]:openapitools/openapi-generator.git";
protected String scmDeveloperConnection = "scm:git:[email protected]:openapitools/openapi-generator.git";
protected String scmUrl = "https://github.com/openapitools/openapi-generator";
protected String licenseName = "Unlicense";
protected String licenseUrl = "http://unlicense.org";
protected String projectFolder = "src/main";
protected String projectTestFolder = "src/test";
protected String sourceFolder = projectFolder + "/java";
protected String testFolder = projectTestFolder + "/java";
protected boolean fullJavaUtil;
protected String javaUtilPrefix = "";
protected Boolean serializableModel = false;
protected boolean serializeBigDecimalAsString = false;
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";
protected boolean supportJava6 = false;
protected boolean disableHtmlEscaping = false;
protected String booleanGetterPrefix = "get";
protected String parentGroupId = "";
protected String parentArtifactId = "";
protected String parentVersion = "";
protected boolean parentOverridden = false;
protected List additionalModelTypeAnnotations = new LinkedList<>();
public AbstractJavaCodegen() {
super();
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML))
.securityFeatures(EnumSet.noneOf(
SecurityFeature.class
))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling
)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism
)
.includeClientModificationFeatures(
ClientModificationFeature.BasePath
)
);
supportsInheritance = true;
modelTemplateFiles.put("model.mustache", ".java");
apiTemplateFiles.put("api.mustache", ".java");
apiTestTemplateFiles.put("api_test.mustache", ".java");
modelDocTemplateFiles.put("model_doc.mustache", ".md");
apiDocTemplateFiles.put("api_doc.mustache", ".md");
hideGenerationTimestamp = false;
setReservedWordsLowerCase(
Arrays.asList(
// special words
"object",
// used as internal variables, can collide with parameter names
"localVarPath", "localVarQueryParams", "localVarCollectionQueryParams",
"localVarHeaderParams", "localVarCookieParams", "localVarFormParams", "localVarPostBody",
"localVarAccepts", "localVarAccept", "localVarContentTypes",
"localVarContentType", "localVarAuthNames", "localReturnType",
"ApiClient", "ApiException", "ApiResponse", "Configuration", "StringUtil",
// language reserved words
"abstract", "continue", "for", "new", "switch", "assert",
"default", "if", "package", "synchronized", "boolean", "do", "goto", "private",
"this", "break", "double", "implements", "protected", "throw", "byte", "else",
"import", "public", "throws", "case", "enum", "instanceof", "return", "transient",
"catch", "extends", "int", "short", "try", "char", "final", "interface", "static",
"void", "class", "finally", "long", "strictfp", "volatile", "const", "float",
"native", "super", "while", "null")
);
languageSpecificPrimitives = new HashSet(
Arrays.asList(
"String",
"boolean",
"Boolean",
"Double",
"Integer",
"Long",
"Float",
"Object",
"byte[]")
);
instantiationTypes.put("array", "ArrayList");
instantiationTypes.put("map", "HashMap");
typeMapping.put("date", "Date");
typeMapping.put("file", "File");
cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
cliOptions.add(new CliOption(CodegenConstants.INVOKER_PACKAGE, CodegenConstants.INVOKER_PACKAGE_DESC).defaultValue(this.getInvokerPackage()));
cliOptions.add(new CliOption(CodegenConstants.GROUP_ID, CodegenConstants.GROUP_ID_DESC).defaultValue(this.getGroupId()));
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_ID, CodegenConstants.ARTIFACT_ID_DESC).defaultValue(this.getArtifactId()));
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, CodegenConstants.ARTIFACT_VERSION_DESC).defaultValue(ARTIFACT_VERSION_DEFAULT_VALUE));
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_URL, CodegenConstants.ARTIFACT_URL_DESC).defaultValue(this.getArtifactUrl()));
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_DESCRIPTION, CodegenConstants.ARTIFACT_DESCRIPTION_DESC).defaultValue(this.getArtifactDescription()));
cliOptions.add(new CliOption(CodegenConstants.SCM_CONNECTION, CodegenConstants.SCM_CONNECTION_DESC).defaultValue(this.getScmConnection()));
cliOptions.add(new CliOption(CodegenConstants.SCM_DEVELOPER_CONNECTION, CodegenConstants.SCM_DEVELOPER_CONNECTION_DESC).defaultValue(this.getScmDeveloperConnection()));
cliOptions.add(new CliOption(CodegenConstants.SCM_URL, CodegenConstants.SCM_URL_DESC).defaultValue(this.getScmUrl()));
cliOptions.add(new CliOption(CodegenConstants.DEVELOPER_NAME, CodegenConstants.DEVELOPER_NAME_DESC).defaultValue(this.getDeveloperName()));
cliOptions.add(new CliOption(CodegenConstants.DEVELOPER_EMAIL, CodegenConstants.DEVELOPER_EMAIL_DESC).defaultValue(this.getDeveloperEmail()));
cliOptions.add(new CliOption(CodegenConstants.DEVELOPER_ORGANIZATION, CodegenConstants.DEVELOPER_ORGANIZATION_DESC).defaultValue(this.getDeveloperOrganization()));
cliOptions.add(new CliOption(CodegenConstants.DEVELOPER_ORGANIZATION_URL, CodegenConstants.DEVELOPER_ORGANIZATION_URL_DESC).defaultValue(this.getDeveloperOrganizationUrl()));
cliOptions.add(new CliOption(CodegenConstants.LICENSE_NAME, CodegenConstants.LICENSE_NAME_DESC).defaultValue(this.getLicenseName()));
cliOptions.add(new CliOption(CodegenConstants.LICENSE_URL, CodegenConstants.LICENSE_URL_DESC).defaultValue(this.getLicenseUrl()));
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC).defaultValue(this.getSourceFolder()));
cliOptions.add(CliOption.newBoolean(CodegenConstants.SERIALIZABLE_MODEL, CodegenConstants.SERIALIZABLE_MODEL_DESC, this.getSerializableModel()));
cliOptions.add(CliOption.newBoolean(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING, CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING_DESC, serializeBigDecimalAsString));
cliOptions.add(CliOption.newBoolean(FULL_JAVA_UTIL, "whether to use fully qualified name for classes under java.util. This option only works for Java API client", fullJavaUtil));
cliOptions.add(CliOption.newBoolean(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC, this.isHideGenerationTimestamp()));
cliOptions.add(CliOption.newBoolean(WITH_XML, "whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)"));
CliOption dateLibrary = new CliOption(DATE_LIBRARY, "Option. Date library to use").defaultValue(this.getDateLibrary());
Map dateOptions = new HashMap<>();
dateOptions.put("java8", "Java 8 native JSR310 (preferred for jdk 1.8+) - note: this also sets \"" + JAVA8_MODE + "\" to true");
dateOptions.put("threetenbp", "Backport of JSR310 (preferred for jdk < 1.8)");
dateOptions.put("java8-localdatetime", "Java 8 using LocalDateTime (for legacy app only)");
dateOptions.put("joda", "Joda (for legacy app only)");
dateOptions.put("legacy", "Legacy java.util.Date (if you really have a good reason not to use threetenbp");
dateLibrary.setEnum(dateOptions);
cliOptions.add(dateLibrary);
CliOption java8Mode = CliOption.newBoolean(JAVA8_MODE, "Option. Use Java8 classes instead of third party equivalents", this.java8Mode);
Map java8ModeOptions = new HashMap<>();
java8ModeOptions.put("true", "Use Java 8 classes such as Base64");
java8ModeOptions.put("false", "Various third party libraries as needed");
java8Mode.setEnum(java8ModeOptions);
cliOptions.add(java8Mode);
cliOptions.add(CliOption.newBoolean(DISABLE_HTML_ESCAPING, "Disable HTML escaping of JSON strings when using gson (needed to avoid problems with byte[] fields)", disableHtmlEscaping));
cliOptions.add(CliOption.newString(BOOLEAN_GETTER_PREFIX, "Set booleanGetterPrefix").defaultValue(this.getBooleanGetterPrefix()));
cliOptions.add(CliOption.newString(ADDITIONAL_MODEL_TYPE_ANNOTATIONS, "Additional annotations for model type(class level annotations)"));
cliOptions.add(CliOption.newString(CodegenConstants.PARENT_GROUP_ID, CodegenConstants.PARENT_GROUP_ID_DESC));
cliOptions.add(CliOption.newString(CodegenConstants.PARENT_ARTIFACT_ID, CodegenConstants.PARENT_ARTIFACT_ID_DESC));
cliOptions.add(CliOption.newString(CodegenConstants.PARENT_VERSION, CodegenConstants.PARENT_VERSION_DESC));
CliOption snapShotVersion = CliOption.newString(CodegenConstants.SNAPSHOT_VERSION, CodegenConstants.SNAPSHOT_VERSION_DESC);
Map snapShotVersionOptions = new HashMap<>();
snapShotVersionOptions.put("true", "Use a SnapShot Version");
snapShotVersionOptions.put("false", "Use a Release Version");
snapShotVersion.setEnum(snapShotVersionOptions);
cliOptions.add(snapShotVersion);
}
@Override
public void processOpts() {
super.processOpts();
if (StringUtils.isEmpty(System.getenv("JAVA_POST_PROCESS_FILE"))) {
LOGGER.info("Environment variable JAVA_POST_PROCESS_FILE not defined so the Java code may not be properly formatted. To define it, try 'export JAVA_POST_PROCESS_FILE=\"/usr/local/bin/clang-format -i\"' (Linux/Mac)");
LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
if (additionalProperties.containsKey(SUPPORT_JAVA6)) {
this.setSupportJava6(Boolean.valueOf(additionalProperties.get(SUPPORT_JAVA6).toString()));
}
additionalProperties.put(SUPPORT_JAVA6, supportJava6);
if (additionalProperties.containsKey(DISABLE_HTML_ESCAPING)) {
this.setDisableHtmlEscaping(Boolean.valueOf(additionalProperties.get(DISABLE_HTML_ESCAPING).toString()));
}
additionalProperties.put(DISABLE_HTML_ESCAPING, disableHtmlEscaping);
if (additionalProperties.containsKey(BOOLEAN_GETTER_PREFIX)) {
this.setBooleanGetterPrefix(additionalProperties.get(BOOLEAN_GETTER_PREFIX).toString());
}
additionalProperties.put(BOOLEAN_GETTER_PREFIX, booleanGetterPrefix);
if (additionalProperties.containsKey(ADDITIONAL_MODEL_TYPE_ANNOTATIONS)) {
String additionalAnnotationsList = additionalProperties.get(ADDITIONAL_MODEL_TYPE_ANNOTATIONS).toString();
this.setAdditionalModelTypeAnnotations(Arrays.asList(additionalAnnotationsList.split(";")));
}
if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
} else if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
// guess from api package
String derivedInvokerPackage = deriveInvokerPackageName((String) additionalProperties.get(CodegenConstants.API_PACKAGE));
this.additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, derivedInvokerPackage);
this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
LOGGER.info("Invoker Package Name, originally not set, is now derived from api package name: " + derivedInvokerPackage);
} else if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
// guess from model package
String derivedInvokerPackage = deriveInvokerPackageName((String) additionalProperties.get(CodegenConstants.MODEL_PACKAGE));
this.additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, derivedInvokerPackage);
this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
LOGGER.info("Invoker Package Name, originally not set, is now derived from model package name: " + derivedInvokerPackage);
} else {
//not set, use default to be passed to template
additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
}
if (additionalProperties.containsKey(CodegenConstants.GROUP_ID)) {
this.setGroupId((String) additionalProperties.get(CodegenConstants.GROUP_ID));
} else {
//not set, use to be passed to template
additionalProperties.put(CodegenConstants.GROUP_ID, groupId);
}
if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_ID)) {
this.setArtifactId((String) additionalProperties.get(CodegenConstants.ARTIFACT_ID));
} else {
//not set, use to be passed to template
additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId);
}
if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_URL)) {
this.setArtifactUrl((String) additionalProperties.get(CodegenConstants.ARTIFACT_URL));
} else {
additionalProperties.put(CodegenConstants.ARTIFACT_URL, artifactUrl);
}
if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_DESCRIPTION)) {
this.setArtifactDescription((String) additionalProperties.get(CodegenConstants.ARTIFACT_DESCRIPTION));
} else {
additionalProperties.put(CodegenConstants.ARTIFACT_DESCRIPTION, artifactDescription);
}
if (additionalProperties.containsKey(CodegenConstants.SCM_CONNECTION)) {
this.setScmConnection((String) additionalProperties.get(CodegenConstants.SCM_CONNECTION));
} else {
additionalProperties.put(CodegenConstants.SCM_CONNECTION, scmConnection);
}
if (additionalProperties.containsKey(CodegenConstants.SCM_DEVELOPER_CONNECTION)) {
this.setScmDeveloperConnection((String) additionalProperties.get(CodegenConstants.SCM_DEVELOPER_CONNECTION));
} else {
additionalProperties.put(CodegenConstants.SCM_DEVELOPER_CONNECTION, scmDeveloperConnection);
}
if (additionalProperties.containsKey(CodegenConstants.SCM_URL)) {
this.setScmUrl((String) additionalProperties.get(CodegenConstants.SCM_URL));
} else {
additionalProperties.put(CodegenConstants.SCM_URL, scmUrl);
}
if (additionalProperties.containsKey(CodegenConstants.DEVELOPER_NAME)) {
this.setDeveloperName((String) additionalProperties.get(CodegenConstants.DEVELOPER_NAME));
} else {
additionalProperties.put(CodegenConstants.DEVELOPER_NAME, developerName);
}
if (additionalProperties.containsKey(CodegenConstants.DEVELOPER_EMAIL)) {
this.setDeveloperEmail((String) additionalProperties.get(CodegenConstants.DEVELOPER_EMAIL));
} else {
additionalProperties.put(CodegenConstants.DEVELOPER_EMAIL, developerEmail);
}
if (additionalProperties.containsKey(CodegenConstants.DEVELOPER_ORGANIZATION)) {
this.setDeveloperOrganization((String) additionalProperties.get(CodegenConstants.DEVELOPER_ORGANIZATION));
} else {
additionalProperties.put(CodegenConstants.DEVELOPER_ORGANIZATION, developerOrganization);
}
if (additionalProperties.containsKey(CodegenConstants.DEVELOPER_ORGANIZATION_URL)) {
this.setDeveloperOrganizationUrl((String) additionalProperties.get(CodegenConstants.DEVELOPER_ORGANIZATION_URL));
} else {
additionalProperties.put(CodegenConstants.DEVELOPER_ORGANIZATION_URL, developerOrganizationUrl);
}
if (additionalProperties.containsKey(CodegenConstants.LICENSE_NAME)) {
this.setLicenseName((String) additionalProperties.get(CodegenConstants.LICENSE_NAME));
} else {
additionalProperties.put(CodegenConstants.LICENSE_NAME, licenseName);
}
if (additionalProperties.containsKey(CodegenConstants.LICENSE_URL)) {
this.setLicenseUrl((String) additionalProperties.get(CodegenConstants.LICENSE_URL));
} else {
additionalProperties.put(CodegenConstants.LICENSE_URL, licenseUrl);
}
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
}
additionalProperties.put(CodegenConstants.SOURCE_FOLDER, sourceFolder);
if (additionalProperties.containsKey(CodegenConstants.SERIALIZABLE_MODEL)) {
this.setSerializableModel(Boolean.valueOf(additionalProperties.get(CodegenConstants.SERIALIZABLE_MODEL).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.LIBRARY)) {
this.setLibrary((String) additionalProperties.get(CodegenConstants.LIBRARY));
}
if (additionalProperties.containsKey(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING)) {
this.setSerializeBigDecimalAsString(Boolean.valueOf(additionalProperties.get(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING).toString()));
}
// need to put back serializableModel (boolean) into additionalProperties as value in additionalProperties is string
additionalProperties.put(CodegenConstants.SERIALIZABLE_MODEL, serializableModel);
if (additionalProperties.containsKey(FULL_JAVA_UTIL)) {
this.setFullJavaUtil(Boolean.valueOf(additionalProperties.get(FULL_JAVA_UTIL).toString()));
}
if (fullJavaUtil) {
javaUtilPrefix = "java.util.";
}
additionalProperties.put(FULL_JAVA_UTIL, fullJavaUtil);
additionalProperties.put("javaUtilPrefix", javaUtilPrefix);
if (additionalProperties.containsKey(WITH_XML)) {
this.setWithXml(Boolean.valueOf(additionalProperties.get(WITH_XML).toString()));
}
additionalProperties.put(WITH_XML, withXml);
if (additionalProperties.containsKey(CodegenConstants.PARENT_GROUP_ID)) {
this.setParentGroupId((String) additionalProperties.get(CodegenConstants.PARENT_GROUP_ID));
}
if (additionalProperties.containsKey(CodegenConstants.PARENT_ARTIFACT_ID)) {
this.setParentArtifactId((String) additionalProperties.get(CodegenConstants.PARENT_ARTIFACT_ID));
}
if (additionalProperties.containsKey(CodegenConstants.PARENT_VERSION)) {
this.setParentVersion((String) additionalProperties.get(CodegenConstants.PARENT_VERSION));
}
if (!StringUtils.isEmpty(parentGroupId) && !StringUtils.isEmpty(parentArtifactId) && !StringUtils.isEmpty(parentVersion)) {
additionalProperties.put("parentOverridden", true);
}
// make api and model doc path available in mustache template
additionalProperties.put("apiDocPath", apiDocPath);
additionalProperties.put("modelDocPath", modelDocPath);
importMapping.put("List", "java.util.List");
if (fullJavaUtil) {
typeMapping.put("array", "java.util.List");
typeMapping.put("map", "java.util.Map");
typeMapping.put("DateTime", "java.util.Date");
typeMapping.put("UUID", "java.util.UUID");
typeMapping.remove("List");
importMapping.remove("Date");
importMapping.remove("Map");
importMapping.remove("HashMap");
importMapping.remove("Array");
importMapping.remove("ArrayList");
importMapping.remove("List");
importMapping.remove("Set");
importMapping.remove("DateTime");
importMapping.remove("UUID");
instantiationTypes.put("array", "java.util.ArrayList");
instantiationTypes.put("map", "java.util.HashMap");
}
this.sanitizeConfig();
// optional jackson mappings for BigDecimal support
importMapping.put("ToStringSerializer", "com.fasterxml.jackson.databind.ser.std.ToStringSerializer");
importMapping.put("JsonSerialize", "com.fasterxml.jackson.databind.annotation.JsonSerialize");
// imports for pojos
importMapping.put("ApiModelProperty", "io.swagger.annotations.ApiModelProperty");
importMapping.put("ApiModel", "io.swagger.annotations.ApiModel");
importMapping.put("BigDecimal", "java.math.BigDecimal");
importMapping.put("JsonProperty", "com.fasterxml.jackson.annotation.JsonProperty");
importMapping.put("JsonSubTypes", "com.fasterxml.jackson.annotation.JsonSubTypes");
importMapping.put("JsonTypeInfo", "com.fasterxml.jackson.annotation.JsonTypeInfo");
importMapping.put("JsonCreator", "com.fasterxml.jackson.annotation.JsonCreator");
importMapping.put("JsonValue", "com.fasterxml.jackson.annotation.JsonValue");
importMapping.put("JsonIgnore", "com.fasterxml.jackson.annotation.JsonIgnore");
importMapping.put("JsonInclude", "com.fasterxml.jackson.annotation.JsonInclude");
importMapping.put("SerializedName", "com.google.gson.annotations.SerializedName");
importMapping.put("TypeAdapter", "com.google.gson.TypeAdapter");
importMapping.put("JsonAdapter", "com.google.gson.annotations.JsonAdapter");
importMapping.put("JsonReader", "com.google.gson.stream.JsonReader");
importMapping.put("JsonWriter", "com.google.gson.stream.JsonWriter");
importMapping.put("IOException", "java.io.IOException");
importMapping.put("Objects", "java.util.Objects");
importMapping.put("StringUtil", invokerPackage + ".StringUtil");
// import JsonCreator if JsonProperty is imported
// used later in recursive import in postProcessingModels
importMapping.put("com.fasterxml.jackson.annotation.JsonProperty", "com.fasterxml.jackson.annotation.JsonCreator");
if (additionalProperties.containsKey(JAVA8_MODE)) {
setJava8Mode(Boolean.parseBoolean(additionalProperties.get(JAVA8_MODE).toString()));
if (java8Mode) {
additionalProperties.put("java8", "true");
}
}
if (additionalProperties.containsKey(SUPPORT_ASYNC)) {
setSupportAsync(Boolean.parseBoolean(additionalProperties.get(SUPPORT_ASYNC).toString()));
if (supportAsync) {
additionalProperties.put(SUPPORT_ASYNC, "true");
}
}
if (additionalProperties.containsKey(WITH_XML)) {
setWithXml(Boolean.parseBoolean(additionalProperties.get(WITH_XML).toString()));
if (withXml) {
additionalProperties.put(WITH_XML, "true");
}
}
if (additionalProperties.containsKey(DATE_LIBRARY)) {
setDateLibrary(additionalProperties.get("dateLibrary").toString());
}
if ("threetenbp".equals(dateLibrary)) {
additionalProperties.put("threetenbp", "true");
additionalProperties.put("jsr310", "true");
typeMapping.put("date", "LocalDate");
typeMapping.put("DateTime", "OffsetDateTime");
importMapping.put("LocalDate", "org.threeten.bp.LocalDate");
importMapping.put("OffsetDateTime", "org.threeten.bp.OffsetDateTime");
} else if ("joda".equals(dateLibrary)) {
additionalProperties.put("joda", "true");
typeMapping.put("date", "LocalDate");
typeMapping.put("DateTime", "DateTime");
importMapping.put("LocalDate", "org.joda.time.LocalDate");
importMapping.put("DateTime", "org.joda.time.DateTime");
} else if (dateLibrary.startsWith("java8")) {
additionalProperties.put("java8", "true");
additionalProperties.put("jsr310", "true");
typeMapping.put("date", "LocalDate");
importMapping.put("LocalDate", "java.time.LocalDate");
if ("java8-localdatetime".equals(dateLibrary)) {
typeMapping.put("DateTime", "LocalDateTime");
importMapping.put("LocalDateTime", "java.time.LocalDateTime");
} else {
typeMapping.put("DateTime", "OffsetDateTime");
importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
}
} else if (dateLibrary.equals("legacy")) {
additionalProperties.put("legacyDates", "true");
}
}
@Override
public Map postProcessAllModels(Map objs) {
objs = super.postProcessAllModels(objs);
objs = super.updateAllModels(objs);
if (!additionalModelTypeAnnotations.isEmpty()) {
for (String modelName : objs.keySet()) {
Map models = (Map) objs.get(modelName);
models.put(ADDITIONAL_MODEL_TYPE_ANNOTATIONS, additionalModelTypeAnnotations);
}
}
return objs;
}
private void sanitizeConfig() {
// Sanitize any config options here. We also have to update the additionalProperties because
// the whole additionalProperties object is injected into the main object passed to the mustache layer
this.setApiPackage(sanitizePackageName(apiPackage));
if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
this.additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
}
this.setModelPackage(sanitizePackageName(modelPackage));
if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
this.additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
}
this.setInvokerPackage(sanitizePackageName(invokerPackage));
if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
this.additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
}
}
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "_" + name;
}
@Override
public String apiFileFolder() {
return (outputFolder + File.separator + sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar);
}
@Override
public String apiTestFileFolder() {
return (outputFolder + File.separator + testFolder + File.separator + apiPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar);
}
@Override
public String modelTestFileFolder() {
return (outputFolder + File.separator + testFolder + File.separator + modelPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar);
}
@Override
public String modelFileFolder() {
return (outputFolder + File.separator + sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar);
}
@Override
public String apiDocFileFolder() {
return (outputFolder + File.separator + apiDocPath).replace('/', File.separatorChar);
}
@Override
public String modelDocFileFolder() {
return (outputFolder + File.separator + modelDocPath).replace('/', File.separatorChar);
}
@Override
public String toApiDocFilename(String name) {
return toApiName(name);
}
@Override
public String toModelDocFilename(String name) {
return toModelName(name);
}
@Override
public String toApiTestFilename(String name) {
return toApiName(name) + "Test";
}
@Override
public String toModelTestFilename(String name) {
return toModelName(name) + "Test";
}
@Override
public String toApiFilename(String name) {
return toApiName(name);
}
@Override
public String toVarName(String name) {
// sanitize name
name = sanitizeName(name, "\\W-[\\$]"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
if (name.toLowerCase(Locale.ROOT).matches("^_*class$")) {
return "propertyClass";
}
if ("_".equals(name)) {
name = "_u";
}
// numbers are not allowed at the beginning
if (name.matches("^\\d.*")) {
name = "_" + name;
}
// if it's all uppper case, do nothing
if (name.matches("^[A-Z0-9_]*$")) {
return name;
}
if (startsWithTwoUppercaseLetters(name)) {
name = name.substring(0, 2).toLowerCase(Locale.ROOT) + name.substring(2);
}
// If name contains special chars -> replace them.
if ((((CharSequence) name).chars().anyMatch(character -> specialCharReplacements.keySet().contains("" + ((char) character))))) {
List allowedCharacters = new ArrayList<>();
allowedCharacters.add("_");
allowedCharacters.add("$");
name = escape(name, specialCharReplacements, allowedCharacters, "_");
}
// camelize (lower first character) the variable name
// pet_id => petId
name = camelize(name, true);
// for reserved word or word starting with number, append _
if (isReservedWord(name) || name.matches("^\\d.*")) {
name = escapeReservedWord(name);
}
return name;
}
private boolean startsWithTwoUppercaseLetters(String name) {
boolean startsWithTwoUppercaseLetters = false;
if (name.length() > 1) {
startsWithTwoUppercaseLetters = name.substring(0, 2).equals(name.substring(0, 2).toUpperCase(Locale.ROOT));
}
return startsWithTwoUppercaseLetters;
}
@Override
public String toParamName(String name) {
// to avoid conflicts with 'callback' parameter for async call
if ("callback".equals(name)) {
return "paramCallback";
}
// should be the same as variable name
return toVarName(name);
}
@Override
public String toModelName(final String name) {
// We need to check if import-mapping has a different model for this class, so we use it
// instead of the auto-generated one.
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
final String sanitizedName = sanitizeName(name);
String nameWithPrefixSuffix = sanitizedName;
if (!StringUtils.isEmpty(modelNamePrefix)) {
// add '_' so that model name can be camelized correctly
nameWithPrefixSuffix = modelNamePrefix + "_" + nameWithPrefixSuffix;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
// add '_' so that model name can be camelized correctly
nameWithPrefixSuffix = nameWithPrefixSuffix + "_" + modelNameSuffix;
}
// camelize the model name
// phone_number => PhoneNumber
final String camelizedName = camelize(nameWithPrefixSuffix);
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(camelizedName)) {
final String modelName = "Model" + camelizedName;
LOGGER.warn(camelizedName + " (reserved word) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
// model name starts with number
if (camelizedName.matches("^\\d.*")) {
final String modelName = "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize)
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
return camelizedName;
}
@Override
public String toModelFilename(String name) {
// should be the same as the model name
return toModelName(name);
}
@Override
public String getTypeDeclaration(Schema p) {
if (ModelUtils.isArraySchema(p)) {
Schema> items = getSchemaItems((ArraySchema) p);
return getSchemaType(p) + "<" + getTypeDeclaration(ModelUtils.unaliasSchema(this.openAPI, items)) + ">";
} else if (ModelUtils.isMapSchema(p)) {
Schema> inner = getSchemaAdditionalProperties(p);
return getSchemaType(p) + "";
}
return super.getTypeDeclaration(p);
}
@Override
public String getAlias(String name) {
if (typeAliases != null && typeAliases.containsKey(name)) {
return typeAliases.get(name);
}
return name;
}
@Override
public String toDefaultValue(Schema schema) {
schema = ModelUtils.getReferencedSchema(this.openAPI, schema);
if (ModelUtils.isArraySchema(schema)) {
final String pattern;
if (fullJavaUtil) {
pattern = "new java.util.ArrayList<%s>()";
} else {
pattern = "new ArrayList<%s>()";
}
Schema> items = getSchemaItems((ArraySchema) schema);
String typeDeclaration = getTypeDeclaration(items);
Object java8obj = additionalProperties.get("java8");
if (java8obj != null) {
Boolean java8 = Boolean.valueOf(java8obj.toString());
if (java8 != null && java8) {
typeDeclaration = "";
}
}
return String.format(Locale.ROOT, pattern, typeDeclaration);
} else if (ModelUtils.isMapSchema(schema)) {
final String pattern;
if (fullJavaUtil) {
pattern = "new java.util.HashMap<%s>()";
} else {
pattern = "new HashMap<%s>()";
}
if (ModelUtils.getAdditionalProperties(schema) == null) {
return null;
}
String typeDeclaration = String.format(Locale.ROOT, "String, %s", getTypeDeclaration(ModelUtils.getAdditionalProperties(schema)));
Object java8obj = additionalProperties.get("java8");
if (java8obj != null) {
Boolean java8 = Boolean.valueOf(java8obj.toString());
if (java8 != null && java8) {
typeDeclaration = "";
}
}
return String.format(Locale.ROOT, pattern, typeDeclaration);
} else if (ModelUtils.isIntegerSchema(schema)) {
if (schema.getDefault() != null) {
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(schema.getFormat())) {
return schema.getDefault().toString() + "l";
} else {
return schema.getDefault().toString();
}
}
return null;
} else if (ModelUtils.isNumberSchema(schema)) {
if (schema.getDefault() != null) {
if (SchemaTypeUtil.FLOAT_FORMAT.equals(schema.getFormat())) {
return schema.getDefault().toString() + "f";
} else {
return schema.getDefault().toString() + "d";
}
}
return null;
} else if (ModelUtils.isBooleanSchema(schema)) {
if (schema.getDefault() != null) {
return schema.getDefault().toString();
}
return null;
} else if (ModelUtils.isURISchema(schema)) {
if (schema.getDefault() != null) {
return "URI.create(\"" + escapeText((String) schema.getDefault()) + "\")";
}
return null;
} else if (ModelUtils.isStringSchema(schema)) {
if (schema.getDefault() != null) {
String _default;
if (schema.getDefault() instanceof Date){
Date date = (Date) schema.getDefault();
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
return String.format(Locale.ROOT, localDate.toString(), "");
}
else{
_default = (String) schema.getDefault();
}
if (schema.getEnum() == null) {
return "\"" + escapeText(_default) + "\"";
} else {
// convert to enum var name later in postProcessModels
return _default;
}
}
return null;
} else if (ModelUtils.isObjectSchema(schema)) {
if (schema.getDefault() != null) {
return super.toDefaultValue(schema);
}
return null;
}
return super.toDefaultValue(schema);
}
@Override
public void setParameterExampleValue(CodegenParameter p) {
String example;
if (p.defaultValue == null) {
example = p.example;
} else {
example = p.defaultValue;
}
String type = p.baseType;
if (type == null) {
type = p.dataType;
}
if ("String".equals(type)) {
if (example == null) {
example = p.paramName + "_example";
}
example = "\"" + escapeText(example) + "\"";
} else if ("Integer".equals(type) || "Short".equals(type)) {
if (example == null) {
example = "56";
}
} else if ("Long".equals(type)) {
if (example == null) {
example = "56";
}
example = example + "L";
} else if ("Float".equals(type)) {
if (example == null) {
example = "3.4";
}
example = example + "F";
} else if ("Double".equals(type)) {
example = "3.4";
example = example + "D";
} else if ("Boolean".equals(type)) {
if (example == null) {
example = "true";
}
} else if ("File".equals(type)) {
if (example == null) {
example = "/path/to/file";
}
example = "new File(\"" + escapeText(example) + "\")";
} else if ("Date".equals(type)) {
example = "new Date()";
} else if (!languageSpecificPrimitives.contains(type)) {
// type is a model class, e.g. User
example = "new " + type + "()";
}
if (example == null) {
example = "null";
} else if (Boolean.TRUE.equals(p.isListContainer)) {
if (p.items.defaultValue != null) {
String innerExample;
if ("String".equals(p.items.dataType)) {
innerExample = "\"" + p.items.defaultValue + "\"";
} else {
innerExample = p.items.defaultValue;
}
example = "Arrays.asList(" + innerExample + ")";
} else {
example = "Arrays.asList()";
}
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
example = "new HashMap()";
}
p.example = example;
}
@Override
public String toExampleValue(Schema p) {
if (p.getExample() != null) {
return escapeText(p.getExample().toString());
} else {
return null;
}
}
@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
// don't apply renaming on types from the typeMapping
if (typeMapping.containsKey(openAPIType)) {
return typeMapping.get(openAPIType);
}
if (null == openAPIType) {
LOGGER.error("No Type defined for Schema " + p);
}
return toModelName(openAPIType);
}
@Override
public String toOperationId(String operationId) {
// throw exception if method name is empty
if (StringUtils.isEmpty(operationId)) {
throw new RuntimeException("Empty method/operation name (operationId) not allowed");
}
operationId = camelize(sanitizeName(operationId), true);
// method name cannot use reserved keyword, e.g. return
if (isReservedWord(operationId)) {
String newOperationId = 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 sname. Renamed to " + camelize("call_" + operationId), true);
operationId = camelize("call_" + operationId, true);
}
return operationId;
}
@Override
public CodegenModel fromModel(String name, Schema model) {
Map allDefinitions = ModelUtils.getSchemas(this.openAPI);
CodegenModel codegenModel = super.fromModel(name, model);
if (codegenModel.description != null) {
codegenModel.imports.add("ApiModel");
}
if (codegenModel.discriminator != null && additionalProperties.containsKey("jackson")) {
codegenModel.imports.add("JsonSubTypes");
codegenModel.imports.add("JsonTypeInfo");
}
if (allDefinitions != null && codegenModel.parentSchema != null && codegenModel.hasEnums) {
final Schema parentModel = allDefinitions.get(codegenModel.parentSchema);
final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel);
codegenModel = AbstractJavaCodegen.reconcileInlineEnums(codegenModel, parentCodegenModel);
}
if ("BigDecimal".equals(codegenModel.dataType)) {
codegenModel.imports.add("BigDecimal");
}
return codegenModel;
}
@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
if (serializeBigDecimalAsString) {
if (property.baseType.equals("BigDecimal")) {
// TODO: 5.0: Remove the camelCased vendorExtension below and ensure templates use the newer property naming.
once(LOGGER).warn("4.3.0 has deprecated the use of vendor extensions which don't follow lower-kebab casing standards with x- prefix.");
// we serialize BigDecimal as `string` to avoid precision loss
property.vendorExtensions.put("extraAnnotation", "@JsonSerialize(using = ToStringSerializer.class)"); // TODO: 5.0 Remove
property.vendorExtensions.put("x-extra-annotation", "@JsonSerialize(using = ToStringSerializer.class)");
// this requires some more imports to be added for this model...
model.imports.add("ToStringSerializer");
model.imports.add("JsonSerialize");
}
}
if (!fullJavaUtil) {
if ("array".equals(property.containerType)) {
model.imports.add("ArrayList");
} else if ("map".equals(property.containerType)) {
model.imports.add("HashMap");
}
}
if (!BooleanUtils.toBoolean(model.isEnum)) {
// needed by all pojos, but not enums
model.imports.add("ApiModelProperty");
model.imports.add("ApiModel");
}
}
@Override
public Map postProcessModels(Map objs) {
// recursively add import for mapping one type to multiple imports
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy