private static CodegenModel reconcileInlineEnums(
      CodegenModel codegenModel, CodegenModel parentCodegenModel) {
    // This generator uses inline classes to define enums, which breaks when
    // dealing with models that have subTypes. To clean this up, we will analyze
    // the parent and child models, look for enums that match, and remove
    // them from the child models and leave them in the parent.
    // Because the child models extend the parents, the enums will be available via the parent.

    // Only bother with reconciliation if the parent model has enums.
    if (parentCodegenModel.hasEnums) {

      // Get the properties for the parent and child models
      final List<CodegenProperty> parentModelCodegenProperties = parentCodegenModel.vars;
      List<CodegenProperty> codegenProperties = codegenModel.vars;

      // Iterate over all of the parent model properties
      boolean removedChildEnum = false;
      for (CodegenProperty parentModelCodegenPropery : parentModelCodegenProperties) {
        // Look for enums
        if (parentModelCodegenPropery.isEnum) {
          // Now that we have found an enum in the parent class,
          // and search the child class for the same enum.
          Iterator<CodegenProperty> iterator = codegenProperties.iterator();
          while (iterator.hasNext()) {
            CodegenProperty codegenProperty = iterator.next();
            if (codegenProperty.isEnum && codegenProperty.equals(parentModelCodegenPropery)) {
              // We found an enum in the child class that is
              // a duplicate of the one in the parent, so remove it.
              iterator.remove();
              removedChildEnum = true;
            }
          }
        }
      }

      if (removedChildEnum) {
        // If we removed an entry from this model's vars, we need to ensure hasMore is updated
        int count = 0, numVars = codegenProperties.size();
        for (CodegenProperty codegenProperty : codegenProperties) {
          count += 1;
          codegenProperty.hasMore = (count < numVars) ? true : null;
        }
        codegenModel.vars = codegenProperties;
      }
    }

    return codegenModel;
  }
 @Override
 public int hashCode() {
   int result = parent != null ? parent.hashCode() : 0;
   result = 31 * result + (parentSchema != null ? parentSchema.hashCode() : 0);
   result = 31 * result + (interfaces != null ? interfaces.hashCode() : 0);
   result = 31 * result + (parentModel != null ? parentModel.hashCode() : 0);
   result = 31 * result + (interfaceModels != null ? interfaceModels.hashCode() : 0);
   result = 31 * result + (name != null ? name.hashCode() : 0);
   result = 31 * result + (classname != null ? classname.hashCode() : 0);
   result = 31 * result + (title != null ? title.hashCode() : 0);
   result = 31 * result + (description != null ? description.hashCode() : 0);
   result = 31 * result + (classVarName != null ? classVarName.hashCode() : 0);
   result = 31 * result + (modelJson != null ? modelJson.hashCode() : 0);
   result = 31 * result + (dataType != null ? dataType.hashCode() : 0);
   result = 31 * result + (classFilename != null ? classFilename.hashCode() : 0);
   result = 31 * result + (unescapedDescription != null ? unescapedDescription.hashCode() : 0);
   result = 31 * result + (discriminator != null ? discriminator.hashCode() : 0);
   result = 31 * result + (defaultValue != null ? defaultValue.hashCode() : 0);
   result = 31 * result + (vars != null ? vars.hashCode() : 0);
   result = 31 * result + (requiredVars != null ? requiredVars.hashCode() : 0);
   result = 31 * result + (optionalVars != null ? optionalVars.hashCode() : 0);
   result = 31 * result + (allVars != null ? allVars.hashCode() : 0);
   result = 31 * result + (allowableValues != null ? allowableValues.hashCode() : 0);
   result = 31 * result + (mandatory != null ? mandatory.hashCode() : 0);
   result = 31 * result + (allMandatory != null ? allMandatory.hashCode() : 0);
   result = 31 * result + (imports != null ? imports.hashCode() : 0);
   result = 31 * result + (hasVars != null ? hasVars.hashCode() : 0);
   result = 31 * result + (emptyVars != null ? emptyVars.hashCode() : 0);
   result = 31 * result + (hasMoreModels != null ? hasMoreModels.hashCode() : 0);
   result = 31 * result + (hasEnums != null ? hasEnums.hashCode() : 0);
   result = 31 * result + (isEnum != null ? isEnum.hashCode() : 0);
   result = 31 * result + (externalDocs != null ? externalDocs.hashCode() : 0);
   result = 31 * result + (vendorExtensions != null ? vendorExtensions.hashCode() : 0);
   result = 31 * result + Objects.hash(hasOnlyReadOnly);
   result = 31 * result + Objects.hash(hasChildren);
   result = 31 * result + Objects.hash(parentVars);
   return result;
 }
  @Override
  public List<File> generate() {
    Boolean generateApis = null;
    Boolean generateModels = null;
    Boolean generateSupportingFiles = null;

    Set<String> modelsToGenerate = null;
    Set<String> apisToGenerate = null;
    Set<String> supportingFilesToGenerate = null;

    // allows generating only models by specifying a CSV of models to generate, or empty for all
    if (System.getProperty("models") != null) {
      String modelNames = System.getProperty("models");
      generateModels = true;
      if (!modelNames.isEmpty()) {
        modelsToGenerate = new HashSet<String>(Arrays.asList(modelNames.split(",")));
      }
    }
    if (System.getProperty("apis") != null) {
      String apiNames = System.getProperty("apis");
      generateApis = true;
      if (!apiNames.isEmpty()) {
        apisToGenerate = new HashSet<String>(Arrays.asList(apiNames.split(",")));
      }
    }
    if (System.getProperty("supportingFiles") != null) {
      String supportingFiles = System.getProperty("supportingFiles");
      generateSupportingFiles = true;
      if (!supportingFiles.isEmpty()) {
        supportingFilesToGenerate = new HashSet<String>(Arrays.asList(supportingFiles.split(",")));
      }
    }

    if (generateApis == null && generateModels == null && generateSupportingFiles == null) {
      // no specifics are set, generate everything
      generateApis = true;
      generateModels = true;
      generateSupportingFiles = true;
    } else {
      if (generateApis == null) {
        generateApis = false;
      }
      if (generateModels == null) {
        generateModels = false;
      }
      if (generateSupportingFiles == null) {
        generateSupportingFiles = false;
      }
    }

    if (swagger == null || config == null) {
      throw new RuntimeException("missing swagger input or config!");
    }
    if (System.getProperty("debugSwagger") != null) {
      Json.prettyPrint(swagger);
    }
    List<File> files = new ArrayList<File>();
    config.processOpts();
    config.preprocessSwagger(swagger);

    config.additionalProperties().put("generatedDate", DateTime.now().toString());
    config.additionalProperties().put("generatorClass", config.getClass().toString());

    if (swagger.getInfo() != null) {
      Info info = swagger.getInfo();
      if (info.getTitle() != null) {
        config.additionalProperties().put("appName", info.getTitle());
      }
      if (info.getVersion() != null) {
        config.additionalProperties().put("appVersion", info.getVersion());
      }
      if (info.getDescription() != null) {
        config
            .additionalProperties()
            .put("appDescription", config.escapeText(info.getDescription()));
      }
      if (info.getContact() != null) {
        Contact contact = info.getContact();
        config.additionalProperties().put("infoUrl", contact.getUrl());
        if (contact.getEmail() != null) {
          config.additionalProperties().put("infoEmail", contact.getEmail());
        }
      }
      if (info.getLicense() != null) {
        License license = info.getLicense();
        if (license.getName() != null) {
          config.additionalProperties().put("licenseInfo", license.getName());
        }
        if (license.getUrl() != null) {
          config.additionalProperties().put("licenseUrl", license.getUrl());
        }
      }
      if (info.getVersion() != null) {
        config.additionalProperties().put("version", info.getVersion());
      }
    }

    StringBuilder hostBuilder = new StringBuilder();
    String scheme;
    if (swagger.getSchemes() != null && swagger.getSchemes().size() > 0) {
      scheme = swagger.getSchemes().get(0).toValue();
    } else {
      scheme = "https";
    }
    hostBuilder.append(scheme);
    hostBuilder.append("://");
    if (swagger.getHost() != null) {
      hostBuilder.append(swagger.getHost());
    } else {
      hostBuilder.append("localhost");
    }
    if (swagger.getBasePath() != null) {
      hostBuilder.append(swagger.getBasePath());
    }
    String contextPath = swagger.getBasePath() == null ? "" : swagger.getBasePath();
    String basePath = hostBuilder.toString();
    String basePathWithoutHost = swagger.getBasePath();

    // resolve inline models
    InlineModelResolver inlineModelResolver = new InlineModelResolver();
    inlineModelResolver.flatten(swagger);

    List<Object> allOperations = new ArrayList<Object>();
    List<Object> allModels = new ArrayList<Object>();

    // models
    Map<String, Model> definitions = swagger.getDefinitions();
    if (definitions != null) {
      List<String> sortedModelKeys = sortModelsByInheritance(definitions);

      if (generateModels) {
        if (modelsToGenerate != null && modelsToGenerate.size() > 0) {
          List<String> updatedKeys = new ArrayList<String>();
          for (String m : sortedModelKeys) {
            if (modelsToGenerate.contains(m)) {
              updatedKeys.add(m);
            }
          }
          sortedModelKeys = updatedKeys;
        }

        for (String name : sortedModelKeys) {
          try {
            // don't generate models that have an import mapping
            if (config.importMapping().containsKey(name)) {
              continue;
            }

            Model model = definitions.get(name);
            Map<String, Model> modelMap = new HashMap<String, Model>();
            modelMap.put(name, model);
            Map<String, Object> models = processModels(config, modelMap, definitions);
            models.putAll(config.additionalProperties());

            allModels.add(((List<Object>) models.get("models")).get(0));

            for (String templateName : config.modelTemplateFiles().keySet()) {
              String suffix = config.modelTemplateFiles().get(templateName);
              String filename =
                  config.modelFileFolder() + File.separator + config.toModelFilename(name) + suffix;
              if (!config.shouldOverwrite(filename)) {
                continue;
              }
              String templateFile = getFullTemplateFile(config, templateName);
              String template = readTemplate(templateFile);
              Template tmpl =
                  Mustache.compiler()
                      .withLoader(
                          new Mustache.TemplateLoader() {
                            @Override
                            public Reader getTemplate(String name) {
                              return getTemplateReader(
                                  getFullTemplateFile(config, name + ".mustache"));
                            }
                          })
                      .defaultValue("")
                      .compile(template);
              writeToFile(filename, tmpl.execute(models));
              files.add(new File(filename));
            }
          } catch (Exception e) {
            throw new RuntimeException("Could not generate model '" + name + "'", e);
          }
        }
      }
    }
    if (System.getProperty("debugModels") != null) {
      System.out.println("############ Model info ############");
      Json.prettyPrint(allModels);
    }

    // apis
    Map<String, List<CodegenOperation>> paths = processPaths(swagger.getPaths());
    if (generateApis) {
      if (apisToGenerate != null && apisToGenerate.size() > 0) {
        Map<String, List<CodegenOperation>> updatedPaths =
            new TreeMap<String, List<CodegenOperation>>();
        for (String m : paths.keySet()) {
          if (apisToGenerate.contains(m)) {
            updatedPaths.put(m, paths.get(m));
          }
        }
        paths = updatedPaths;
      }
      for (String tag : paths.keySet()) {
        try {
          List<CodegenOperation> ops = paths.get(tag);
          Map<String, Object> operation = processOperations(config, tag, ops);

          operation.put("basePath", basePath);
          operation.put("basePathWithoutHost", basePathWithoutHost);
          operation.put("contextPath", contextPath);
          operation.put("baseName", tag);
          operation.put("modelPackage", config.modelPackage());
          operation.putAll(config.additionalProperties());
          operation.put("classname", config.toApiName(tag));
          operation.put("classVarName", config.toApiVarName(tag));
          operation.put("importPath", config.toApiImport(tag));

          // Pass sortParamsByRequiredFlag through to the Mustache template...
          boolean sortParamsByRequiredFlag = true;
          if (this.config
              .additionalProperties()
              .containsKey(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)) {
            sortParamsByRequiredFlag =
                Boolean.valueOf(
                    (String)
                        this.config
                            .additionalProperties()
                            .get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)
                            .toString());
          }
          operation.put("sortParamsByRequiredFlag", sortParamsByRequiredFlag);

          processMimeTypes(swagger.getConsumes(), operation, "consumes");
          processMimeTypes(swagger.getProduces(), operation, "produces");

          allOperations.add(new HashMap<String, Object>(operation));
          for (int i = 0; i < allOperations.size(); i++) {
            Map<String, Object> oo = (Map<String, Object>) allOperations.get(i);
            if (i < (allOperations.size() - 1)) {
              oo.put("hasMore", "true");
            }
          }

          for (String templateName : config.apiTemplateFiles().keySet()) {
            String filename = config.apiFilename(templateName, tag);
            if (!config.shouldOverwrite(filename) && new File(filename).exists()) {
              continue;
            }

            String templateFile = getFullTemplateFile(config, templateName);
            String template = readTemplate(templateFile);
            Template tmpl =
                Mustache.compiler()
                    .withLoader(
                        new Mustache.TemplateLoader() {
                          @Override
                          public Reader getTemplate(String name) {
                            return getTemplateReader(
                                getFullTemplateFile(config, name + ".mustache"));
                          }
                        })
                    .defaultValue("")
                    .compile(template);

            writeToFile(filename, tmpl.execute(operation));
            files.add(new File(filename));
          }
        } catch (Exception e) {
          throw new RuntimeException("Could not generate api file for '" + tag + "'", e);
        }
      }
    }
    if (System.getProperty("debugOperations") != null) {
      System.out.println("############ Operation info ############");
      Json.prettyPrint(allOperations);
    }

    // supporting files
    Map<String, Object> bundle = new HashMap<String, Object>();
    bundle.putAll(config.additionalProperties());
    bundle.put("apiPackage", config.apiPackage());

    Map<String, Object> apis = new HashMap<String, Object>();
    apis.put("apis", allOperations);
    if (swagger.getHost() != null) {
      bundle.put("host", swagger.getHost());
    }
    bundle.put("basePath", basePath);
    bundle.put("scheme", scheme);
    bundle.put("contextPath", contextPath);
    bundle.put("apiInfo", apis);
    bundle.put("models", allModels);
    bundle.put("apiFolder", config.apiPackage().replace('.', File.separatorChar));
    bundle.put("modelPackage", config.modelPackage());
    List<CodegenSecurity> authMethods = config.fromSecurity(swagger.getSecurityDefinitions());
    if (authMethods != null && !authMethods.isEmpty()) {
      bundle.put("authMethods", authMethods);
      bundle.put("hasAuthMethods", true);
    }
    if (swagger.getExternalDocs() != null) {
      bundle.put("externalDocs", swagger.getExternalDocs());
    }
    for (int i = 0; i < allModels.size() - 1; i++) {
      HashMap<String, CodegenModel> cm = (HashMap<String, CodegenModel>) allModels.get(i);
      CodegenModel m = cm.get("model");
      m.hasMoreModels = true;
    }

    config.postProcessSupportingFileData(bundle);

    if (System.getProperty("debugSupportingFiles") != null) {
      System.out.println("############ Supporting file info ############");
      Json.prettyPrint(bundle);
    }

    if (generateSupportingFiles) {
      for (SupportingFile support : config.supportingFiles()) {
        try {
          String outputFolder = config.outputFolder();
          if (isNotEmpty(support.folder)) {
            outputFolder += File.separator + support.folder;
          }
          File of = new File(outputFolder);
          if (!of.isDirectory()) {
            of.mkdirs();
          }
          String outputFilename = outputFolder + File.separator + support.destinationFilename;
          if (!config.shouldOverwrite(outputFilename)) {
            continue;
          }

          String templateFile = getFullTemplateFile(config, support.templateFile);

          boolean shouldGenerate = true;
          if (supportingFilesToGenerate != null && supportingFilesToGenerate.size() > 0) {
            if (supportingFilesToGenerate.contains(support.destinationFilename)) {
              shouldGenerate = true;
            } else {
              shouldGenerate = false;
            }
          }
          if (shouldGenerate) {
            if (templateFile.endsWith("mustache")) {
              String template = readTemplate(templateFile);
              Template tmpl =
                  Mustache.compiler()
                      .withLoader(
                          new Mustache.TemplateLoader() {
                            @Override
                            public Reader getTemplate(String name) {
                              return getTemplateReader(
                                  getFullTemplateFile(config, name + ".mustache"));
                            }
                          })
                      .defaultValue("")
                      .compile(template);

              writeToFile(outputFilename, tmpl.execute(bundle));
              files.add(new File(outputFilename));
            } else {
              InputStream in = null;

              try {
                in = new FileInputStream(templateFile);
              } catch (Exception e) {
                // continue
              }
              if (in == null) {
                in =
                    this.getClass()
                        .getClassLoader()
                        .getResourceAsStream(getCPResourcePath(templateFile));
              }
              File outputFile = new File(outputFilename);
              OutputStream out = new FileOutputStream(outputFile, false);
              if (in != null && out != null) {
                System.out.println("writing file " + outputFile);
                IOUtils.copy(in, out);
              } else {
                if (in == null) {
                  System.out.println("can't open " + templateFile + " for input");
                }
                if (out == null) {
                  System.out.println("can't open " + outputFile + " for output");
                }
              }

              files.add(outputFile);
            }
          }
        } catch (Exception e) {
          throw new RuntimeException("Could not generate supporting file '" + support + "'", e);
        }
      }
    }

    config.processSwagger(swagger);

    return files;
  }
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    CodegenModel that = (CodegenModel) o;

    if (parent != null ? !parent.equals(that.parent) : that.parent != null) return false;
    if (parentSchema != null ? !parentSchema.equals(that.parentSchema) : that.parentSchema != null)
      return false;
    if (interfaces != null ? !interfaces.equals(that.interfaces) : that.interfaces != null)
      return false;
    if (parentModel != null ? !parentModel.equals(that.parentModel) : that.parentModel != null)
      return false;
    if (interfaceModels != null
        ? !interfaceModels.equals(that.interfaceModels)
        : that.interfaceModels != null) return false;
    if (name != null ? !name.equals(that.name) : that.name != null) return false;
    if (classname != null ? !classname.equals(that.classname) : that.classname != null)
      return false;
    if (title != null ? !title.equals(that.title) : that.title != null) return false;
    if (description != null ? !description.equals(that.description) : that.description != null)
      return false;
    if (classVarName != null ? !classVarName.equals(that.classVarName) : that.classVarName != null)
      return false;
    if (modelJson != null ? !modelJson.equals(that.modelJson) : that.modelJson != null)
      return false;
    if (dataType != null ? !dataType.equals(that.dataType) : that.dataType != null) return false;
    if (classFilename != null
        ? !classFilename.equals(that.classFilename)
        : that.classFilename != null) return false;
    if (unescapedDescription != null
        ? !unescapedDescription.equals(that.unescapedDescription)
        : that.unescapedDescription != null) return false;
    if (discriminator != null
        ? !discriminator.equals(that.discriminator)
        : that.discriminator != null) return false;
    if (defaultValue != null ? !defaultValue.equals(that.defaultValue) : that.defaultValue != null)
      return false;
    if (vars != null ? !vars.equals(that.vars) : that.vars != null) return false;
    if (requiredVars != null ? !requiredVars.equals(that.requiredVars) : that.requiredVars != null)
      return false;
    if (optionalVars != null ? !optionalVars.equals(that.optionalVars) : that.optionalVars != null)
      return false;
    if (allVars != null ? !allVars.equals(that.allVars) : that.allVars != null) return false;
    if (allowableValues != null
        ? !allowableValues.equals(that.allowableValues)
        : that.allowableValues != null) return false;
    if (mandatory != null ? !mandatory.equals(that.mandatory) : that.mandatory != null)
      return false;
    if (allMandatory != null ? !allMandatory.equals(that.allMandatory) : that.allMandatory != null)
      return false;
    if (imports != null ? !imports.equals(that.imports) : that.imports != null) return false;
    if (hasVars != null ? !hasVars.equals(that.hasVars) : that.hasVars != null) return false;
    if (emptyVars != null ? !emptyVars.equals(that.emptyVars) : that.emptyVars != null)
      return false;
    if (hasMoreModels != null
        ? !hasMoreModels.equals(that.hasMoreModels)
        : that.hasMoreModels != null) return false;
    if (hasEnums != null ? !hasEnums.equals(that.hasEnums) : that.hasEnums != null) return false;
    if (isEnum != null ? !isEnum.equals(that.isEnum) : that.isEnum != null) return false;
    if (externalDocs != null ? !externalDocs.equals(that.externalDocs) : that.externalDocs != null)
      return false;
    if (!Objects.equals(hasOnlyReadOnly, that.hasOnlyReadOnly)) return false;
    if (!Objects.equals(hasChildren, that.hasChildren)) return false;
    if (!Objects.equals(parentVars, that.parentVars)) return false;
    return vendorExtensions != null
        ? vendorExtensions.equals(that.vendorExtensions)
        : that.vendorExtensions == null;
  }