All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.swagger.codegen.v3.utils.ModelUtils Maven / Gradle / Ivy

There is a newer version: 3.0.62
Show newest version
package io.swagger.codegen.v3.utils;

import io.swagger.codegen.v3.CodegenConstants;
import io.swagger.codegen.v3.CodegenModel;
import io.swagger.codegen.v3.CodegenProperty;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ModelUtils {
    /**
     * Searches for the model by name in the map of models and returns it
     *
     * @param name Name of the model
     * @param models Map of models
     * @return model
     */
    public static CodegenModel getModelByName(final String name, final Map models) {
        final Object data = models.get(name);
        if (data instanceof Map) {
            final Map dataMap = (Map) data;
            final Object dataModels = dataMap.get("models");
            if (dataModels instanceof List) {
                final List dataModelsList = (List) dataModels;
                for (final Object entry : dataModelsList) {
                    if (entry instanceof Map) {
                        final Map entryMap = (Map) entry;
                        final Object model = entryMap.get("model");
                        if (model instanceof CodegenModel) {
                            return (CodegenModel) model;
                        }
                    }
                }
            }
        }
        return null;
    }

    public static Operation[] createOperationArray (PathItem pathItem) {
        return Stream.of(
                pathItem.getGet(),
                pathItem.getPost(),
                pathItem.getDelete(),
                pathItem.getHead(),
                pathItem.getPut(),
                pathItem.getPatch(),
                pathItem.getOptions()
        )
                .filter(Objects::nonNull)
                .collect(Collectors.toSet())
                .toArray(new Operation[]{});
    }

    public static void processCodegenModels(Map allModels) {
        // Fix up all parent and interface CodegenModel references.
        for (CodegenModel codegenModel : allModels.values()) {
            if (codegenModel.getParent() != null) {
                codegenModel.setParentModel(allModels.get(codegenModel.getParent()));
            }
            if (codegenModel.getInterfaces() == null || codegenModel.getInterfaces().isEmpty()) {
                continue;
            }
            codegenModel.setInterfaceModels(new ArrayList(codegenModel.getInterfaces().size()));
            for (String intf : codegenModel.getInterfaces()) {
                CodegenModel intfModel = allModels.get(intf);
                if (intfModel != null) {
                    codegenModel.getInterfaceModels().add(intfModel);
                }
            }
        }
        // Let parent know about all its children
        for (String name : allModels.keySet()) {
            CodegenModel codegenModel = allModels.get(name);
            CodegenModel parent = allModels.get(codegenModel.getParent());
            // if a discriminator exists on the parent, don't add this child to the inheritance heirarchy
            // TODO Determine what to do if the parent discriminator name == the grandparent discriminator name
            while (parent != null) {
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList());
                }
                parent.getChildren().add(codegenModel);
                if (parent.getDiscriminator() == null) {
                    parent = allModels.get(parent.parent);
                } else {
                    parent = null;
                }
            }
        }
    }

    public static void processModelEnums(Map objs) {
        List models = (List) objs.get("models");
        for (Object _mo : models) {
            Map mo = (Map) _mo;
            CodegenModel cm = (CodegenModel) mo.get("model");

            // for enum model
            boolean isEnum = false;
            if (cm.vendorExtensions.get(CodegenConstants.IS_ENUM_EXT_NAME) != null) {
                isEnum = Boolean.parseBoolean(cm.vendorExtensions.get(CodegenConstants.IS_ALIAS_EXT_NAME).toString());
            }
            if (Boolean.TRUE.equals(isEnum) && cm.allowableValues != null) {
                Map allowableValues = cm.allowableValues;
                List values = (List) allowableValues.get("values");
                List> enumVars = new ArrayList>();
                String commonPrefix = findCommonPrefixOfVars(values);
                int truncateIdx = commonPrefix.length();
                for (Object value : values) {
                    Map enumVar = new HashMap();
                    String enumName;
                    if (truncateIdx == 0) {
                        enumName = value.toString();
                    } else {
                        enumName = value.toString().substring(truncateIdx);
                        if ("".equals(enumName)) {
                            enumName = value.toString();
                        }
                    }
                    enumVar.put("name", toEnumVarName(enumName));
                    enumVar.put("value", toEnumValue(value.toString(), cm.dataType));
                    enumVars.add(enumVar);

                }
                cm.allowableValues.put("enumVars", enumVars);
            }

            // update codegen property enum with proper naming convention
            // and handling of numbers, special characters
            for (CodegenProperty var : cm.vars) {
                updateCodegenPropertyEnum(var);
            }
        }
    }

    /**
     * Returns the common prefix of variables for enum naming
     *
     * @param vars List of variable names
     * @return the common prefix for naming
     */
    public static String findCommonPrefixOfVars(List vars) {
        try {
            String[] listStr = vars.toArray(new String[vars.size()]);
            String prefix = StringUtils.getCommonPrefix(listStr);
            // exclude trailing characters that should be part of a valid variable
            // e.g. ["status-on", "status-off"] => "status-" (not "status-o")
            return prefix.replaceAll("[a-zA-Z0-9]+\\z", "");
        } catch (ArrayStoreException e) {
            return "";
        }
    }

    /**
     * Update codegen property's enum by adding "enumVars" (with name and value)
     *
     * @param var list of CodegenProperty
     */
    public static void updateCodegenPropertyEnum(CodegenProperty var) {
        Map allowableValues = var.allowableValues;

        // handle ArrayProperty
        if (var.items != null) {
            allowableValues = var.items.allowableValues;
        }

        if (allowableValues == null) {
            return;
        }

        List values = (List) allowableValues.get("values");
        if (values == null) {
            return;
        }

        // put "enumVars" map into `allowableValues", including `name` and `value`
        List> enumVars = new ArrayList>();
        String commonPrefix = findCommonPrefixOfVars(values);
        int truncateIdx = commonPrefix.length();
        for (Object value : values) {
            Map enumVar = new HashMap();
            String enumName;
            if (truncateIdx == 0) {
                enumName = value.toString();
            } else {
                enumName = value.toString().substring(truncateIdx);
                if ("".equals(enumName)) {
                    enumName = value.toString();
                }
            }
            enumVar.put("name", toEnumVarName(enumName));
            enumVar.put("value", toEnumValue(value.toString(), var.datatype));
            enumVars.add(enumVar);
        }
        allowableValues.put("enumVars", enumVars);

        // handle default value for enum, e.g. available => StatusEnum.AVAILABLE
        if (var.defaultValue != null) {
            String enumName = null;
            for (Map enumVar : enumVars) {
                if (toEnumValue(var.defaultValue, var.datatype).equals(enumVar.get("value"))) {
                    enumName = enumVar.get("name");
                    break;
                }
            }
            if (enumName != null) {
                var.defaultValue = String.format("%s.%s", var.datatypeWithEnum, enumName);
            }
        }
    }

    public static String toEnumVarName(String value) {
        if (value.length() == 0) {
            return "EMPTY";
        }
        String var = value.replaceAll("\\W+", "_").toUpperCase();
        if (var.matches("\\d.*")) {
            return "_" + var;
        } else {
            return var;
        }
    }

    private static String toEnumValue(String value, String datatype) {
        if ("number".equalsIgnoreCase(datatype)) {
            return value;
        } else {
            value = StringEscapeUtils.unescapeJava(
                    StringEscapeUtils.escapeJava(value)
                            .replace("\\/", "/"))
                    .replaceAll("[\\t\\n\\r]"," ")
                    .replace("\\", "\\\\")
                    .replace("\"", "\\\"");
            return String.format("\"%s\"", value);
        }
    }
}