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

org.openapitools.codegen.languages.PowerShellClientCodegen Maven / Gradle / Ivy

There is a newer version: 7.9.0
Show newest version
/*
 * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
 *
 * 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 io.swagger.v3.oas.models.media.Schema;
import lombok.Setter;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.*;
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.utils.ModelUtils;
import org.openapitools.codegen.utils.ProcessUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;

import static java.util.UUID.randomUUID;
import static org.openapitools.codegen.utils.StringUtils.camelize;

public class PowerShellClientCodegen extends DefaultCodegen implements CodegenConfig {
    private final Logger LOGGER = LoggerFactory.getLogger(PowerShellClientCodegen.class);
    @Setter private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}";


    @Setter protected String sourceFolder = "src";
    protected String packageName = "PSOpenAPITools";
    @Setter protected String packageVersion = "0.1.2";
    protected String apiDocPath = "docs/";
    protected String modelDocPath = "docs/";
    protected String apiTestPath = "tests/Api";
    protected String modelTestPath = "tests/Model";
    protected HashSet nullablePrimitives;
    @Setter protected String powershellGalleryUrl;
    protected HashSet powershellVerbs;
    protected Map commonVerbs; // verbs not in the official ps verb list but can be mapped to one of the verbs
    protected HashSet methodNames; // store a list of method names to detect duplicates
    @Setter protected boolean useOneOfDiscriminatorLookup = false; // use oneOf discriminator's mapping for model lookup
    @Setter protected boolean discardReadOnly = false; // Discard the readonly property in initialize cmdlet
    @Setter protected boolean skipVerbParsing = false; // Attempt to parse cmdlets from operation names
    @Setter protected String projectUri;
    @Setter protected String licenseUri;
    @Setter protected String releaseNotes;
    @Setter protected String tags;
    @Setter protected String iconUri;
    protected Set paramNameReservedWords;
    protected String modelsCmdletVerb = "Initialize";
    protected boolean useClassNameInModelsExamples = true;

    private Map schemaKeyToModelNameCache = new HashMap<>();

    /**
     * Constructs an instance of `PowerShellClientCodegen`.
     */
    public PowerShellClientCodegen() {
        super();
        modifyFeatureSet(features -> features
                .includeDocumentationFeatures(DocumentationFeature.Readme)
                .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML))
                .securityFeatures(EnumSet.of(
                        SecurityFeature.BasicAuth,
                        SecurityFeature.BearerToken,
                        SecurityFeature.ApiKey,
                        SecurityFeature.SignatureAuth
                ))
                .excludeGlobalFeatures(
                        GlobalFeature.XMLStructureDefinitions,
                        GlobalFeature.Callbacks,
                        GlobalFeature.LinkObjects,
                        GlobalFeature.ParameterStyling
                )
                .excludeSchemaSupportFeatures(
                        SchemaSupportFeature.Polymorphism
                )
                .excludeParameterFeatures(
                        ParameterFeature.Cookie
                )
        );

        generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
                .stability(Stability.BETA)
                .build();

        outputFolder = "generated-code" + File.separator + "powershell-experimental";
        modelTemplateFiles.put("model.mustache", ".ps1");
        apiTemplateFiles.put("api.mustache", ".ps1");
        modelTestTemplateFiles.put("model_test.mustache", ".ps1");
        apiTestTemplateFiles.put("api_test.mustache", ".ps1");
        modelDocTemplateFiles.put("model_doc.mustache", ".md");
        apiDocTemplateFiles.put("api_doc.mustache", ".md");
        embeddedTemplateDir = templateDir = "powershell";
        apiPackage = packageName + File.separator + "Api";
        modelPackage = packageName + File.separator + "Model";

        // https://blogs.msdn.microsoft.com/powershell/2010/01/07/how-objects-are-sent-to-and-from-remote-sessions/
        languageSpecificPrimitives = new HashSet<>(Arrays.asList(
                "Byte",
                "SByte",
                "Byte[]",
                "Int16",
                "Int32",
                "Int64",
                "UInt16",
                "UInt32",
                "UInt64",
                "Decimal",
                "Single",
                "Double",
                "TimeSpan",
                "System.DateTime",
                "ProgressRecord",
                "Char",
                "String",
                "XmlDocument",
                "SecureString",
                "Boolean",
                "Guid",
                "Uri",
                "System.IO.FileInfo",
                "Version"
        ));

        commonVerbs = new HashMap<>();

        Map> verbMappings = new HashMap<>();

        // common
        verbMappings.put("Add", Arrays.asList("Append", "Attach", "Concatenate", "Insert"));
        verbMappings.put("Clear", Arrays.asList("Flush", "Erase", "Release", "Unmark", "Unset", "Nullify"));
        verbMappings.put("Close", Arrays.asList());
        verbMappings.put("Copy", Arrays.asList("Duplicate", "Clone", "Replicate", "Sync"));
        verbMappings.put("Enter", Arrays.asList("PushInto"));
        verbMappings.put("Exit", Arrays.asList("PopOut"));
        verbMappings.put("Find", Arrays.asList());
        verbMappings.put("Format", Arrays.asList());
        verbMappings.put("Get", Arrays.asList("Read", "Open", "Cat", "Type", "Dir", "Obtain", "Dump", "Acquire", "Examine", "Find", "Search"));
        verbMappings.put("Hide", Arrays.asList("Block"));
        verbMappings.put("Join", Arrays.asList("Combine", "Unite", "Connect", "Associate"));
        verbMappings.put("Lock", Arrays.asList("RestrictSecure"));
        verbMappings.put("Move", Arrays.asList("Transfer", "Name", "Migrate"));
        verbMappings.put("New", Arrays.asList("Create", "Generate", "Build", "Make", "Allocate"));
        verbMappings.put("Open", Arrays.asList());
        verbMappings.put("Optimize", Arrays.asList());
        verbMappings.put("Pop", Arrays.asList());
        verbMappings.put("Push", Arrays.asList());
        verbMappings.put("Redo", Arrays.asList());
        verbMappings.put("Remove", Arrays.asList("Clear", "Cut", "Dispose", "Discard", "Erase"));
        verbMappings.put("Rename", Arrays.asList("Change"));
        verbMappings.put("Reset", Arrays.asList());
        verbMappings.put("Search", Arrays.asList("FindLocate"));
        verbMappings.put("Select", Arrays.asList("FindLocate"));
        verbMappings.put("Set", Arrays.asList("Write", "Reset", "Assign", "Configure"));
        verbMappings.put("Show", Arrays.asList("DisplayProduce"));
        verbMappings.put("Skip", Arrays.asList("BypassJump"));
        verbMappings.put("Split", Arrays.asList("parate"));
        verbMappings.put("Step", Arrays.asList());
        verbMappings.put("Switch", Arrays.asList());
        verbMappings.put("Undo", Arrays.asList());
        verbMappings.put("Unlock", Arrays.asList("Release", "Unrestrict", "Unsecure"));
        verbMappings.put("Watch", Arrays.asList());

        // communication
        verbMappings.put("Connect", Arrays.asList("JoinTelnet"));
        verbMappings.put("Disconnect", Arrays.asList("BreakLogoff"));
        verbMappings.put("Read", Arrays.asList("Acquire", "Prompt", "Get"));
        verbMappings.put("Receive", Arrays.asList("Read", "Accept", "Peek"));
        verbMappings.put("Send", Arrays.asList("Put", "Broadcast", "Mail", "Fax"));
        verbMappings.put("Write", Arrays.asList("PutPrint"));

        // data
        verbMappings.put("Backup", Arrays.asList(" Save", " Burn", " Replicate", "Sync"));
        verbMappings.put("Checkpoint", Arrays.asList("  Diff"));
        verbMappings.put("Compare", Arrays.asList("  Diff"));
        verbMappings.put("Compress", Arrays.asList("  Compact"));
        verbMappings.put("Convert", Arrays.asList(" Change", " Resize", "Resample"));
        verbMappings.put("ConvertFrom", Arrays.asList(" Export", " Output", "Out"));
        verbMappings.put("ConvertTo", Arrays.asList(" Import", " Input", "In"));
        verbMappings.put("Dismount", Arrays.asList(" UnmountUnlink"));
        verbMappings.put("Edit", Arrays.asList(" Change", " Update", "Modify"));
        verbMappings.put("Expand", Arrays.asList(" ExplodeUncompress"));
        verbMappings.put("Export", Arrays.asList(" ExtractBackup"));
        verbMappings.put("Group", Arrays.asList(" Aggregate", " Arrange", " Associate", "Correlate"));
        verbMappings.put("Import", Arrays.asList(" BulkLoadLoad"));
        verbMappings.put("Initialize", Arrays.asList(" Erase", " Init", " Renew", " Rebuild", " Reinitialize", "Setup"));
        verbMappings.put("Limit", Arrays.asList("  Quota"));
        verbMappings.put("Merge", Arrays.asList(" CombineJoin"));
        verbMappings.put("Mount", Arrays.asList(" Connect"));
        verbMappings.put("Out", Arrays.asList());
        verbMappings.put("Publish", Arrays.asList(" Deploy", " Release", "Install"));
        verbMappings.put("Restore", Arrays.asList(" Repair", " Return", " Undo", "Fix"));
        verbMappings.put("Save", Arrays.asList());
        verbMappings.put("Sync", Arrays.asList(" Replicate", " Coerce", "Match"));
        verbMappings.put("Unpublish", Arrays.asList(" Uninstall", " Revert", "Hide"));
        verbMappings.put("Update", Arrays.asList(" Refresh", " Renew", " Recalculate", "Re-index"));

        // diagnostic
        verbMappings.put("Debug", Arrays.asList("Diagnose"));
        verbMappings.put("Measure", Arrays.asList("Calculate", "Determine", "Analyze"));
        verbMappings.put("Ping", Arrays.asList());
        verbMappings.put("Repair", Arrays.asList("FixRestore"));
        verbMappings.put("Resolve", Arrays.asList("ExpandDetermine"));
        verbMappings.put("Test", Arrays.asList("Diagnose", "Analyze", "Salvage", "Verify"));
        verbMappings.put("Trace", Arrays.asList("Track", "Follow", "Inspect", "Dig"));

        // lifecycle
        verbMappings.put("Approve", Arrays.asList());
        verbMappings.put("Assert", Arrays.asList("Certify"));
        verbMappings.put("Build", Arrays.asList());
        verbMappings.put("Complete", Arrays.asList());
        verbMappings.put("Confirm", Arrays.asList("Acknowledge", "Agree", "Certify", "Validate", "Verify"));
        verbMappings.put("Deny", Arrays.asList("Block", "Object", "Refuse", "Reject"));
        verbMappings.put("Deploy", Arrays.asList());
        verbMappings.put("Disable", Arrays.asList("HaltHide"));
        verbMappings.put("Enable", Arrays.asList("StartBegin"));
        verbMappings.put("Install", Arrays.asList("Setup"));
        verbMappings.put("Invoke", Arrays.asList("RunStart"));
        verbMappings.put("Register", Arrays.asList());
        verbMappings.put("Request", Arrays.asList());
        verbMappings.put("Restart", Arrays.asList("Recycle"));
        verbMappings.put("Resume", Arrays.asList());
        verbMappings.put("Start", Arrays.asList("Launch", "Initiate", "Boot"));
        verbMappings.put("Stop", Arrays.asList("End", "Kill", "Terminate", "Cancel"));
        verbMappings.put("Submit", Arrays.asList("Post"));
        verbMappings.put("Suspend", Arrays.asList("Pause"));
        verbMappings.put("Uninstall", Arrays.asList());
        verbMappings.put("Unregister", Arrays.asList("Remove"));
        verbMappings.put("Wait", Arrays.asList("SleepPause"));

        // security
        verbMappings.put("Block", Arrays.asList("Prevent", "Limit", "Deny"));
        verbMappings.put("Grant", Arrays.asList("AllowEnable"));
        verbMappings.put("Protect", Arrays.asList("Encrypt", "Safeguard", "Seal"));
        verbMappings.put("Revoke", Arrays.asList("RemoveDisable"));
        verbMappings.put("Unblock", Arrays.asList("ClearAllow"));
        verbMappings.put("Unprotect", Arrays.asList("DecryptUnseal"));

        // other
        verbMappings.put("Use", Arrays.asList());

        for (Map.Entry> entry : verbMappings.entrySet()) {
            // loop through each verb in the list
            for (String verb : entry.getValue()) {
                if (verbMappings.containsKey(verb)) {
                    // the verb to be mapped is also a common verb, do nothing
                    LOGGER.debug("verbmapping: skipped {}", verb);
                } else {
                    commonVerbs.put(verb, entry.getKey());
                    LOGGER.debug("verbmapping: adding {} => {}", verb, entry.getKey());
                }
            }
        }

        powershellVerbs = new HashSet<>(Arrays.asList(
                "Add",
                "Clear",
                "Close",
                "Copy",
                "Enter",
                "Exit",
                "Find",
                "Format",
                "Get",
                "Hide",
                "Join",
                "Lock",
                "Move",
                "New",
                "Open",
                "Optimize",
                "Pop",
                "Push",
                "Redo",
                "Remove",
                "Rename",
                "Reset",
                "Search",
                "Select",
                "Set",
                "Show",
                "Skip",
                "Split",
                "Step",
                "Switch",
                "Undo",
                "Unlock",
                "Watch",
                "Connect",
                "Disconnect",
                "Read",
                "Receive",
                "Send",
                "Write",
                "Backup",
                "Checkpoint",
                "Compare",
                "Compress",
                "Convert",
                "ConvertFrom",
                "ConvertTo",
                "Dismount",
                "Edit",
                "Expand",
                "Export",
                "Group",
                "Import",
                "Initialize",
                "Limit",
                "Merge",
                "Mount",
                "Out",
                "Publish",
                "Restore",
                "Save",
                "Sync",
                "Unpublish",
                "Update",
                "Debug",
                "Measure",
                "Ping",
                "Repair",
                "Resolve",
                "Test",
                "Trace",
                "Approve",
                "Assert",
                "Build",
                "Complete",
                "Confirm",
                "Deny",
                "Deploy",
                "Disable",
                "Enable",
                "Install",
                "Invoke",
                "Register",
                "Request",
                "Restart",
                "Resume",
                "Start",
                "Stop",
                "Submit",
                "Suspend",
                "Uninstall",
                "Unregister",
                "Wait",
                "Block",
                "Grant",
                "Protect",
                "Revoke",
                "Unblock",
                "Unprotect",
                "Use"
        ));

        methodNames = new HashSet<>();

        nullablePrimitives = new HashSet<>(Arrays.asList(
                "System.Nullable[Byte]",
                "System.Nullable[SByte]",
                "System.Nullable[Int16]",
                "System.Nullable[Int32]",
                "System.Nullable[Int64]",
                "System.Nullable[UInt16]",
                "System.Nullable[UInt32]",
                "System.Nullable[UInt64]",
                "System.Nullable[Decimal]",
                "System.Nullable[Single]",
                "System.Nullable[Double]",
                "System.Nullable[Boolean]"
        ));

        // list of reserved words - must be in lower case
        reservedWords = new HashSet<>(Arrays.asList(
                // https://richardspowershellblog.wordpress.com/2009/05/02/powershell-reserved-words/
                "begin",
                "break",
                "catch",
                "continue",
                "data",
                "do",
                "dynamicparam",
                "else",
                "elseif",
                "end",
                "exit",
                "filter",
                "finally",
                "for",
                "foreach",
                "from",
                "function",
                "if",
                "in",
                "param",
                "process",
                "return",
                "switch",
                "throw",
                "trap",
                "try",
                "until",
                "while",
                "local",
                "private",
                "where",
                // special variables
                "args",
                "consolefilename",
                "error",
                "event",
                "eventargs",
                "eventsubscriber",
                "executioncontext",
                "false",
                "foreach",
                "home",
                "host",
                "input",
                "lastexitcode",
                "matches",
                "myinvocation",
                "nestedpromptlevel",
                "null",
                "pid",
                "profile",
                "pscmdlet",
                "pscommandpath",
                "psculture",
                "psdebugcontext",
                "pshome",
                "psitem",
                "psscriptroot",
                "pssenderinfo",
                "psuiculture",
                "psversiontable",
                "sender",
                "shellid",
                "stacktrace",
                "this",
                "true"
        ));

        paramNameReservedWords = new HashSet<>(Arrays.asList(
                "args",
                "error",
                "executioncontext",
                "false",
                "home",
                "host",
                "input",
                "myinvocation",
                "nestedpromptlevel",
                "null",
                "pid",
                "profile",
                "pscommandpath",
                "psculture",
                "pshome",
                "psscriptroot",
                "psuiculture",
                "psversiontable",
                "shellid",
                "stacktrace",
                "true"
        ));

        defaultIncludes = new HashSet<>(Arrays.asList(
                "Byte",
                "SByte",
                "Byte[]",
                "Int16",
                "Int32",
                "Int64",
                "UInt16",
                "UInt32",
                "UInt64",
                "Decimal",
                "Single",
                "Double",
                "TimeSpan",
                "System.DateTime",
                "ProgressRecord",
                "Char",
                "String",
                "XmlDocument",
                "SecureString",
                "Boolean",
                "Guid",
                "Uri",
                "System.IO.FileInfo",
                "Version"
        ));

        typeMapping = new HashMap<>();
        typeMapping.put("string", "String");
        typeMapping.put("boolean", "Boolean");
        typeMapping.put("integer", "Int32");
        typeMapping.put("float", "Double");
        typeMapping.put("long", "Int64");
        typeMapping.put("double", "Double");
        typeMapping.put("number", "Decimal");
        typeMapping.put("decimal", "Decimal");
        typeMapping.put("object", "System.Collections.Hashtable");
        typeMapping.put("file", "System.IO.FileInfo");
        typeMapping.put("ByteArray", "System.Byte[]");
        typeMapping.put("binary", "System.IO.FileInfo");
        typeMapping.put("date", "System.DateTime");
        typeMapping.put("date-time", "System.DateTime");
        typeMapping.put("Date", "System.DateTime");
        typeMapping.put("DateTime", "System.DateTime");
        typeMapping.put("UUID", "String");
        typeMapping.put("URI", "String");

        cliOptions.clear();
        cliOptions.add(new CliOption("powershellGalleryUrl", "URL to the module in PowerShell Gallery (e.g. https://www.powershellgallery.com/packages/PSTwitter/)."));
        cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. PSTwitter).").defaultValue(this.packageName));
        cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Package version (e.g. 0.1.2).").defaultValue(this.packageVersion));
        cliOptions.add(new CliOption(CodegenConstants.OPTIONAL_PROJECT_GUID, "GUID for PowerShell module (e.g. a27b908d-2a20-467f-bc32-af6f3a654ac5). A random GUID will be generated by default."));
        cliOptions.add(new CliOption(CodegenConstants.API_NAME_PREFIX, "Prefix that will be appended to all PS objects. Default: empty string. e.g. Pet => PSPet."));
        cliOptions.add(new CliOption("commonVerbs", "PS common verb mappings. e.g. Delete=Remove:Patch=Update to map Delete with Remove and Patch with Update accordingly."));
        cliOptions.add(new CliOption(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP_DESC));
        cliOptions.add(new CliOption("discardReadOnly", "Set discardReadonly to true to generate the Initialize cmdlet without readonly parameters"));
        cliOptions.add(new CliOption("tags","Tags applied to the generated PowerShell module. These help with module discovery in online galleries"));
        cliOptions.add(new CliOption("projectUri","A URL to the main website for this project"));
        cliOptions.add(new CliOption("licenseUri","A URL to the license for the generated PowerShell module"));
        cliOptions.add(new CliOption("iconUri","A URL to an icon representing the generated PowerShell module"));
        cliOptions.add(new CliOption("releaseNotes","Release notes of the generated PowerShell module"));
        cliOptions.add(new CliOption("skipVerbParsing", "Set skipVerbParsing to not try get powershell verbs of operation names"));
        cliOptions.add(new CliOption("modelsCmdletVerb", "Verb to be used when generating the Models cmdlets in the examples.").defaultValue(this.modelsCmdletVerb));

        CliOption useClassNameInModelsExamplesOpt = CliOption.newBoolean(
            "useClassNameInModelsExamples",
            "Use classname instead of name when generating the Models cmdlets in the examples."
        ).defaultValue(this.useClassNameInModelsExamples ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
        cliOptions.add(useClassNameInModelsExamplesOpt);

        // option to change how we process + set the data in the 'additionalProperties' keyword.
        CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean(
                CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
                CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString());
        Map disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>();
        disallowAdditionalPropertiesIfNotPresentOpts.put("false",
                "The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
        disallowAdditionalPropertiesIfNotPresentOpts.put("true",
                "Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.");
        disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts);
        cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt);
        this.setDisallowAdditionalPropertiesIfNotPresent(true);

        // default value in the template
        additionalProperties.put("powershellVersion", "6.2"); // minimal PS version
        additionalProperties.put("author", "OpenAPI Generator Team");
        additionalProperties.put("companyName", "openapitools.org");
        additionalProperties.put("psData", null);
    }

    @Override
    public CodegenType getTag() {
        return CodegenType.CLIENT;
    }

    @Override
    public String getName() {
        return "powershell";
    }

    @Override
    public String getHelp() {
        return "Generates a PowerShell API client (beta)";
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
        this.apiPackage = packageName + File.separator + "Api";
        this.modelPackage = packageName + File.separator + "Model";
    }

    public boolean getUseOneOfDiscriminatorLookup() {
        return this.useOneOfDiscriminatorLookup;
    }

    public boolean getDiscardReadOnly() {
        return this.discardReadOnly;
    }

   public void SetModelsCmdletVerb(String modelsCmdletVerb) { this.modelsCmdletVerb = modelsCmdletVerb; }

   public void SetUseClassNameInModelsExamples(boolean useClassNameInModelsExamples) { this.useClassNameInModelsExamples = useClassNameInModelsExamples; }

    @Override
    public void processOpts() {
        this.setLegacyDiscriminatorBehavior(false);
        super.processOpts();

        if (StringUtils.isEmpty(System.getenv("POWERSHELL_POST_PROCESS_FILE"))) {
            LOGGER.info("Environment variable POWERSHELL_POST_PROCESS_FILE not defined so the PowerShell code may not be properly formatted. To define it, try 'export POWERSHELL_POST_PROCESS_FILE=\"Edit-DTWBeautifyScript\"'");
            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 'POWERSHELL_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).");
        }

        if (additionalProperties.containsKey("powershellGalleryUrl")) {
            setPowershellGalleryUrl((String) additionalProperties.get("powershellGalleryUrl"));
        } else {
            additionalProperties.put("powershellGalleryUrl", powershellGalleryUrl);
        }

        if (additionalProperties.containsKey(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP)) {
            setUseOneOfDiscriminatorLookup(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP));
        } else {
            additionalProperties.put(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, useOneOfDiscriminatorLookup);
        }

        if (additionalProperties.containsKey("discardReadOnly")) {
            setDiscardReadOnly(convertPropertyToBooleanAndWriteBack("discardReadOnly"));
        } else {
            additionalProperties.put("discardReadOnly", discardReadOnly);
        }

        if (additionalProperties.containsKey("skipVerbParsing")) {
            setSkipVerbParsing(convertPropertyToBoolean("skipVerbParsing"));
        } else {
            additionalProperties.put("skipVerbParsing", skipVerbParsing);
        }

        if (additionalProperties.containsKey("tags")) {
            String[] entries = ((String) additionalProperties.get("tags")).split(",");
            String prefix = "";
            StringBuilder tagStr =  new StringBuilder("@(");
            for (String entry : entries) {
                tagStr.append(prefix);
                prefix = ",";
                tagStr.append(String.format(Locale.ROOT, "'%s' ",entry));
            }
            tagStr.append(")");
            setTags(tagStr.toString());
            additionalProperties.put("tags",tagStr.toString());
        }

        if (additionalProperties.containsKey("releaseNotes")) {
            setReleaseNotes((String) additionalProperties.get("releaseNotes"));
        } else {
            additionalProperties.put("releaseNotes", releaseNote);
        }

        if (additionalProperties.containsKey("licenseUri")) {
            setLicenseUri((String) additionalProperties.get("licenseUri"));
        } else {
            additionalProperties.put("licenseUri", licenseUri);
        }

        if (additionalProperties.containsKey("projectUri")) {
            setProjectUri((String) additionalProperties.get("projectUri"));
        } else {
            additionalProperties.put("projectUri", tags);
        }

        if (additionalProperties.containsKey("iconUri")) {
            setIconUri((String) additionalProperties.get("iconUri"));
        } else {
            additionalProperties.put("iconUri", iconUri);
        }

        if (additionalProperties.containsKey("modelsCmdletVerb")) {
            this.SetModelsCmdletVerb((String) additionalProperties.get("modelsCmdletVerb"));
        } else {
            additionalProperties.put("modelsCmdletVerb", this.modelsCmdletVerb);
        }

        if (additionalProperties.containsKey("useClassNameInModelsExamples")) {
            this.SetUseClassNameInModelsExamples(convertPropertyToBoolean("useClassNameInModelsExamples"));
        } else {
            additionalProperties.put("useClassNameInModelsExamples", this.useClassNameInModelsExamples);
        }

        if (StringUtils.isNotBlank(powershellGalleryUrl)) {
            // get the last segment of the URL
            // e.g. https://www.powershellgallery.com/packages/PSTwitter => PSTwitter
            additionalProperties.put("powershellGalleryId", powershellGalleryUrl.replaceFirst(".*/([^/?]+).*", "$1"));
        }

        if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) {
            setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID));
        } else {
            additionalProperties.put("packageGuid", packageGuid);
        }

        if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
            this.setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
        } else {
            additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
        }

        if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
            this.setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
        } else {
            additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
        }

        if (additionalProperties.containsKey("commonVerbs")) {
            String[] entries = ((String) additionalProperties.get("commonVerbs")).split(":");
            for (String entry : entries) {
                String[] pair = entry.split("=");
                if (pair.length == 2) {
                    commonVerbs.put(pair[0], pair[1]);
                    LOGGER.debug("Add commonVerbs: {} => {}", pair[0], pair[1]);
                } else {
                    LOGGER.error("Failed to parse commonVerbs: {}", entry);
                }
            }
        }

        if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
            LOGGER.warn(
                    "{} with {} generator is ignored. Setting this value independently of {} is not currently supported.",
                    CodegenConstants.MODEL_PACKAGE, this.getName(), CodegenConstants.PACKAGE_NAME);
        }

        if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
            LOGGER.warn(
                    "{} with {} generator is ignored. Setting this value independently of {} is not currently supported.",
                    CodegenConstants.API_PACKAGE, this.getName(), CodegenConstants.PACKAGE_NAME);
        }

        if (additionalProperties.containsKey(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)) {
            this.setDisallowAdditionalPropertiesIfNotPresent(Boolean.parseBoolean(additionalProperties
                    .get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString()));
        }

        additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage());
        additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage());

        additionalProperties.put("apiDocPath", apiDocPath);
        additionalProperties.put("modelDocPath", modelDocPath);

        supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
        supportingFiles.add(new SupportingFile("Build.ps1.mustache", "", "Build.ps1"));

        final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator);

        supportingFiles.add(new SupportingFile("Org.OpenAPITools.psm1.mustache", infrastructureFolder, packageName + ".psm1"));

        // client/configuration
        supportingFiles.add(new SupportingFile("configuration.mustache", infrastructureFolder + "Client", apiNamePrefix + "Configuration.ps1"));

        // private
        supportingFiles.add(new SupportingFile("api_client.mustache", infrastructureFolder + "Private", apiNamePrefix + "ApiClient.ps1"));
        supportingFiles.add(new SupportingFile("Get-CommonParameters.mustache", infrastructureFolder + File.separator + "Private" + File.separator, "Get-CommonParameters.ps1"));
        supportingFiles.add(new SupportingFile("Out-DebugParameter.mustache", infrastructureFolder + File.separator + "Private" + File.separator, "Out-DebugParameter.ps1"));
        supportingFiles.add(new SupportingFile("http_signature_auth.mustache", infrastructureFolder + "Private", apiNamePrefix + "HttpSignatureAuth.ps1"));
        supportingFiles.add(new SupportingFile("rsa_provider.mustache", infrastructureFolder + "Private", apiNamePrefix + "RSAEncryptionProvider.cs"));


        // en-US
        supportingFiles.add(new SupportingFile("about_Org.OpenAPITools.help.txt.mustache", infrastructureFolder + File.separator + "en-US" + File.separator + "about_" + packageName + ".help.txt"));

        // appveyor
        supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml"));
    }

    @SuppressWarnings("static-method")
    @Override
    public String escapeText(String input) {

        if (input == null) {
            return input;
        }

        // remove \t, \n, \r
        // replace \ with \\
        // replace " with \"
        // outer unescape to retain the original multi-byte characters
        // finally escalate characters avoiding code injection
        return escapeUnsafeCharacters(
                StringEscapeUtils.unescapeJava(
                        StringEscapeUtils.escapeJava(input)
                                .replace("\\/", "/"))
                        .replaceAll("[\\t\\n\\r]", " ")
                        .replace("\"", "\"\""));

    }

    @Override
    public String escapeUnsafeCharacters(String input) {
        return input.replace("#>", "#_>").replace("<#", "<_#");
    }

    @Override
    public String escapeQuotationMark(String input) {
        // remove " to avoid code injection
        return input.replace("\"", "");
    }

    @Override
    public String toApiTestFilename(String name) {
        return toApiFilename(name) + ".Tests";
    }

    @Override
    public String toModelTestFilename(String name) {
        return toModelFilename(name) + ".Tests";
    }

    @Override
    public String apiTestFileFolder() {
        return (outputFolder + "/" + apiTestPath).replace('/', File.separatorChar);
    }

    @Override
    public String apiDocFileFolder() {
        return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
    }

    @Override
    public String apiFileFolder() {
        return outputFolder + File.separator + sourceFolder + File.separator + apiPackage();
    }

    @Override
    public String modelTestFileFolder() {
        return (outputFolder + "/" + modelTestPath).replace('/', File.separatorChar);
    }

    @Override
    public String modelDocFileFolder() {
        return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar);
    }


    @Override
    public String modelFileFolder() {
        return outputFolder + File.separator + sourceFolder + File.separator + modelPackage();
    }

    @Override
    public String escapeReservedWord(String name) {
        return "Var" + name;
    }

    /**
     * Output the proper model name (capitalized).
     * In case the name belongs to the TypeSystem it won't be renamed.
     *
     * @param name the name of the model
     * @return capitalized model name
     */
    @Override
    public String toModelName(String name) {
        // obtain the name from modelNameMapping directly if provided
        if (modelNameMapping.containsKey(name)) {
            return modelNameMapping.get(name);
        }

        // check if schema-mapping has a different model for this class, so we can use it
        // instead of the auto-generated one.
        if (schemaMapping.containsKey(name)) {
            return schemaMapping.get(name);
        }

        // memoization
        String origName = name;
        if (schemaKeyToModelNameCache.containsKey(origName)) {
            return schemaKeyToModelNameCache.get(origName);
        }

        if (!StringUtils.isEmpty(modelNamePrefix)) {
            name = modelNamePrefix + "_" + name;
        }

        if (!StringUtils.isEmpty(modelNameSuffix)) {
            name = name + "_" + modelNameSuffix;
        }

        // camelize the model name
        // phone_number => PhoneNumber
        name = camelize(sanitizeName(name));

        // model name cannot use reserved keyword, e.g. return
        if (isReservedWord(name)) {
            LOGGER.warn("{} (reserved word or special variable name) cannot be used as model name. Renamed to {}",
                    name, camelize("model_" + name));
            name = camelize("model_" + name); // e.g. return => ModelReturn (after camelize)
        }

        // model name starts with number
        if (name.matches("^\\d.*")) {
            LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name,
                    camelize("model_" + name));
            name = camelize("model_" + name); // e.g. 200Response => Model200Response (after camelize)
        }

        schemaKeyToModelNameCache.put(origName, name);

        return name;
    }

    @Override
    public String toModelFilename(String name) {
        // should be the same as the model name
        return toModelName(name);
    }

    /**
     * returns the OpenAPI type for the property
     *
     * @param p OpenAPI property object
     * @return string presentation of the type
     **/
    @Override
    public String getSchemaType(Schema p) {
        String openAPIType = super.getSchemaType(p);
        String type;

        // This maps, for example, long -> Long based on hashes in this type's constructor
        if (typeMapping.containsKey(openAPIType)) {
            type = typeMapping.get(openAPIType);
            if (languageSpecificPrimitives.contains(type)) {
                return type;
            }
        } else {
            type = openAPIType;
        }

        // model/object
        return toModelName(type);
    }

    /**
     * Output the type declaration of the property
     *
     * @param p OpenAPI Schema object
     * @return a string presentation of the property type
     */
    @Override
    public String getTypeDeclaration(Schema p) {
        if (ModelUtils.isArraySchema(p)) {
            Schema inner = ModelUtils.getSchemaItems(p);
            return getTypeDeclaration(inner) + "[]";
        } else if (ModelUtils.isMapSchema(p)) {
            return "System.Collections.Hashtable";
        } else if (!languageSpecificPrimitives.contains(getSchemaType(p))) {
            return super.getTypeDeclaration(p);
        }
        return super.getTypeDeclaration(p);
    }

    @Override
    public String toOperationId(String operationId) {
        // throw exception if method name is empty (should not occur as an auto-generated method name will be used)
        if (StringUtils.isEmpty(operationId)) {
            throw new RuntimeException("Empty method name (operationId) not allowed");
        }

        return sanitizeName(operationId);
    }

    @Override
    public String toParamName(String name) {
        // obtain the name from parameterNameMapping directly if provided
        if (parameterNameMapping.containsKey(name)) {
            return parameterNameMapping.get(name);
        }

        // sanitize and camelize parameter name
        // pet_id => PetId
        name = camelize(sanitizeName(name));

        // for param name reserved word or word starting with number, append _
        if (paramNameReservedWords.contains(name) || name.matches("^\\d.*")) {
            LOGGER.warn("{} (reserved word or special variable name) cannot be used in naming. Renamed to {}", name,
                    escapeReservedWord(name));
            name = escapeReservedWord(name);
        }

        return name;
    }

    @Override
    public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) {
        OperationMap operations = objs.getOperations();
        HashMap modelMaps = ModelMap.toCodegenModelMap(allModels);
        HashMap processedModelMaps = new HashMap<>();

        List operationList = operations.getOperation();
        for (CodegenOperation op : operationList) {
            int index = 0;
            for (CodegenParameter p : op.allParams) {
                p.vendorExtensions.put("x-powershell-data-type", getPSDataType(p));
                p.vendorExtensions.put("x-powershell-example", constructExampleCode(p, modelMaps, processedModelMaps, false));

                if (p.required) {
                    // clear processed Models after each constructed API Operation full example.
                    processedModelMaps.clear();

                    p.vendorExtensions.put("x-powershell-example-required", constructExampleCode(p, modelMaps, processedModelMaps, true));
                }

                p.vendorExtensions.put("x-index", index);
                index++;
            }

            // clear processed Models after each constructed API Operation examples.
            processedModelMaps.clear();

            if (!op.vendorExtensions.containsKey("x-powershell-method-name")) { // x-powershell-method-name not set
                String methodName = toMethodName(op.operationId);
                op.vendorExtensions.put("x-powershell-method-name", methodName);
                op.vendorExtensions.put("x-powershell-method-name-lowercase", methodName);
            } else {
                op.vendorExtensions.put("x-powershell-method-name-lowercase", ((String) op.vendorExtensions.get("x-powershell-method-name")).toLowerCase(Locale.ROOT));
            }

            // detect duplicated method name
            if (methodNames.contains(op.vendorExtensions.get("x-powershell-method-name"))) {
                LOGGER.error("Duplicated method name found: {}", op.vendorExtensions.get("x-powershell-method-name"));
            } else {
                methodNames.add(op.vendorExtensions.get("x-powershell-method-name"));
            }

            if (op.produces != null && op.produces.size() > 1) {
                op.vendorExtensions.put("x-powershell-select-accept", true);
            }
        }

        // check if return type is oneOf/anyOf model
        for (CodegenOperation op : operationList) {
            if (op.returnType != null) {
                // look up the model to see if it's anyOf/oneOf
                if (modelMaps.containsKey(op.returnType) && modelMaps.get(op.returnType) != null) {
                    CodegenModel cm = modelMaps.get(op.returnType);

                    if (cm.oneOf != null && !cm.oneOf.isEmpty()) {
                        op.vendorExtensions.put("x-ps-return-type-one-of", true);
                    }

                    if (cm.anyOf != null && !cm.anyOf.isEmpty()) {
                        op.vendorExtensions.put("x-ps-return-type-any-of", true);
                    }
                } else {
                    //LOGGER.error("cannot lookup model " + op.returnType);
                }
            }
        }

        return objs;
    }

    @Override
    public ModelsMap postProcessModels(ModelsMap objs) {
        List models = objs.getModels();
        // add x-index to properties
        ProcessUtils.addIndexToProperties(models);

        // add x-data-type to store powershell type
        for (ModelMap _model : models) {
            CodegenModel model = _model.getModel();
            CodegenProperty lastWritableProperty = null;

            for (CodegenProperty cp : model.allVars) {
                cp.vendorExtensions.put("x-powershell-data-type", getPSDataType(cp));
                if (this.discardReadOnly && !cp.isReadOnly) {
                    lastWritableProperty = cp;
                }
            }

            // Mark the last readonly false property
            if (this.discardReadOnly && lastWritableProperty != null) {
                lastWritableProperty.vendorExtensions.put("x-powershell-last-writable", true);
                model.allVars.set(model.allVars.indexOf(lastWritableProperty), lastWritableProperty);
            }

            // if oneOf contains "null" type
            if (model.oneOf != null && !model.oneOf.isEmpty() && model.oneOf.contains("ModelNull")) {
                model.isNullable = true;
                model.oneOf.remove("ModelNull");
            }

            // if anyOf contains "null" type
            if (model.anyOf != null && !model.anyOf.isEmpty() && model.anyOf.contains("ModelNull")) {
                model.isNullable = true;
                model.anyOf.remove("ModelNull");
            }
        }

        // process enum in models
        return postProcessModelsEnum(objs);
    }

    @Override
    public String toVarName(String name) {
        // obtain the name from nameMapping directly if provided
        if (nameMapping.containsKey(name)) {
            return nameMapping.get(name);
        }

        // sanitize name
        name = sanitizeName(name);

        // camelize the variable name
        // pet_id => PetId
        name = camelize(name);

        // for reserved word or word starting with number, append _
        if (isReservedWord(name) || name.matches("^\\d.*")) {
            LOGGER.warn("{} (reserved word or special variable name) cannot be used in naming. Renamed to {}", name,
                    escapeReservedWord(name));
            name = escapeReservedWord(name);
        }

        return name;
    }

    private String constructExampleCode(CodegenParameter codegenParameter, HashMap modelMaps, HashMap processedModelMap, boolean requiredOnly) {
        StringBuilder example = new StringBuilder();

        if (codegenParameter.isString) {
            if (codegenParameter.isEnum || (codegenParameter.allowableValues != null && !codegenParameter.allowableValues.isEmpty())) {
                example.append(constructEnumExample(codegenParameter.allowableValues));
            } else {
                String genericStringExample = codegenParameter.paramName + "_example";
                example.append(constructStringExample(codegenParameter.paramName, codegenParameter.example, genericStringExample));
            }
        } else if (codegenParameter.isBoolean) {
            example.append(constructBooleanExample(codegenParameter.example));
        } else if (codegenParameter.isDate || codegenParameter.isDateTime) {
            example.append("(Get-Date)");
        } else if (codegenParameter.isArray) {
            if (codegenParameter.items.isModel || (modelMaps.containsKey(codegenParameter.items.dataType) && codegenParameter.items.allowableValues == null)) {
                String modelExample;
                if (codegenParameter.items.isModel) {
                    modelExample = constructExampleCode(codegenParameter.items, modelMaps, processedModelMap, requiredOnly);
                } else {
                    modelExample = constructExampleCode(modelMaps.get(codegenParameter.items.dataType), modelMaps, processedModelMap, requiredOnly);
                }

                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample);
                }
            } else if (codegenParameter.items.isString) {
                if (codegenParameter.items.isEnum || (codegenParameter.items.allowableValues != null && !codegenParameter.items.allowableValues.isEmpty())) {
                    example.append(constructEnumExample(codegenParameter.items.allowableValues));
                } else {
                    String genericStringExample = codegenParameter.items.name + "_example";
                    example.append(constructStringExample(codegenParameter.paramName, codegenParameter.items.example, genericStringExample));
                }
            } else {
                example.append(constructExampleCode(codegenParameter.items, modelMaps, processedModelMap, requiredOnly));
            }
        } else if (codegenParameter.isMap) {
            if (codegenParameter.items == null) {
                example.append("@{ key_example = ... }");
            } else if (codegenParameter.items.isModel) {
                String modelExample = constructExampleCode(codegenParameter.items, modelMaps, processedModelMap, requiredOnly);
                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample).append("\n");
                }

                example.append("$").append(codegenParameter.paramName).append(" = @{ key_example = $").append(codegenParameter.items.dataType).append(" }");
            } else {
                example.append("@{ key_example = ");
                example.append(constructExampleCode(codegenParameter.items, modelMaps, processedModelMap, requiredOnly));
                example.append(" }");
            }
        } else if (codegenParameter.isEnum || (codegenParameter.allowableValues != null && !codegenParameter.allowableValues.isEmpty())) {
            example.append(constructEnumExample(codegenParameter.allowableValues));
        } else if (codegenParameter.isModel) {
            if (modelMaps.containsKey(codegenParameter.dataType)) {
                String modelExample = constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap, requiredOnly);
                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample);
                }
            }
        } else if ((languageSpecificPrimitives.contains(codegenParameter.dataType) || nullablePrimitives.contains(codegenParameter.dataType)) && !codegenParameter.isFile) {
            // If the data type is primitive and it is not a String, Enum, Boolean, File, Date or DateTime, then it's a number.
            example.append(constructNumericExample(codegenParameter.example));
        }

        // Replace multiple new lines with a single new line and trim leading and trailing spaces.
        return example.toString().replaceAll("[\n]{2,}", "\n\n").trim();
    }

    private String constructExampleCode(CodegenProperty codegenProperty, HashMap modelMaps, HashMap processedModelMap, boolean requiredOnly) {
        StringBuilder example = new StringBuilder();

        if (codegenProperty.isString) {
            if (codegenProperty.isEnum || (codegenProperty.allowableValues != null && !codegenProperty.allowableValues.isEmpty())) {
                example.append(constructEnumExample(codegenProperty.allowableValues));
            } else {
                String genericStringExample = codegenProperty.name + "_example";
                example.append(constructStringExample(codegenProperty.name, codegenProperty.example, genericStringExample));
            }
        } else if (codegenProperty.isBoolean) {
            example.append(constructBooleanExample(codegenProperty.example));
        } else if (codegenProperty.isDate || codegenProperty.isDateTime) {
            example.append("(Get-Date)");
        } else if (codegenProperty.isArray) {
            example.append(constructExampleCode(codegenProperty.items, modelMaps, processedModelMap, requiredOnly));
        } else if (codegenProperty.isMap) {
            example.append("@{ key_example = ");
            if (codegenProperty.items != null) {
                example.append(constructExampleCode(codegenProperty.items, modelMaps, processedModelMap, requiredOnly));
            } else {
                example.append(" ... ");
            }
            example.append(" }");
        } else if (codegenProperty.isEnum || (codegenProperty.allowableValues != null && !codegenProperty.allowableValues.isEmpty())) {
            example.append(constructEnumExample(codegenProperty.allowableValues));
        } else if (codegenProperty.isModel) {
            if (modelMaps.containsKey(codegenProperty.dataType)) {
                String modelExample = constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap, requiredOnly);
                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample);
                }
            }
        } else if ((languageSpecificPrimitives.contains(codegenProperty.dataType) || nullablePrimitives.contains(codegenProperty.dataType)) && !codegenProperty.isFile) {
            // If the data type is primitive and it is not a String, Enum, Boolean, File, Date or DateTime, then it's a number.
            example.append(constructNumericExample(codegenProperty.example));
        }

        return example.toString();
    }

    private String constructExampleCode(CodegenModel codegenModel, HashMap modelMaps, HashMap processedModelMap, boolean requiredOnly) {
        StringBuilder example = new StringBuilder();
        Boolean hasModelProperty = false;

        // This behaviour is needed to break infinite recursion. Return, in case a model is already processed in the current context.
        String model = codegenModel.name;
        if (processedModelMap.containsKey(model)) {
            return "";
        } else {
            processedModelMap.put(model, 1);
        }

        List propertyExamples = new ArrayList<>();
        for (CodegenProperty codegenProperty : codegenModel.allVars) {
            if (
                !hasModelProperty && (
                codegenProperty.isModel ||
                (codegenProperty.isArray && (codegenProperty.items.isModel || (modelMaps.containsKey(codegenProperty.items.dataType) && codegenProperty.items.allowableValues == null))) ||
                (codegenProperty.isMap && codegenProperty.items.isModel))
            ) {
                example.append("\n");
                hasModelProperty = true;
            }

            if (requiredOnly && !codegenProperty.required) {
                continue;
            }

            if (codegenProperty.isModel) {
                String modelExample = constructExampleCode(codegenProperty, modelMaps, processedModelMap, requiredOnly);
                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample).append("\n");
                }

                propertyExamples.add("-" + codegenProperty.name + " " + "$" + codegenProperty.dataType);
            } else if (codegenProperty.isArray && (codegenProperty.items.isModel || (modelMaps.containsKey(codegenProperty.items.dataType) && codegenProperty.items.allowableValues == null))) {
                String modelExample;
                if (codegenProperty.items.isModel) {
                    modelExample = constructExampleCode(codegenProperty.items, modelMaps, processedModelMap, requiredOnly);
                } else {
                    modelExample = constructExampleCode(modelMaps.get(codegenProperty.items.dataType), modelMaps, processedModelMap, requiredOnly);
                }

                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample).append("\n");
                }

                propertyExamples.add("-" + codegenProperty.name + " " + "$" + codegenProperty.complexType);
            } else if (codegenProperty.isArray && codegenProperty.items.isString) {
                if (codegenProperty.items.isEnum || (codegenProperty.items.allowableValues != null && !codegenProperty.items.allowableValues.isEmpty())) {
                    example.append(constructEnumExample(codegenProperty.items.allowableValues));
                    propertyExamples.add("-" + codegenProperty.name + " " + example);
                } else {
                    StringBuilder stringArrayPropertyValue = new StringBuilder();
                    String genericStringExample = codegenProperty.items.name + "_example";

                    stringArrayPropertyValue.append(constructStringExample(codegenProperty.name, codegenProperty.items.example, genericStringExample));

                    propertyExamples.add("-" + codegenProperty.name + " " + stringArrayPropertyValue);
                }
            } else if (codegenProperty.isMap && codegenProperty.items.isModel) {
                String modelExample = constructExampleCode(codegenProperty.items, modelMaps, processedModelMap, requiredOnly);
                if (!StringUtils.isEmpty(modelExample)) {
                    example.append(modelExample).append("\n");
                }

                propertyExamples.add("-" + codegenProperty.name + " " + "@{ key_example = " + "$" + codegenProperty.complexType + " }");
            } else {
                propertyExamples.add("-" + codegenProperty.name + " " + constructExampleCode(codegenProperty, modelMaps, processedModelMap, requiredOnly));
            }
        }

        example.append("$");
        if (this.useClassNameInModelsExamples) {
            example.append(codegenModel.classname);
        } else {
            example.append(codegenModel.name);
        }
        example.append(" = ").append(this.modelsCmdletVerb).append("-");
        if (this.useClassNameInModelsExamples) {
            example.append(codegenModel.classname);
        } else {
            example.append(codegenModel.name);
        }
        example.append(" ");

        example.append(StringUtils.join(propertyExamples, " "));

        if (hasModelProperty) {
            example.append("\n");
        }

        return example.toString();
    }

    private String constructStringExample(String codegenName, String codegenExample, String genericStringExample) {
        StringBuilder example = new StringBuilder();
        example.append("\"");

        if (
            StringUtils.isEmpty(codegenExample) ||
            codegenExample.equals("null") ||
            codegenExample.equals(genericStringExample)
        ) {
            example.append("My").append(codegenName);
        } else {
            example.append(codegenExample);
        }

        example.append("\"");

        return example.toString();
    }

    private String constructEnumExample(Map allowableValues) {
        StringBuilder example = new StringBuilder();

        example.append("\"");

        List enumValues = (List) allowableValues.get("values");
        example.append(enumValues.get(0));

        example.append("\"");

        return example.toString();
    }

    private String constructNumericExample(String codegenExample) {
        StringBuilder example = new StringBuilder();

        if (StringUtils.isEmpty(codegenExample) || codegenExample.equals("null")) {
            example.append("0");
        } else {
            example.append(codegenExample);
        }

        return example.toString();
    }

    private String constructBooleanExample(String codegenExample) {
        StringBuilder example = new StringBuilder();

        if (Boolean.parseBoolean(codegenExample)) {
            example.append("$true");
        } else {
            example.append("$false");
        }

        return example.toString();
    }

    private String getPSDataType(CodegenProperty cp) {
        String dataType;
        if (cp.isPrimitiveType) {
            dataType = cp.dataType;
            if (!(cp.isString || cp.isFile || cp.isContainer)
                    && (cp.isNullable || !cp.required)) {
                dataType = "System.Nullable[" + dataType + "]";
            }
            return dataType;
        } else if (cp.isArray) { // array
            return getPSDataType(cp.items) + "[]";
        } else if (cp.isMap) { // map
            return "System.Collections.Hashtable";
        } else { // model
            return "PSCustomObject";
        }
    }

    private String getPSDataType(CodegenParameter cp) {
        String dataType;
        if (cp.isPrimitiveType) {
            dataType = cp.dataType;
            if (!(cp.isString || cp.isFile || cp.isContainer)
                    && (cp.isNullable || !cp.required)) {
                dataType = "System.Nullable[" + dataType + "]";
            }
            return dataType;
        } else if (cp.isArray) { // array
            return getPSDataType(cp.items) + "[]";
        } else if (cp.isMap) { // map
            return "System.Collections.Hashtable";
        } else { // model
            return "PSCustomObject";
        }
    }

    private String toMethodName(String operationId) {
        String methodName = camelize(operationId);

        if (!skipVerbParsing) {
            // check if method name starts with powershell verbs
            for (String verb : (HashSet) powershellVerbs) {
                if (methodName.startsWith(verb)) {
                    methodName = verb + "-" + apiNamePrefix + methodName.substring(verb.length());
                    LOGGER.info("Naming the method using the PowerShell verb: {} => {}", operationId, methodName);
                    return methodName;
                }
            }

            for (Map.Entry entry : commonVerbs.entrySet()) {
                if (methodName.startsWith(entry.getKey())) {
                    methodName = entry.getValue() + "-" + apiNamePrefix + methodName.substring(entry.getKey().length());
                    LOGGER.info("Naming the method by mapping the common verbs (e.g. Create, Change) to PS verbs: {} => {}", operationId, methodName);
                    return methodName;
                }
            }
        }

        // not using powershell verb
        return "Invoke-" + apiNamePrefix + methodName;
    }

    @Override
    public void postProcessFile(File file, String fileType) {
        super.postProcessFile(file, fileType);
        if (file == null) {
            return;
        }
        String powershellPostProcessFile = System.getenv("POWERSHELL_POST_PROCESS_FILE");
        if (StringUtils.isEmpty(powershellPostProcessFile)) {
            return; // skip if POWERSHELL_POST_PROCESS_FILE env variable is not defined
        }

        // only process files with ps extension
        if ("ps".equals(FilenameUtils.getExtension(file.toString()))) {
            this.executePostProcessor(new String[] {powershellPostProcessFile, file.toString()});
        }

    }

    @Override
    public String toRegularExpression(String pattern) {
        return escapeText(pattern);
    }

    @Override
    public String toDefaultValue(Schema p) {
        if (p.getDefault() != null) {
            if (ModelUtils.isBooleanSchema(p)) {
                if (Boolean.parseBoolean(p.getDefault().toString())) {
                    return "$true";
                } else {
                    return "$false";
                }
            } else if (ModelUtils.isDateSchema(p)) {
                LOGGER.warn("Default value for `date` not yet supported. Please open an issue with https://github.com/openapitools/openapi-generator");
            } else if (ModelUtils.isDateTimeSchema(p)) {
                LOGGER.warn("Default value for `datetime` not yet supported. Please open an issue with https://github.com/openapitools/openapi-generator");
            } else if (ModelUtils.isNumberSchema(p)) {
                return p.getDefault().toString();
            } else if (ModelUtils.isIntegerSchema(p)) {
                return p.getDefault().toString();
            } else if (ModelUtils.isStringSchema(p)) {
                return "\"" + p.getDefault() + "\"";
            }
        }

        return null;
    }

    @Override
    public void postProcess() {
        System.out.println("################################################################################");
        System.out.println("# Thanks for using OpenAPI Generator.                                          #");
        System.out.println("# Please consider donation to help us maintain this project \uD83D\uDE4F                 #");
        System.out.println("# https://opencollective.com/openapi_generator/donate                          #");
        System.out.println("#                                                                              #");
        System.out.println("# This generator has been refactored by wing328 (https://github.com/wing328)   #");
        System.out.println("# Please support his work directly by purchasing a copy of the eBook \ud83d\udcd8        #");
        System.out.println("# - OpenAPI Generator for PowerShell Developers      https://bit.ly/3qBWfRJ    #");
        System.out.println("################################################################################");
    }

    @Override
    public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.POWERSHELL; }

    @Override
    public String toEnumVarName(String name, String datatype) {
        if (name.length() == 0) {
            return "EMPTY";
        }

        // for symbol, e.g. $, #
        if (getSymbolName(name) != null) {
            return (getSymbolName(name)).toUpperCase(Locale.ROOT);
        }

        // number
        if ("Int16".equals(datatype) ||  "Int32".equals(datatype)  || "Int64".equals(datatype) ||
            "UInt16".equals(datatype) ||  "UInt32".equals(datatype)  || "UInt64".equals(datatype) ||
            "Double".equals(datatype) || "Single".equals(datatype) || "Decimal".equals(datatype)) {
            String varName = name;
            varName = varName.replaceAll("-", "MINUS_");
            varName = varName.replaceAll("\\+", "PLUS_");
            varName = varName.replaceAll("\\.", "_DOT_");
            return "NUMBER_" + varName;
        }

        // remove special character
        return sanitizeName(name);
    }
}