From d2a6dc2eec74846584f2d4f2f463c8e31e78a16e Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 16 Mar 2026 22:57:26 +0100 Subject: [PATCH 01/22] improve speed of generator --- .../openapitools/codegen/DefaultCodegen.java | 11 ++-- .../codegen/DefaultGenerator.java | 9 +-- .../openapitools/codegen/TemplateManager.java | 11 +++- .../templating/HandlebarsEngineAdapter.java | 63 +++++++++++++------ .../templating/MustacheEngineAdapter.java | 17 +++-- 5 files changed, 79 insertions(+), 32 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index d83cdef420cf..988445107156 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -6341,12 +6341,15 @@ private String uniqueCaseInsensitiveString(String value, Map see return seenValues.get(value); } - Optional> foundEntry = seenValues.entrySet().stream().filter(v -> v.getValue().toLowerCase(Locale.ROOT).equals(value.toLowerCase(Locale.ROOT))).findAny(); - if (foundEntry.isPresent()) { + // Build the set of already-used lowercase values once, to avoid O(n) re-collection per loop iteration. + Set lowercaseValues = seenValues.values().stream() + .map(v -> v.toLowerCase(Locale.ROOT)) + .collect(Collectors.toSet()); + + if (lowercaseValues.contains(value.toLowerCase(Locale.ROOT))) { int counter = 0; String uniqueValue = value + "_" + counter; - - while (seenValues.values().stream().map(v -> v.toLowerCase(Locale.ROOT)).collect(Collectors.toList()).contains(uniqueValue.toLowerCase(Locale.ROOT))) { + while (lowercaseValues.contains(uniqueValue.toLowerCase(Locale.ROOT))) { counter++; uniqueValue = value + "_" + counter; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 999beb6ad3ce..942f371f0c1a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -1430,7 +1430,8 @@ protected File processTemplateToFile(Map templateData, String te return processTemplateToFile(templateData, templateName, outputFilename, shouldGenerate, skippedByOption, this.config.getOutputDir()); } - private final Set seenFiles = new HashSet<>(); + /** Stores lowercased absolute paths for O(1) case-insensitive duplicate detection. */ + private final Set seenFilesLower = new HashSet<>(); private File processTemplateToFile(Map templateData, String templateName, String outputFilename, boolean shouldGenerate, String skippedByOption, String intendedOutputDir) throws IOException { String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar); @@ -1443,10 +1444,10 @@ private File processTemplateToFile(Map templateData, String temp throw new RuntimeException(String.format(Locale.ROOT, "Target files must be generated within the output directory; absoluteTarget=%s outDir=%s", absoluteTarget, outDir)); } - if (seenFiles.stream().filter(f -> f.toLowerCase(Locale.ROOT).equals(absoluteTarget.toString().toLowerCase(Locale.ROOT))).findAny().isPresent()) { - LOGGER.warn("Duplicate file path detected. Not all operating systems can handle case sensitive file paths. path={}", absoluteTarget.toString()); + // O(1) case-insensitive duplicate check via a pre-lowercased shadow set + if (!seenFilesLower.add(absoluteTarget.toString().toLowerCase(Locale.ROOT))) { + LOGGER.warn("Duplicate file path detected. Not all operating systems can handle case sensitive file paths. path={}", absoluteTarget); } - seenFiles.add(absoluteTarget.toString()); return this.templateProcessor.write(templateData, templateName, target); } else { this.templateProcessor.skip(target.toPath(), String.format(Locale.ROOT, "Skipped by %s options supplied by user.", skippedByOption)); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java index 5fce684170e3..c347cb3f3526 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Objects; import java.util.Scanner; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; /** @@ -33,6 +34,9 @@ public class TemplateManager implements TemplatingExecutor, TemplateProcessor { private final Logger LOGGER = LoggerFactory.getLogger(TemplateManager.class); + /** Cache of resolved template path -> raw template content, populated on first read per run. */ + private final Map templateContentCache = new ConcurrentHashMap<>(); + /** * Constructs a new instance of a {@link TemplateManager} * @@ -75,7 +79,8 @@ private String getFullTemplateFile(String name) { */ @Override public String getFullTemplateContents(String name) { - return readTemplate(getFullTemplateFile(name)); + String fullPath = getFullTemplateFile(name); + return templateContentCache.computeIfAbsent(fullPath, this::readTemplate); } /** @@ -262,6 +267,8 @@ private File writeToFileRaw(String filename, byte[] contents) throws IOException } private boolean filesEqual(File file1, File file2) throws IOException { - return file1.exists() && file2.exists() && Arrays.equals(Files.readAllBytes(file1.toPath()), Files.readAllBytes(file2.toPath())); + if (!file1.exists() || !file2.exists()) return false; + if (file1.length() != file2.length()) return false; + return Arrays.equals(Files.readAllBytes(file1.toPath()), Files.readAllBytes(file2.toPath())); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java index 0805c267282d..9bb9332f4ac8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java @@ -40,6 +40,7 @@ import java.util.Arrays; import java.util.Locale; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class HandlebarsEngineAdapter extends AbstractTemplatingEngineAdapter { final Logger LOGGER = LoggerFactory.getLogger(HandlebarsEngineAdapter.class); @@ -50,6 +51,19 @@ public class HandlebarsEngineAdapter extends AbstractTemplatingEngineAdapter { private boolean infiniteLoops = false; @Setter private boolean prettyPrint = false; + /** + * Cache of (templateFile -> compiled Template). Compiled Handlebars templates are stateless after + * compilation and safe to reuse across multiple data-bundle invocations within the same run. + */ + private final Map compiledTemplateCache = new ConcurrentHashMap<>(); + + /** + * Cached Handlebars engine instance with all helpers pre-registered. + * Re-created lazily whenever the TemplatingExecutor changes (different template paths). + */ + private volatile Handlebars cachedHandlebars; + private volatile TemplatingExecutor cachedExecutor; + /** * Provides an identifier used to load the adapter. This could be a name, uuid, or any other string. * @@ -63,13 +77,6 @@ public String getIdentifier() { @Override public String compileTemplate(TemplatingExecutor executor, Map bundle, String templateFile) throws IOException { - TemplateLoader loader = new AbstractTemplateLoader() { - @Override - public TemplateSource sourceAt(String location) { - return findTemplate(executor, location); - } - }; - Context context = Context .newBuilder(bundle) .resolver( @@ -79,18 +86,38 @@ public TemplateSource sourceAt(String location) { AccessAwareFieldValueResolver.INSTANCE) .build(); - Handlebars handlebars = new Handlebars(loader); - handlebars.registerHelperMissing((obj, options) -> { - LOGGER.warn(String.format(Locale.ROOT, "Unregistered helper name '%s', processing template:%n%s", options.helperName, options.fn.text())); - return ""; + // Reuse the Handlebars engine when the executor hasn't changed; rebuild otherwise. + if (cachedHandlebars == null || cachedExecutor != executor) { + TemplateLoader loader = new AbstractTemplateLoader() { + @Override + public TemplateSource sourceAt(String location) { + return findTemplate(executor, location); + } + }; + Handlebars handlebars = new Handlebars(loader); + handlebars.registerHelperMissing((obj, options) -> { + LOGGER.warn(String.format(Locale.ROOT, "Unregistered helper name '%s', processing template:%n%s", options.helperName, options.fn.text())); + return ""; + }); + handlebars.registerHelper("json", Jackson2Helper.INSTANCE); + StringHelpers.register(handlebars); + handlebars.registerHelpers(ConditionalHelpers.class); + handlebars.registerHelpers(org.openapitools.codegen.templating.handlebars.StringHelpers.class); + handlebars.setInfiniteLoops(infiniteLoops); + handlebars.setPrettyPrint(prettyPrint); + // Changing the executor means template content may differ; clear stale entries. + compiledTemplateCache.clear(); + cachedHandlebars = handlebars; + cachedExecutor = executor; + } + + Template tmpl = compiledTemplateCache.computeIfAbsent(templateFile, tf -> { + try { + return cachedHandlebars.compile(tf); + } catch (IOException e) { + throw new RuntimeException("Failed to compile Handlebars template: " + tf, e); + } }); - handlebars.registerHelper("json", Jackson2Helper.INSTANCE); - StringHelpers.register(handlebars); - handlebars.registerHelpers(ConditionalHelpers.class); - handlebars.registerHelpers(org.openapitools.codegen.templating.handlebars.StringHelpers.class); - handlebars.setInfiniteLoops(infiniteLoops); - handlebars.setPrettyPrint(prettyPrint); - Template tmpl = handlebars.compile(templateFile); return tmpl.apply(context); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java index a71b71a985b0..81b24bebddc9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java @@ -31,6 +31,7 @@ import java.io.StringReader; import java.io.StringWriter; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class MustacheEngineAdapter implements TemplatingEngineAdapter { @@ -51,6 +52,12 @@ public String getIdentifier() { @Getter @Setter Mustache.Compiler compiler = Mustache.compiler(); + /** + * Cache of template file name -> compiled Template object. + * Templates are stateless after compilation and safe to reuse across invocations. + */ + private final Map compiledTemplateCache = new ConcurrentHashMap<>(); + /** * Compiles a template into a string * @@ -62,10 +69,12 @@ public String getIdentifier() { */ @Override public String compileTemplate(TemplatingExecutor executor, Map bundle, String templateFile) throws IOException { - Template tmpl = compiler - .withLoader(name -> findTemplate(executor, name)) - .defaultValue("") - .compile(executor.getFullTemplateContents(templateFile)); + // Compile once and cache; the compiled Template is stateless and reusable. + Template tmpl = compiledTemplateCache.computeIfAbsent(templateFile, tf -> + compiler + .withLoader(name -> findTemplate(executor, name)) + .defaultValue("") + .compile(executor.getFullTemplateContents(tf))); StringWriter out = new StringWriter(); // the value of bundle[MUSTACHE_PARENT_CONTEXT] is used a parent content in mustache. From d34343369e0b2deb502997405454adebdecf13d7 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 16 Mar 2026 23:22:07 +0100 Subject: [PATCH 02/22] implement suggested fixes from CR. fix some general nitpicks. --- .../openapitools/codegen/DefaultCodegen.java | 316 +++++++++--------- .../templating/HandlebarsEngineAdapter.java | 13 +- .../templating/MustacheEngineAdapter.java | 33 +- 3 files changed, 186 insertions(+), 176 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 988445107156..61b3e536c35a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -244,9 +244,9 @@ apiTemplateFiles are for API outputs only (controllers/handlers). // sort operations by default protected boolean skipSortingOperations = false; - protected final static Pattern XML_MIME_PATTERN = Pattern.compile("(?i)application\\/(.*)[+]?xml(;.*)?"); - protected final static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application\\/json(;.*)?"); - protected final static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application\\/vnd.(.*)+json(;.*)?"); + protected final static Pattern XML_MIME_PATTERN = Pattern.compile("(?i)application/(.*)[+]?xml(;.*)?"); + protected final static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application/json(;.*)?"); + protected final static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application/vnd.(.*)+json(;.*)?"); private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); /** @@ -472,7 +472,7 @@ protected ImmutableMap.Builder addMustacheLambdas() { private void registerMustacheLambdas() { ImmutableMap lambdas = addMustacheLambdas().build(); - if (lambdas.size() == 0) { + if (lambdas.isEmpty()) { return; } @@ -552,7 +552,7 @@ public Map postProcessAllModels(Map objs) List> modelsImports = modelsAttrs.getImportsOrEmpty(); for (ModelMap mo : modelsAttrs.getModels()) { CodegenModel cm = mo.getModel(); - if (cm.oneOf.size() > 0) { + if (!cm.oneOf.isEmpty()) { cm.vendorExtensions.put(X_IS_ONE_OF_INTERFACE, true); for (String one : cm.oneOf) { if (!additionalDataMap.containsKey(one)) { @@ -594,11 +594,9 @@ public Map postProcessAllModels(Map objs) * @return */ private boolean codegenPropertyIsNew(CodegenModel model, CodegenProperty property) { - return model.parentModel == null - ? false - : model.parentModel.allVars.stream().anyMatch(p -> + return model.parentModel != null && model.parentModel.allVars.stream().anyMatch(p -> p.name.equals(property.name) && - (p.dataType.equals(property.dataType) == false || p.datatypeWithEnum.equals(property.datatypeWithEnum) == false)); + (!p.dataType.equals(property.dataType) || !p.datatypeWithEnum.equals(property.datatypeWithEnum))); } /** @@ -742,7 +740,7 @@ protected void removeSelfReferenceImports(CodegenModel model) { for (CodegenProperty cp : model.allVars) { // detect self import if (cp.dataType.equalsIgnoreCase(model.classname) || - (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { + (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { model.imports.remove(model.classname); // remove self import cp.isSelfReference = true; } @@ -784,7 +782,7 @@ private List getModelDependencies(List vars) { } private void setCircularReferencesOnProperties(final String root, - final Map> dependencyMap) { + final Map> dependencyMap) { dependencyMap.getOrDefault(root, new ArrayList<>()) .forEach(prop -> { final List unvisited = @@ -797,9 +795,9 @@ private void setCircularReferencesOnProperties(final String root, } private boolean isCircularReference(final String root, - final Set visited, - final List unvisited, - final Map> dependencyMap) { + final Set visited, + final List unvisited, + final Map> dependencyMap) { for (int i = 0; i < unvisited.size(); i++) { final String next = unvisited.get(i); if (!visited.contains(next)) { @@ -832,7 +830,7 @@ public ModelsMap postProcessModelsEnum(ModelsMap objs) { CodegenModel cm = mo.getModel(); // for enum model - if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) { + if (cm.isEnum && cm.allowableValues != null) { Map allowableValues = cm.allowableValues; List values = (List) allowableValues.get("values"); List> enumVars = buildEnumVars(values, cm.dataType); @@ -1123,7 +1121,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } else if (ModelUtils.isMapSchema(s)) { Schema addProps = ModelUtils.getAdditionalProperties(s); - if (addProps != null && ModelUtils.isComposedSchema(addProps)) { + if (ModelUtils.isComposedSchema(addProps)) { addOneOfNameExtension(addProps, nOneOf); addOneOfInterfaceModel(addProps, nOneOf); } @@ -1157,7 +1155,7 @@ public TemplatingEngineAdapter processTemplatingEngine(TemplatingEngineAdapter t @SuppressWarnings("static-method") public String escapeText(String input) { if (input == null) { - return input; + return null; } // remove \t, \n, \r @@ -1197,7 +1195,7 @@ public String escapeTextInSingleQuotes(String input) { @Override public String escapeTextWhileAllowingNewLines(String input) { if (input == null) { - return input; + return null; } // remove \t @@ -1209,7 +1207,7 @@ public String escapeTextWhileAllowingNewLines(String input) { StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(input) .replace("\\/", "/")) - .replaceAll("[\\t]", " ") + .replaceAll("\\t", " ") .replace("\\", "\\\\") .replace("\"", "\\\"")); } @@ -1231,7 +1229,7 @@ public String encodePath(String input) { @Override public String escapeUnsafeCharacters(String input) { LOGGER.warn("escapeUnsafeCharacters should be overridden in the code generator with proper logic to escape " + - "unsafe characters"); + "unsafe characters"); // doing nothing by default and code generator should implement // the logic to prevent code injection // later we'll make this method abstract to make sure @@ -1248,7 +1246,7 @@ public String escapeUnsafeCharacters(String input) { @Override public String escapeQuotationMark(String input) { LOGGER.warn("escapeQuotationMark should be overridden in the code generator with proper logic to escape " + - "single/double quote"); + "single/double quote"); return input.replace("\"", "\\\""); } @@ -2027,35 +2025,35 @@ public void setParameterExampleValue(CodegenParameter codegenParameter) { // TODO need to revise how to obtain the example value if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); - } else if (Boolean.TRUE.equals(codegenParameter.isBoolean)) { + } else if (codegenParameter.isBoolean) { codegenParameter.example = "true"; - } else if (Boolean.TRUE.equals(codegenParameter.isLong)) { + } else if (codegenParameter.isLong) { codegenParameter.example = "789"; - } else if (Boolean.TRUE.equals(codegenParameter.isInteger)) { + } else if (codegenParameter.isInteger) { codegenParameter.example = "56"; - } else if (Boolean.TRUE.equals(codegenParameter.isFloat)) { + } else if (codegenParameter.isFloat) { codegenParameter.example = "3.4"; - } else if (Boolean.TRUE.equals(codegenParameter.isDouble)) { + } else if (codegenParameter.isDouble) { codegenParameter.example = "1.2"; - } else if (Boolean.TRUE.equals(codegenParameter.isNumber)) { + } else if (codegenParameter.isNumber) { codegenParameter.example = "8.14"; - } else if (Boolean.TRUE.equals(codegenParameter.isBinary)) { + } else if (codegenParameter.isBinary) { codegenParameter.example = "BINARY_DATA_HERE"; - } else if (Boolean.TRUE.equals(codegenParameter.isByteArray)) { + } else if (codegenParameter.isByteArray) { codegenParameter.example = "BYTE_ARRAY_DATA_HERE"; - } else if (Boolean.TRUE.equals(codegenParameter.isFile)) { + } else if (codegenParameter.isFile) { codegenParameter.example = "/path/to/file.txt"; - } else if (Boolean.TRUE.equals(codegenParameter.isDate)) { + } else if (codegenParameter.isDate) { codegenParameter.example = "2013-10-20"; - } else if (Boolean.TRUE.equals(codegenParameter.isDateTime)) { + } else if (codegenParameter.isDateTime) { codegenParameter.example = "2013-10-20T19:20:30+01:00"; - } else if (Boolean.TRUE.equals(codegenParameter.isUuid)) { + } else if (codegenParameter.isUuid) { codegenParameter.example = "38400000-8cf0-11bd-b23e-10b96e4ef00d"; - } else if (Boolean.TRUE.equals(codegenParameter.isUri)) { + } else if (codegenParameter.isUri) { codegenParameter.example = "https://openapi-generator.tech"; - } else if (Boolean.TRUE.equals(codegenParameter.isString)) { + } else if (codegenParameter.isString) { codegenParameter.example = codegenParameter.paramName + "_example"; - } else if (Boolean.TRUE.equals(codegenParameter.isFreeFormObject)) { + } else if (codegenParameter.isFreeFormObject) { codegenParameter.example = "Object"; } @@ -2664,10 +2662,10 @@ private NamedSchema(String name, Schema s, boolean required, boolean schemaIsFro this.schemaIsFromAdditionalProperties = schemaIsFromAdditionalProperties; } - private String name; - private Schema schema; - private boolean required; - private boolean schemaIsFromAdditionalProperties; + private final String name; + private final Schema schema; + private final boolean required; + private final boolean schemaIsFromAdditionalProperties; @Override public boolean equals(Object o) { @@ -2677,9 +2675,9 @@ public boolean equals(Object o) { return false; NamedSchema that = (NamedSchema) o; return Objects.equals(required, that.required) && - Objects.equals(name, that.name) && - Objects.equals(schema, that.schema) && - Objects.equals(schemaIsFromAdditionalProperties, that.schemaIsFromAdditionalProperties); + Objects.equals(name, that.name) && + Objects.equals(schema, that.schema) && + Objects.equals(schemaIsFromAdditionalProperties, that.schemaIsFromAdditionalProperties); } @Override @@ -2701,7 +2699,7 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map extractSchemaTestCases(String refToTestC } LinkedHashMap> testNameToTesCase = (LinkedHashMap>) schemaNameToTestCases.get(schemaName); for (Entry> entry : testNameToTesCase.entrySet()) { - LinkedHashMap testExample = (LinkedHashMap) entry.getValue(); + LinkedHashMap testExample = entry.getValue(); String nameInSnakeCase = toTestCaseName(entry.getKey()); Object data = processTestExampleData(testExample.get("data")); SchemaTestCase testCase = new SchemaTestCase( @@ -3096,7 +3094,7 @@ public CodegenModel fromModel(String name, Schema schema) { m.getVendorExtensions().putAll(schema.getExtensions()); } m.isAlias = (typeAliases.containsKey(name) - || isAliasOfSimpleTypes(schema)); // check if the unaliased schema is an alias of simple OAS types + || isAliasOfSimpleTypes(schema)); // check if the unaliased schema is an alias of simple OAS types m.setDiscriminator(createDiscriminator(name, schema)); if (schema.getDeprecated() != null) { @@ -3466,8 +3464,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, ArrayList vis } } if (discriminatorsPropNames.size() > 1) { - once(LOGGER).warn("The oneOf schemas have conflicting discriminator property names. " + - "oneOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); + once(LOGGER).warn("The oneOf schemas have conflicting discriminator property names. oneOf schemas must have the same property name, but found {}", String.join(", ", discriminatorsPropNames)); } if (foundDisc != null && (hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getOneOf().size() && discriminatorsPropNames.size() == 1) { disc.setPropertyName(foundDisc.getPropertyName()); @@ -3495,8 +3492,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, ArrayList vis } } if (discriminatorsPropNames.size() > 1) { - once(LOGGER).warn("The anyOf schemas have conflicting discriminator property names. " + - "anyOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); + once(LOGGER).warn("The anyOf schemas have conflicting discriminator property names. anyOf schemas must have the same property name, but found {}", String.join(", ", discriminatorsPropNames)); } if (foundDisc != null && (hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getAnyOf().size() && discriminatorsPropNames.size() == 1) { disc.setPropertyName(foundDisc.getPropertyName()); @@ -3550,7 +3546,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, } CodegenProperty df = discriminatorFound(composedSchemaName, sc, discPropName, new TreeSet()); String modelName = ModelUtils.getSimpleRef(ref); - if (df == null || !df.isString || df.required != true) { + if (df == null || !df.isString || !df.required) { String msgSuffix = ""; if (df == null) { msgSuffix += discPropName + " is missing from the schema, define it as required and type string"; @@ -3558,7 +3554,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, if (!df.isString) { msgSuffix += "invalid type for " + discPropName + ", set it to string"; } - if (df.required != true) { + if (!df.required) { String spacer = ""; if (msgSuffix.length() != 0) { spacer = ". "; @@ -3695,13 +3691,13 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch boolean matched = false; for (MappedModel uniqueDescendant : uniqueDescendants) { if (uniqueDescendant.getMappingName().equals(otherDescendant.getMappingName()) - || (uniqueDescendant.getModelName().equals(otherDescendant.getModelName()))) { + || (uniqueDescendant.getModelName().equals(otherDescendant.getModelName()))) { matched = true; break; } } - if (matched == false) { + if (!matched) { uniqueDescendants.add(otherDescendant); } } @@ -3893,9 +3889,9 @@ protected void updatePropertyForAnyType(CodegenProperty property, Schema p) { } property.isNullable = property.isNullable || - !(ModelUtils.isComposedSchema(p)) || - p.getAllOf() == null || - p.getAllOf().size() == 0; + !(ModelUtils.isComposedSchema(p)) || + p.getAllOf() == null || + p.getAllOf().size() == 0; if (languageSpecificPrimitives.contains(property.dataType)) { property.isPrimitiveType = true; } @@ -4151,7 +4147,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo if (referencedSchema.getNullable() != null) { property.isNullable = referencedSchema.getNullable(); } else if (referencedSchema.getExtensions() != null && - referencedSchema.getExtensions().containsKey(X_NULLABLE)) { + referencedSchema.getExtensions().containsKey(X_NULLABLE)) { property.isNullable = (Boolean) referencedSchema.getExtensions().get(X_NULLABLE); } @@ -4211,11 +4207,10 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo property.isContainer = true; if (ModelUtils.isSet(p)) { property.containerType = "set"; - property.containerTypeMapped = typeMapping.get(property.containerType); } else { property.containerType = "array"; - property.containerTypeMapped = typeMapping.get(property.containerType); } + property.containerTypeMapped = typeMapping.get(property.containerType); property.baseType = getSchemaType(p); // handle inner property @@ -4235,9 +4230,9 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo } boolean isAnyTypeWithNothingElseSet = (ModelUtils.isAnyType(p) && - (p.getProperties() == null || p.getProperties().isEmpty()) && - !ModelUtils.isComposedSchema(p) && - p.getAdditionalProperties() == null && p.getNot() == null && p.getEnum() == null); + (p.getProperties() == null || p.getProperties().isEmpty()) && + !ModelUtils.isComposedSchema(p) && + p.getAdditionalProperties() == null && p.getNot() == null && p.getEnum() == null); if (!ModelUtils.isArraySchema(p) && !ModelUtils.isMapSchema(p) && !ModelUtils.isFreeFormObject(p, openAPI) && !isAnyTypeWithNothingElseSet) { /* schemas that are not Array, not ModelUtils.isMapSchema, not isFreeFormObject, not AnyType with nothing else set @@ -4356,7 +4351,7 @@ void updateDefaultToEmptyContainer(CodegenProperty cp, Schema p) { * @param input a set of rules separated by `|` */ void parseDefaultToEmptyContainer(String input) { - String[] inputs = ((String) input).split("[|]"); + String[] inputs = input.split("[|]"); String containerType; for (String rule : inputs) { if (StringUtils.isEmpty(rule)) { @@ -4374,7 +4369,7 @@ void parseDefaultToEmptyContainer(String input) { LOGGER.error("Skipped invalid container type `{}` in `{}`.", containerType, input); } } else if (rule.startsWith("?")) { // nullable (required) - containerType = rule.substring(1, rule.length()); + containerType = rule.substring(1); if ("array".equalsIgnoreCase(containerType)) { arrayNullableDefaultToEmpty = true; } else if ("map".equalsIgnoreCase(containerType)) { @@ -4492,8 +4487,8 @@ protected Boolean isPropertyInnerMostEnum(CodegenProperty property) { protected CodegenProperty getMostInnerItems(CodegenProperty property) { CodegenProperty currentProperty = property; - while (currentProperty != null && (Boolean.TRUE.equals(currentProperty.isMap) - || Boolean.TRUE.equals(currentProperty.isArray)) && currentProperty.items != null) { + while (currentProperty != null && (currentProperty.isMap + || currentProperty.isArray) && currentProperty.items != null) { currentProperty = currentProperty.items; } return currentProperty; @@ -4512,8 +4507,8 @@ protected Map getInnerEnumAllowableValues(CodegenProperty proper */ protected void updateDataTypeWithEnumForArray(CodegenProperty property) { CodegenProperty baseItem = property.items; - while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap) - || Boolean.TRUE.equals(baseItem.isArray))) { + while (baseItem != null && (baseItem.isMap + || baseItem.isArray)) { baseItem = baseItem.items; } if (baseItem != null) { @@ -4540,8 +4535,8 @@ protected void updateDataTypeWithEnumForArray(CodegenProperty property) { */ protected void updateDataTypeWithEnumForMap(CodegenProperty property) { CodegenProperty baseItem = property.items; - while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap) - || Boolean.TRUE.equals(baseItem.isArray))) { + while (baseItem != null && (baseItem.isMap + || baseItem.isArray)) { baseItem = baseItem.items; } @@ -4602,9 +4597,9 @@ protected ApiResponse findMethodResponse(ApiResponses responses) { * @param methodResponse the default ApiResponse for the endpoint */ protected void handleMethodResponse(Operation operation, - Map schemas, - CodegenOperation op, - ApiResponse methodResponse) { + Map schemas, + CodegenOperation op, + ApiResponse methodResponse) { handleMethodResponse(operation, schemas, op, methodResponse, Collections.emptyMap()); } @@ -4618,10 +4613,10 @@ protected void handleMethodResponse(Operation operation, * @param schemaMappings mappings of external types to be omitted by unaliasing */ protected void handleMethodResponse(Operation operation, - Map schemas, - CodegenOperation op, - ApiResponse methodResponse, - Map schemaMappings) { + Map schemas, + CodegenOperation op, + ApiResponse methodResponse, + Map schemaMappings) { ApiResponse response = ModelUtils.getReferencedApiResponse(openAPI, methodResponse); Schema responseSchema = unaliasSchema(ModelUtils.getSchemaFromResponse(openAPI, response)); @@ -4690,9 +4685,9 @@ protected void handleMethodResponse(Operation operation, */ @Override public CodegenOperation fromOperation(String path, - String httpMethod, - Operation operation, - List servers) { + String httpMethod, + Operation operation, + List servers) { LOGGER.debug("fromOperation => operation: {}", operation); if (operation == null) throw new RuntimeException("operation cannot be null in fromOperation"); @@ -4760,8 +4755,8 @@ public CodegenOperation fromOperation(String path, r.setContent(getContent(response.getContent(), imports, mediaTypeSchemaSuffix)); if (r.baseType != null && - !defaultIncludes.contains(r.baseType) && - !languageSpecificPrimitives.contains(r.baseType)) { + !defaultIncludes.contains(r.baseType) && + !languageSpecificPrimitives.contains(r.baseType)) { imports.add(r.baseType); } @@ -4771,26 +4766,26 @@ public CodegenOperation fromOperation(String path, } op.responses.add(r); - if (Boolean.TRUE.equals(r.isBinary) && Boolean.TRUE.equals(r.is2xx) && Boolean.FALSE.equals(op.isResponseBinary)) { + if (r.isBinary && r.is2xx && !op.isResponseBinary) { op.isResponseBinary = Boolean.TRUE; } - if (Boolean.TRUE.equals(r.isFile) && Boolean.TRUE.equals(r.is2xx) && Boolean.FALSE.equals(op.isResponseFile)) { + if (r.isFile && r.is2xx && !op.isResponseFile) { op.isResponseFile = Boolean.TRUE; } - if (Boolean.TRUE.equals(r.isDefault)) { + if (r.isDefault) { op.defaultReturnType = Boolean.TRUE; } // check if any 4xx or 5xx response has an error response object defined - if ((Boolean.TRUE.equals(r.is4xx) || Boolean.TRUE.equals(r.is5xx)) && - Boolean.FALSE.equals(r.primitiveType) && Boolean.FALSE.equals(r.simpleType)) { + if ((r.is4xx || r.is5xx) && + !r.primitiveType && !r.simpleType) { op.hasErrorResponseObject = Boolean.TRUE; } } // check if the operation can both return a 2xx response with a body and without if (op.responses.stream().anyMatch(response -> response.is2xx && response.dataType != null) && - op.responses.stream().anyMatch(response -> response.is2xx && response.dataType == null)) { + op.responses.stream().anyMatch(response -> response.is2xx && response.dataType == null)) { op.isResponseOptional = Boolean.TRUE; } @@ -4863,8 +4858,8 @@ public CodegenOperation fromOperation(String path, contentType = contentType.toLowerCase(Locale.ROOT); } if (contentType != null && - ((!(this instanceof RustAxumServerCodegen) && contentType.startsWith("application/x-www-form-urlencoded")) || - contentType.startsWith("multipart"))) { + ((!(this instanceof RustAxumServerCodegen) && contentType.startsWith("application/x-www-form-urlencoded")) || + contentType.startsWith("multipart"))) { // process form parameters formParams = fromRequestBodyToFormParameters(requestBody, imports); op.isMultipart = contentType.startsWith("multipart"); @@ -5054,23 +5049,23 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) { r.code = responseCode; switch (r.code.charAt(0)) { - case '1': - r.is1xx = true; - break; - case '2': - r.is2xx = true; - break; - case '3': - r.is3xx = true; - break; - case '4': - r.is4xx = true; - break; - case '5': - r.is5xx = true; - break; - default: - throw new RuntimeException("Invalid response code " + responseCode); + case '1': + r.is1xx = true; + break; + case '2': + r.is2xx = true; + break; + case '3': + r.is3xx = true; + break; + case '4': + r.is4xx = true; + break; + case '5': + r.is5xx = true; + break; + default: + throw new RuntimeException("Invalid response code " + responseCode); } } @@ -5341,7 +5336,7 @@ protected void updateParameterForString(CodegenParameter codegenParameter, Schem codegenParameter.isDecimal = true; codegenParameter.isPrimitiveType = true; } - if (Boolean.TRUE.equals(codegenParameter.isString)) { + if (codegenParameter.isString) { codegenParameter.isPrimitiveType = true; } } @@ -5534,7 +5529,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) } CodegenProperty codegenProperty = fromProperty(parameter.getName(), parameterSchema, false); - if (Boolean.TRUE.equals(codegenProperty.isModel)) { + if (codegenProperty.isModel) { codegenParameter.isModel = true; } @@ -5879,7 +5874,7 @@ protected String getOrGenerateOperationId(Operation operation, String path, Stri */ protected boolean needToImport(String type) { return StringUtils.isNotBlank(type) && !defaultIncludes.contains(type) - && !languageSpecificPrimitives.contains(type); + && !languageSpecificPrimitives.contains(type); } @SuppressWarnings("static-method") @@ -6088,7 +6083,7 @@ protected Map unaliasPropertySchema(Map properti } protected void addVars(CodegenModel m, Map properties, List required, - Map allProperties, List allRequired) { + Map allProperties, List allRequired) { m.hasRequired = false; m.hasReadOnly = false; @@ -6169,7 +6164,7 @@ protected void addVars(IJsonSchemaValidationProperties m, List } else { final CodegenProperty cp; - if (cm != null && cm.allVars == vars && varsMap.keySet().contains(key)) { + if (cm != null && cm.allVars == vars && varsMap.containsKey(key)) { // when updating allVars, reuse the codegen property from the child model if it's already present // the goal is to avoid issues when the property is defined in both child, parent but the // definition is not identical, e.g. required vs optional, integer vs string @@ -6204,18 +6199,18 @@ protected void addVars(IJsonSchemaValidationProperties m, List } // set model's hasOnlyReadOnly to false if the property is read-only - if (!Boolean.TRUE.equals(cp.isReadOnly)) { + if (!cp.isReadOnly) { cm.hasOnlyReadOnly = false; } addImportsForPropertyType(cm, cp); // if required, add to the list "requiredVars" - if (Boolean.FALSE.equals(cp.required)) { + if (!cp.required) { cm.optionalVars.add(cp); } // if readonly, add to readOnlyVars (list of properties) - if (Boolean.TRUE.equals(cp.isReadOnly)) { + if (cp.isReadOnly) { cm.readOnlyVars.add(cp); cm.hasReadOnly = true; } else { // else add to readWriteVars (list of properties) @@ -6223,12 +6218,11 @@ protected void addVars(IJsonSchemaValidationProperties m, List cm.readWriteVars.add(cp); } - if (Boolean.FALSE.equals(cp.isNullable)) { + if (!cp.isNullable) { cm.nonNullableVars.add(cp); } } } - return; } /** @@ -6291,7 +6285,7 @@ private Boolean isAliasOfSimpleTypes(Schema schema) { // allOf with a single item if (schema.getAllOf() != null && schema.getAllOf().size() == 1 - && schema.getAllOf().get(0) instanceof Schema) { + && schema.getAllOf().get(0) instanceof Schema) { schema = unaliasSchema((Schema) schema.getAllOf().get(0)); schema = ModelUtils.getReferencedSchema(this.openAPI, schema); } @@ -6337,7 +6331,7 @@ protected String removeNonNameElementToCamelCase(final String name, final String * Not all operating systems support case-sensitive paths */ private String uniqueCaseInsensitiveString(String value, Map seenValues) { - if (seenValues.keySet().contains(value)) { + if (seenValues.containsKey(value)) { return seenValues.get(value); } @@ -6770,7 +6764,7 @@ public String sanitizeName(final String name, String removeCharRegEx, ArrayList< } private String sanitizeValue(String value, String replaceMatch, String replaceValue, List exceptionList) { - if (exceptionList.size() == 0 || !exceptionList.contains(replaceMatch)) { + if (exceptionList.isEmpty() || !exceptionList.contains(replaceMatch)) { return value.replaceAll(replaceMatch, replaceValue); } return value; @@ -6813,65 +6807,65 @@ public void setParameterBooleanFlagWithCodegenProperty(CodegenParameter paramete LOGGER.error("Codegen Property cannot be null."); return; } - if (Boolean.TRUE.equals(property.isEmail) && Boolean.TRUE.equals(property.isString)) { + if (property.isEmail && property.isString) { parameter.isEmail = true; - } else if (Boolean.TRUE.equals(property.isPassword) && Boolean.TRUE.equals(property.isString)) { + } else if (property.isPassword && property.isString) { parameter.isPassword = true; - } else if (Boolean.TRUE.equals(property.isUuid) && Boolean.TRUE.equals(property.isString)) { + } else if (property.isUuid && property.isString) { parameter.isUuid = true; - } else if (Boolean.TRUE.equals(property.isByteArray)) { + } else if (property.isByteArray) { parameter.isByteArray = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isBinary)) { + } else if (property.isBinary) { parameter.isBinary = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isString)) { + } else if (property.isString) { parameter.isString = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isBoolean)) { + } else if (property.isBoolean) { parameter.isBoolean = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isLong)) { + } else if (property.isLong) { parameter.isLong = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isInteger)) { + } else if (property.isInteger) { parameter.isInteger = true; parameter.isPrimitiveType = true; - if (Boolean.TRUE.equals(property.isShort)) { + if (property.isShort) { parameter.isShort = true; - } else if (Boolean.TRUE.equals(property.isUnboundedInteger)) { + } else if (property.isUnboundedInteger) { parameter.isUnboundedInteger = true; } - } else if (Boolean.TRUE.equals(property.isDouble)) { + } else if (property.isDouble) { parameter.isDouble = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isFloat)) { + } else if (property.isFloat) { parameter.isFloat = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isDecimal)) { + } else if (property.isDecimal) { parameter.isDecimal = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isNumber)) { + } else if (property.isNumber) { parameter.isNumber = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isDate)) { + } else if (property.isDate) { parameter.isDate = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isDateTime)) { + } else if (property.isDateTime) { parameter.isDateTime = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isFreeFormObject)) { + } else if (property.isFreeFormObject) { parameter.isFreeFormObject = true; - } else if (Boolean.TRUE.equals(property.isAnyType)) { + } else if (property.isAnyType) { parameter.isAnyType = true; } else { LOGGER.debug("Property type is not primitive: {}", property.dataType); } - if (Boolean.TRUE.equals(property.isFile)) { + if (property.isFile) { parameter.isFile = true; } - if (Boolean.TRUE.equals(property.isModel)) { + if (property.isModel) { parameter.isModel = true; } } @@ -7005,7 +6999,7 @@ protected void postProcessEnumVars(List> enumVars) { long count = enumVars.stream().filter(v1 -> v1.get("name").equals(name)).count(); if (count > 1) { String uniqueEnumName = getUniqueEnumName(name, enumVars); - LOGGER.debug("Changing duplicate enumeration name from " + v.get("name") + " to " + uniqueEnumName); + LOGGER.debug("Changing duplicate enumeration name from {} to {}", v.get("name"), uniqueEnumName); v.put("name", uniqueEnumName); } }); @@ -7309,8 +7303,8 @@ public boolean hasFormParameter(Operation operation) { for (String consume : consumesInfo) { if (consume != null && - (consume.toLowerCase(Locale.ROOT).startsWith("application/x-www-form-urlencoded") || - consume.toLowerCase(Locale.ROOT).startsWith("multipart"))) { + (consume.toLowerCase(Locale.ROOT).startsWith("application/x-www-form-urlencoded") || + consume.toLowerCase(Locale.ROOT).startsWith("multipart"))) { return true; } } @@ -7429,7 +7423,7 @@ public List fromRequestBodyToFormParameters(RequestBody body, Schema original = null; // check if it's allOf (only 1 sub schema) with or without default/nullable/etc set in the top level if (ModelUtils.isAllOf(schema) && schema.getAllOf().size() == 1 && - (ModelUtils.getType(schema) == null || "object".equals(ModelUtils.getType(schema)))) { + (ModelUtils.getType(schema) == null || "object".equals(ModelUtils.getType(schema)))) { if (schema.getAllOf().get(0) instanceof Schema) { original = schema; schema = (Schema) schema.getAllOf().get(0); @@ -7550,7 +7544,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set codegenParameter.isDecimal = true; codegenParameter.isPrimitiveType = true; } - if (Boolean.TRUE.equals(codegenParameter.isString)) { + if (codegenParameter.isString) { codegenParameter.isPrimitiveType = true; } } else if (ModelUtils.isBooleanSchema(ps)) { @@ -7580,7 +7574,6 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set codegenParameter.setAdditionalPropertiesIsAnyType(codegenProperty.getAdditionalPropertiesIsAnyType()); codegenParameter.items = codegenProperty.items; codegenParameter.isPrimitiveType = false; - codegenParameter.items = codegenProperty.items; codegenParameter.mostInnerItems = codegenProperty.mostInnerItems; } else if (ModelUtils.isFreeFormObject(ps, openAPI)) { codegenParameter.isFreeFormObject = true; @@ -7633,7 +7626,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set // referenced schemas } - if (Boolean.TRUE.equals(codegenProperty.isModel)) { + if (codegenProperty.isModel) { codegenParameter.isModel = true; } @@ -7721,9 +7714,9 @@ protected void addBodyModelSchema(CodegenParameter codegenParameter, String name codegenModelDescription = codegenModel.description; } else { LOGGER.warn("The following schema has undefined (null) baseType. " + - "It could be due to form parameter defined in OpenAPI v2 spec with incorrect consumes. " + - "A correct 'consumes' for form parameters should be " + - "'application/x-www-form-urlencoded' or 'multipart/?'"); + "It could be due to form parameter defined in OpenAPI v2 spec with incorrect consumes. " + + "A correct 'consumes' for form parameters should be " + + "'application/x-www-form-urlencoded' or 'multipart/?'"); LOGGER.warn("schema: {}", schema); LOGGER.warn("codegenModel is null. Default to UNKNOWN_BASE_TYPE"); codegenModelName = "UNKNOWN_BASE_TYPE"; @@ -8005,8 +7998,8 @@ protected LinkedHashMap getContent(Content content, Se enc.getContentType(), headers, enc.getStyle().toString(), - enc.getExplode() == null ? false : enc.getExplode().booleanValue(), - enc.getAllowReserved() == null ? false : enc.getAllowReserved().booleanValue() + enc.getExplode() != null && enc.getExplode(), + enc.getAllowReserved() != null && enc.getAllowReserved() ); if (enc.getExtensions() != null) { @@ -8081,7 +8074,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S Schema original = null; // check if it's allOf (only 1 sub schema) with or without default/nullable/etc set in the top level if (ModelUtils.isAllOf(schema) && schema.getAllOf().size() == 1 && - (ModelUtils.getType(schema) == null || "object".equals(ModelUtils.getType(schema)))) { + (ModelUtils.getType(schema) == null || "object".equals(ModelUtils.getType(schema)))) { if (schema.getAllOf().get(0) instanceof Schema) { original = schema; schema = (Schema) schema.getAllOf().get(0); @@ -8233,9 +8226,8 @@ protected void addRequiredVarsMap(Schema schema, IJsonSchemaValidationProperties break; } } - if (found == false) { + if (!found) { LOGGER.warn("Property {} is not processed correctly (missing from getVars). Maybe it's a const (not yet supported) in openapi v3.1 spec.", requiredPropertyName); - continue; } } else if (schema.getAdditionalProperties() instanceof Boolean && Boolean.FALSE.equals(schema.getAdditionalProperties())) { // TODO add processing for requiredPropertyName @@ -8469,7 +8461,7 @@ protected boolean executePostProcessor(String[] commandArr) { int exitValue = p.exitValue(); if (exitValue != 0) { try (InputStreamReader inputStreamReader = new InputStreamReader(p.getErrorStream(), StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(inputStreamReader)) { + BufferedReader br = new BufferedReader(inputStreamReader)) { StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { @@ -8580,7 +8572,7 @@ public void setRemoveEnumValuePrefix(final boolean removeEnumValuePrefix) { * @param name name of the parent oneOf schema */ public void addOneOfNameExtension(Schema schema, String name) { - if (schema.getOneOf() != null && schema.getOneOf().size() > 0) { + if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) { schema.addExtension(X_ONE_OF_NAME, name); } } @@ -8661,8 +8653,8 @@ public boolean equals(Object o) { return false; SanitizeNameOptions that = (SanitizeNameOptions) o; return Objects.equals(getName(), that.getName()) && - Objects.equals(getRemoveCharRegEx(), that.getRemoveCharRegEx()) && - Objects.equals(getExceptions(), that.getExceptions()); + Objects.equals(getRemoveCharRegEx(), that.getRemoveCharRegEx()) && + Objects.equals(getExceptions(), that.getExceptions()); } @Override @@ -8768,7 +8760,7 @@ private List getComposedProperties(List xOfCollection, i += 1; if (dataTypeSet.contains(cp.dataType) - || (isTypeErasedGenerics() && dataTypeSet.contains(cp.baseType))) { + || (isTypeErasedGenerics() && dataTypeSet.contains(cp.baseType))) { // add "x-duplicated-data-type" to indicate if the (base) dataType already occurs before // in other sub-schemas of allOf/anyOf/oneOf cp.vendorExtensions.putIfAbsent("x-duplicated-data-type", true); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java index 9bb9332f4ac8..05bcf62bbe54 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java @@ -111,13 +111,12 @@ public TemplateSource sourceAt(String location) { cachedExecutor = executor; } - Template tmpl = compiledTemplateCache.computeIfAbsent(templateFile, tf -> { - try { - return cachedHandlebars.compile(tf); - } catch (IOException e) { - throw new RuntimeException("Failed to compile Handlebars template: " + tf, e); - } - }); + Template tmpl = compiledTemplateCache.get(templateFile); + if (tmpl == null) { + // compile() declares throws IOException — propagate it directly without wrapping. + tmpl = cachedHandlebars.compile(templateFile); + compiledTemplateCache.put(templateFile, tmpl); + } return tmpl.apply(context); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java index 81b24bebddc9..fb5da53f2151 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java @@ -54,10 +54,18 @@ public String getIdentifier() { /** * Cache of template file name -> compiled Template object. - * Templates are stateless after compilation and safe to reuse across invocations. + * Templates are stateless after compilation and safe to reuse across invocations + * for the same executor. The cache is invalidated whenever the executor changes. */ private final Map compiledTemplateCache = new ConcurrentHashMap<>(); + /** + * Tracks the executor whose template sources were used to populate {@link #compiledTemplateCache}. + * A different executor may resolve the same template name to different content (e.g. user-defined + * templates vs. built-ins, or different test fixtures), so the cache must be cleared on change. + */ + private volatile TemplatingExecutor cachedExecutor; + /** * Compiles a template into a string * @@ -69,12 +77,23 @@ public String getIdentifier() { */ @Override public String compileTemplate(TemplatingExecutor executor, Map bundle, String templateFile) throws IOException { - // Compile once and cache; the compiled Template is stateless and reusable. - Template tmpl = compiledTemplateCache.computeIfAbsent(templateFile, tf -> - compiler - .withLoader(name -> findTemplate(executor, name)) - .defaultValue("") - .compile(executor.getFullTemplateContents(tf))); + // If the executor changed, a different set of template sources may be in use; invalidate + // stale compiled templates so we don't return results built from the old executor. + if (cachedExecutor != executor) { + compiledTemplateCache.clear(); + cachedExecutor = executor; + } + + // Manual get → compile → put so the correct (current) executor is always captured in + // the partial loader, and so that any compile-time exception propagates naturally. + Template tmpl = compiledTemplateCache.get(templateFile); + if (tmpl == null) { + tmpl = compiler + .withLoader(name -> findTemplate(executor, name)) + .defaultValue("") + .compile(executor.getFullTemplateContents(templateFile)); + compiledTemplateCache.put(templateFile, tmpl); + } StringWriter out = new StringWriter(); // the value of bundle[MUSTACHE_PARENT_CONTEXT] is used a parent content in mustache. From cecf84fc05dc42619436ee320839b16bd26415b1 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 16 Mar 2026 23:50:07 +0100 Subject: [PATCH 03/22] implement suggested fixes from CR. fix some general nitpicks. --- .../codegen/DefaultGenerator.java | 41 +++++----- .../openapitools/codegen/TemplateManager.java | 11 ++- .../GeneratorTemplateContentLocator.java | 24 +++++- .../templating/HandlebarsEngineAdapter.java | 81 ++++++++++--------- .../templating/MustacheEngineAdapter.java | 43 +++++----- 5 files changed, 117 insertions(+), 83 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 942f371f0c1a..2dc98ec1b7f9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -31,6 +31,7 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.openapitools.codegen.api.*; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; @@ -60,7 +61,6 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.removeStart; import static org.openapitools.codegen.CodegenConstants.X_INTERNAL; import static org.openapitools.codegen.utils.OnceLogger.once; @@ -86,16 +86,17 @@ public class DefaultGenerator implements Generator { private String basePath; private String basePathWithoutHost; private String contextPath; - private Map generatorPropertyDefaults = new HashMap<>(); + private final Map generatorPropertyDefaults = new HashMap<>(); /** * Retrieves an instance to the configured template processor, available after user-defined options are * applied via */ - @Getter protected TemplateProcessor templateProcessor = null; + @Getter + protected TemplateProcessor templateProcessor = null; private List userDefinedTemplates = new ArrayList<>(); - private String generatorCheck = "spring"; - private String templateCheck = "apiController.mustache"; + private final String generatorCheck = "spring"; + private final String templateCheck = "apiController.mustache"; public DefaultGenerator() { @@ -266,8 +267,7 @@ void configureGeneratorProperties() { openapiNormalizer.normalize(); } } catch (Exception e) { - LOGGER.error("An exception occurred in OpenAPI Normalizer. Please report the issue via https://github.com/openapitools/openapi-generator/issues/new/: "); - e.printStackTrace(); + LOGGER.error("An exception occurred in OpenAPI Normalizer. Please report the issue via https://github.com/openapitools/openapi-generator/issues/new/: ", e); } // resolve inline models @@ -607,10 +607,10 @@ private void generateModelsForVariable(List files, List allModel if (!processedModels.contains(key) && allSchemas.containsKey(key)) { generateModels(files, allModels, unusedModels, aliasModels, processedModels, () -> Set.of(key)); } else { - LOGGER.info("Type " + variable.getComplexType() + " of variable " + variable.getName() + " could not be resolve because it is not declared as a model."); + LOGGER.info("Type {} of variable {} could not be resolve because it is not declared as a model.", variable.getComplexType(), variable.getName()); } } else { - LOGGER.info("Type " + variable.getOpenApiType() + " of variable " + variable.getName() + " could not be resolve because it is not declared as a model."); + LOGGER.info("Type {} of variable {} could not be resolve because it is not declared as a model.", variable.getOpenApiType(), variable.getName()); } } @@ -639,8 +639,8 @@ private Set getPropertyAsSet(String propertyName) { } return Arrays.stream(propertyRaw.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); + .map(String::trim) + .collect(Collectors.toSet()); } private Set modelKeys() { @@ -665,7 +665,6 @@ private Set modelKeys() { return modelKeys; } - @SuppressWarnings("unchecked") void generateApis(List files, List allOperations, List allModels) { if (!generateApis) { // TODO: Process these anyway and present info via dryRun? @@ -1006,7 +1005,7 @@ private void generateOpenapiGeneratorIgnoreFile() { File ignoreFile = new File(ignoreFileNameTarget); // use the entries provided by the users to pre-populate .openapi-generator-ignore try { - LOGGER.info("Writing file " + ignoreFileNameTarget + " (which is always overwritten when the option `openapiGeneratorIgnoreFile` is enabled.)"); + LOGGER.info("Writing file {} (which is always overwritten when the option `openapiGeneratorIgnoreFile` is enabled.)", ignoreFileNameTarget); new File(config.outputFolder()).mkdirs(); if (!ignoreFile.createNewFile()) { // file may already exist, do nothing @@ -1393,7 +1392,7 @@ private void processUserDefinedTemplates() { // hack: destination filename in this scenario might be a suffix like Impl.java templateExt = userDefinedTemplate.getDestinationFilename(); } else { - templateExt = StringUtils.prependIfMissing(templateExt, "."); + templateExt = Strings.CS.prependIfMissing(templateExt, "."); } String templateOutputFolder = userDefinedTemplate.getFolder(); if (!templateOutputFolder.isEmpty()) { @@ -1430,7 +1429,9 @@ protected File processTemplateToFile(Map templateData, String te return processTemplateToFile(templateData, templateName, outputFilename, shouldGenerate, skippedByOption, this.config.getOutputDir()); } - /** Stores lowercased absolute paths for O(1) case-insensitive duplicate detection. */ + /** + * Stores lowercased absolute paths for O(1) case-insensitive duplicate detection. + */ private final Set seenFilesLower = new HashSet<>(); private File processTemplateToFile(Map templateData, String templateName, String outputFilename, boolean shouldGenerate, String skippedByOption, String intendedOutputDir) throws IOException { @@ -1993,7 +1994,7 @@ private void generateFilesMetadata(List files) { // Some implementations make the output ./c/d which seems to mix the logic // as documented for symlinks. So we need to trim any / or ./ from the start, // as nobody should be generating into system root and our expectation is no ./ - String relativePath = removeStart(removeStart(f.toString(), "." + File.separator), File.separator); + String relativePath = Strings.CS.removeStart(Strings.CS.removeStart(f.toString(), "." + File.separator), File.separator); if (File.separator.equals("\\")) { // ensure that windows outputs same FILES format relativePath = relativePath.replace(File.separator, "/"); @@ -2003,10 +2004,8 @@ private void generateFilesMetadata(List files) { } }); - Collections.sort(relativePaths, (a, b) -> IOCase.SENSITIVE.checkCompareTo(a, b)); - relativePaths.forEach(relativePath -> { - sb.append(relativePath).append(System.lineSeparator()); - }); + relativePaths.sort(IOCase.SENSITIVE::checkCompareTo); + relativePaths.forEach(relativePath -> sb.append(relativePath).append(System.lineSeparator())); String targetFile = config.outputFolder() + File.separator + METADATA_DIR + File.separator + config.getFilesMetadataFilename(); @@ -2021,7 +2020,7 @@ private void generateFilesMetadata(List files) { } private String removeTrailingSlash(String value) { - return StringUtils.removeEnd(value, "/"); + return Strings.CS.removeEnd(value, "/"); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java index c347cb3f3526..b0ef14ca549c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/TemplateManager.java @@ -94,6 +94,13 @@ public Path getFullTemplatePath(String name) { return Paths.get(getFullTemplateFile(name)); } + /** + * Pre-compiled pattern for replacing the OS file separator with '/' in classpath resource paths. + * Only non-null on operating systems where {@link File#separator} is not already '/'. + */ + private static final Pattern FILE_SEP_PATTERN = + "/".equals(File.separator) ? null : Pattern.compile(Pattern.quote(File.separator)); + /** * Gets a normalized classpath resource location according to OS-specific file separator * @@ -101,8 +108,8 @@ public Path getFullTemplatePath(String name) { * @return A normalized string according to OS-specific file separator */ public static String getCPResourcePath(final String name) { - if (!"/".equals(File.separator)) { - return name.replaceAll(Pattern.quote(File.separator), "/"); + if (FILE_SEP_PATTERN != null) { + return FILE_SEP_PATTERN.matcher(name).replaceAll("/"); } return name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java index ef1d8500b5b1..62b9ca91887a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java @@ -7,6 +7,8 @@ import java.io.File; import java.nio.file.Paths; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; /** * Locates templates according to {@link CodegenConfig} settings. @@ -14,6 +16,13 @@ public class GeneratorTemplateContentLocator implements TemplatePathLocator { private final CodegenConfig codegenConfig; + /** + * Cache of relativeTemplateFile -> resolved full path (or empty Optional when the template does not exist). + * The filesystem/classpath existence probes inside resolveFullTemplatePath are expensive on repeated calls + * for the same template name, so we memoize the result for the lifetime of this locator instance. + */ + private final ConcurrentHashMap> templatePathCache = new ConcurrentHashMap<>(); + /** * Constructs a new instance of {@link GeneratorTemplateContentLocator} for the provided {@link CodegenConfig} * @@ -51,12 +60,25 @@ private boolean classpathTemplateExists(String name) { * 4) (embedded template dir) *

* Where "template dir" may be user defined and "embedded template dir" are the built-in templates for the given generator. + *

+ * Results are cached per {@code relativeTemplateFile} name because the filesystem/classpath probes are expensive + * and the outcome is constant for the lifetime of this locator instance. * * @param relativeTemplateFile Template file - * @return String Full template file path + * @return String Full template file path, or {@code null} if the template does not exist in any location */ @Override public String getFullTemplatePath(String relativeTemplateFile) { + return templatePathCache + .computeIfAbsent(relativeTemplateFile, key -> Optional.ofNullable(resolveFullTemplatePath(key))) + .orElse(null); + } + + /** + * Performs the actual filesystem/classpath probes to find the full template path. + * Called at most once per unique {@code relativeTemplateFile} value; all subsequent lookups use the cache. + */ + private String resolveFullTemplatePath(String relativeTemplateFile) { CodegenConfig config = this.codegenConfig; //check the supplied template library folder for the file diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java index 05bcf62bbe54..f07006a4c13e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java @@ -38,7 +38,6 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -49,20 +48,24 @@ public class HandlebarsEngineAdapter extends AbstractTemplatingEngineAdapter { // We use this as a simple lookup for valid file name extensions. This adapter will inspect .mustache (built-in) and infer the relevant handlebars filename private final String[] canCompileFromExtensions = {".handlebars", ".hbs", ".mustache"}; private boolean infiniteLoops = false; - @Setter private boolean prettyPrint = false; + @Setter + private boolean prettyPrint = false; /** - * Cache of (templateFile -> compiled Template). Compiled Handlebars templates are stateless after - * compilation and safe to reuse across multiple data-bundle invocations within the same run. + * Per-executor cache of fully-configured {@link Handlebars} engine instances. + * Each executor gets its own engine because the engine's {@link TemplateLoader} closes over the + * executor; sharing an engine across executors would silently resolve templates from the wrong source. + * {@link ConcurrentHashMap#computeIfAbsent} ensures the engine is built at most once per executor. */ - private final Map compiledTemplateCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap engineCache = new ConcurrentHashMap<>(); /** - * Cached Handlebars engine instance with all helpers pre-registered. - * Re-created lazily whenever the TemplatingExecutor changes (different template paths). + * Per-executor cache of compiled {@link Template} objects. + * Keying on the executor instance eliminates the non-atomic check-clear-update invalidation + * that the previous single-cache approach required; no state ever needs to be cleared. */ - private volatile Handlebars cachedHandlebars; - private volatile TemplatingExecutor cachedExecutor; + private final ConcurrentHashMap> templateCaches = + new ConcurrentHashMap<>(); /** * Provides an identifier used to load the adapter. This could be a name, uuid, or any other string. @@ -86,40 +89,44 @@ public String compileTemplate(TemplatingExecutor executor, AccessAwareFieldValueResolver.INSTANCE) .build(); - // Reuse the Handlebars engine when the executor hasn't changed; rebuild otherwise. - if (cachedHandlebars == null || cachedExecutor != executor) { - TemplateLoader loader = new AbstractTemplateLoader() { - @Override - public TemplateSource sourceAt(String location) { - return findTemplate(executor, location); - } - }; - Handlebars handlebars = new Handlebars(loader); - handlebars.registerHelperMissing((obj, options) -> { - LOGGER.warn(String.format(Locale.ROOT, "Unregistered helper name '%s', processing template:%n%s", options.helperName, options.fn.text())); - return ""; - }); - handlebars.registerHelper("json", Jackson2Helper.INSTANCE); - StringHelpers.register(handlebars); - handlebars.registerHelpers(ConditionalHelpers.class); - handlebars.registerHelpers(org.openapitools.codegen.templating.handlebars.StringHelpers.class); - handlebars.setInfiniteLoops(infiniteLoops); - handlebars.setPrettyPrint(prettyPrint); - // Changing the executor means template content may differ; clear stale entries. - compiledTemplateCache.clear(); - cachedHandlebars = handlebars; - cachedExecutor = executor; - } + // Each executor gets its own Handlebars engine (the loader closes over the executor) and its + // own compiled-template cache. computeIfAbsent is atomic, so concurrent calls with the same + // executor share one engine/cache rather than racing to create duplicates. + Handlebars handlebars = engineCache.computeIfAbsent(executor, this::buildHandlebars); + ConcurrentHashMap cache = + templateCaches.computeIfAbsent(executor, k -> new ConcurrentHashMap<>()); - Template tmpl = compiledTemplateCache.get(templateFile); + // Manual get → compile → put so IOException propagates naturally. + Template tmpl = cache.get(templateFile); if (tmpl == null) { - // compile() declares throws IOException — propagate it directly without wrapping. - tmpl = cachedHandlebars.compile(templateFile); - compiledTemplateCache.put(templateFile, tmpl); + tmpl = handlebars.compile(templateFile); + cache.put(templateFile, tmpl); } return tmpl.apply(context); } + /** Constructs and fully configures a {@link Handlebars} engine for the given executor. */ + private Handlebars buildHandlebars(TemplatingExecutor executor) { + TemplateLoader loader = new AbstractTemplateLoader() { + @Override + public TemplateSource sourceAt(String location) { + return findTemplate(executor, location); + } + }; + Handlebars handlebars = new Handlebars(loader); + handlebars.registerHelperMissing((obj, options) -> { + LOGGER.warn("Unregistered helper name '{}', processing template:\n{}", options.helperName, options.fn.text()); + return ""; + }); + handlebars.registerHelper("json", Jackson2Helper.INSTANCE); + StringHelpers.register(handlebars); + handlebars.registerHelpers(ConditionalHelpers.class); + handlebars.registerHelpers(org.openapitools.codegen.templating.handlebars.StringHelpers.class); + handlebars.setInfiniteLoops(infiniteLoops); + handlebars.setPrettyPrint(prettyPrint); + return handlebars; + } + @SuppressWarnings("java:S108") public TemplateSource findTemplate(TemplatingExecutor generator, String templateFile) { String[] possibilities = getModifiedFileLocation(templateFile); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java index fb5da53f2151..fff6b7441ebf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java @@ -53,18 +53,18 @@ public String getIdentifier() { Mustache.Compiler compiler = Mustache.compiler(); /** - * Cache of template file name -> compiled Template object. - * Templates are stateless after compilation and safe to reuse across invocations - * for the same executor. The cache is invalidated whenever the executor changes. + * Per-executor cache of template file name → compiled {@link Template}. + *

+ * Keying on the executor instance eliminates the non-atomic check-clear-update invalidation pattern + * that the previous single-cache approach required. Each executor gets its own independent inner + * map, so different executors (e.g. different generator runs, test fixtures) can never observe + * each other's compiled templates, and no state ever needs to be cleared. + *

+ * {@link ConcurrentHashMap#computeIfAbsent} guarantees that the inner map for a given executor + * is created exactly once even under concurrent access. */ - private final Map compiledTemplateCache = new ConcurrentHashMap<>(); - - /** - * Tracks the executor whose template sources were used to populate {@link #compiledTemplateCache}. - * A different executor may resolve the same template name to different content (e.g. user-defined - * templates vs. built-ins, or different test fixtures), so the cache must be cleared on change. - */ - private volatile TemplatingExecutor cachedExecutor; + private final ConcurrentHashMap> compiledTemplateCaches = + new ConcurrentHashMap<>(); /** * Compiles a template into a string @@ -77,22 +77,21 @@ public String getIdentifier() { */ @Override public String compileTemplate(TemplatingExecutor executor, Map bundle, String templateFile) throws IOException { - // If the executor changed, a different set of template sources may be in use; invalidate - // stale compiled templates so we don't return results built from the old executor. - if (cachedExecutor != executor) { - compiledTemplateCache.clear(); - cachedExecutor = executor; - } - - // Manual get → compile → put so the correct (current) executor is always captured in - // the partial loader, and so that any compile-time exception propagates naturally. - Template tmpl = compiledTemplateCache.get(templateFile); + // Each executor gets its own compiled-template cache. computeIfAbsent is atomic, so two threads + // racing on the same executor key will share one inner map rather than creating two separate ones. + ConcurrentHashMap cache = + compiledTemplateCaches.computeIfAbsent(executor, k -> new ConcurrentHashMap<>()); + + // Manual get → compile → put so IOException propagates naturally. + // At worst, two threads compile the same template simultaneously; the last writer wins, + // which is harmless because compilation is pure/deterministic. + Template tmpl = cache.get(templateFile); if (tmpl == null) { tmpl = compiler .withLoader(name -> findTemplate(executor, name)) .defaultValue("") .compile(executor.getFullTemplateContents(templateFile)); - compiledTemplateCache.put(templateFile, tmpl); + cache.put(templateFile, tmpl); } StringWriter out = new StringWriter(); From ae35597b6d36b0df45a0370579b2f383b87ef6fb Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 00:02:43 +0100 Subject: [PATCH 04/22] implement suggested fixes from CR. fix some general nitpicks. --- .../openapitools/codegen/DefaultCodegen.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 61b3e536c35a..1ff331cbe6dd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -79,6 +79,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -248,6 +249,14 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected final static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application/json(;.*)?"); protected final static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application/vnd.(.*)+json(;.*)?"); private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); + /** Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. */ + private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); + /** + * Cache of removeCharRegEx string → compiled {@link Pattern} with {@link Pattern#UNICODE_CHARACTER_CLASS}. + * {@code sanitizeName} is called once per unique (name, regex, exceptions) tuple, so the regex string + * (almost always {@code "\\W"}) would otherwise be recompiled for every unique property/model name. + */ + private static final ConcurrentHashMap REMOVE_CHAR_UNICODE_PATTERN_CACHE = new ConcurrentHashMap<>(); /** * True if the code generator supports multiple class inheritance. @@ -6009,8 +6018,7 @@ protected void addParentContainer(CodegenModel model, String name, Schema schema * @return The next name for the base name */ private static String generateNextName(String name) { - Pattern pattern = Pattern.compile("\\d+\\z"); - Matcher matcher = pattern.matcher(name); + Matcher matcher = TRAILING_DIGITS.matcher(name); if (matcher.find()) { String numStr = matcher.group(); int num = Integer.parseInt(numStr) + 1; @@ -6755,7 +6763,10 @@ public String sanitizeName(final String name, String removeCharRegEx, ArrayList< // remove everything else other than word, number and _ // $php_variable => php_variable if (allowUnicodeIdentifiers) { //could be converted to a single line with ?: operator - modifiable = Pattern.compile(sanitizeNameOptions.getRemoveCharRegEx(), Pattern.UNICODE_CHARACTER_CLASS).matcher(modifiable).replaceAll(""); + modifiable = REMOVE_CHAR_UNICODE_PATTERN_CACHE + .computeIfAbsent(sanitizeNameOptions.getRemoveCharRegEx(), + regex -> Pattern.compile(regex, Pattern.UNICODE_CHARACTER_CLASS)) + .matcher(modifiable).replaceAll(""); } else { modifiable = modifiable.replaceAll(sanitizeNameOptions.getRemoveCharRegEx(), ""); } From 3b8e4c28389f62817bf22fe23a6f417248106da1 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 07:53:58 +0100 Subject: [PATCH 05/22] revert some changes --- .../openapitools/codegen/DefaultCodegen.java | 40 ++++++++++--------- .../codegen/DefaultGenerator.java | 18 +++++---- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 1ff331cbe6dd..a720f49b4c18 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -603,9 +603,11 @@ public Map postProcessAllModels(Map objs) * @return */ private boolean codegenPropertyIsNew(CodegenModel model, CodegenProperty property) { - return model.parentModel != null && model.parentModel.allVars.stream().anyMatch(p -> + return model.parentModel == null + ? false + : model.parentModel.allVars.stream().anyMatch(p -> p.name.equals(property.name) && - (!p.dataType.equals(property.dataType) || !p.datatypeWithEnum.equals(property.datatypeWithEnum))); + (p.dataType.equals(property.dataType) == false || p.datatypeWithEnum.equals(property.datatypeWithEnum) == false)); } /** @@ -749,7 +751,7 @@ protected void removeSelfReferenceImports(CodegenModel model) { for (CodegenProperty cp : model.allVars) { // detect self import if (cp.dataType.equalsIgnoreCase(model.classname) || - (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { + (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { model.imports.remove(model.classname); // remove self import cp.isSelfReference = true; } @@ -791,7 +793,7 @@ private List getModelDependencies(List vars) { } private void setCircularReferencesOnProperties(final String root, - final Map> dependencyMap) { + final Map> dependencyMap) { dependencyMap.getOrDefault(root, new ArrayList<>()) .forEach(prop -> { final List unvisited = @@ -839,7 +841,7 @@ public ModelsMap postProcessModelsEnum(ModelsMap objs) { CodegenModel cm = mo.getModel(); // for enum model - if (cm.isEnum && cm.allowableValues != null) { + if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) { Map allowableValues = cm.allowableValues; List values = (List) allowableValues.get("values"); List> enumVars = buildEnumVars(values, cm.dataType); @@ -1130,7 +1132,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } else if (ModelUtils.isMapSchema(s)) { Schema addProps = ModelUtils.getAdditionalProperties(s); - if (ModelUtils.isComposedSchema(addProps)) { + if (addProps != null && ModelUtils.isComposedSchema(addProps)) { addOneOfNameExtension(addProps, nOneOf); addOneOfInterfaceModel(addProps, nOneOf); } @@ -2993,7 +2995,7 @@ private HashMap extractSchemaTestCases(String refToTestC } LinkedHashMap> testNameToTesCase = (LinkedHashMap>) schemaNameToTestCases.get(schemaName); for (Entry> entry : testNameToTesCase.entrySet()) { - LinkedHashMap testExample = entry.getValue(); + LinkedHashMap testExample = (LinkedHashMap) entry.getValue(); String nameInSnakeCase = toTestCaseName(entry.getKey()); Object data = processTestExampleData(testExample.get("data")); SchemaTestCase testCase = new SchemaTestCase( @@ -3555,7 +3557,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, } CodegenProperty df = discriminatorFound(composedSchemaName, sc, discPropName, new TreeSet()); String modelName = ModelUtils.getSimpleRef(ref); - if (df == null || !df.isString || !df.required) { + if (df == null || !df.isString || df.required != true) { String msgSuffix = ""; if (df == null) { msgSuffix += discPropName + " is missing from the schema, define it as required and type string"; @@ -3563,7 +3565,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, if (!df.isString) { msgSuffix += "invalid type for " + discPropName + ", set it to string"; } - if (!df.required) { + if (df.required != true) { String spacer = ""; if (msgSuffix.length() != 0) { spacer = ". "; @@ -3706,7 +3708,7 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch } } - if (!matched) { + if (matched == false) { uniqueDescendants.add(otherDescendant); } } @@ -4216,10 +4218,11 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo property.isContainer = true; if (ModelUtils.isSet(p)) { property.containerType = "set"; + property.containerTypeMapped = typeMapping.get(property.containerType); } else { property.containerType = "array"; - } property.containerTypeMapped = typeMapping.get(property.containerType); + } property.baseType = getSchemaType(p); // handle inner property @@ -4360,7 +4363,7 @@ void updateDefaultToEmptyContainer(CodegenProperty cp, Schema p) { * @param input a set of rules separated by `|` */ void parseDefaultToEmptyContainer(String input) { - String[] inputs = input.split("[|]"); + String[] inputs = ((String) input).split("[|]"); String containerType; for (String rule : inputs) { if (StringUtils.isEmpty(rule)) { @@ -4378,7 +4381,7 @@ void parseDefaultToEmptyContainer(String input) { LOGGER.error("Skipped invalid container type `{}` in `{}`.", containerType, input); } } else if (rule.startsWith("?")) { // nullable (required) - containerType = rule.substring(1); + containerType = rule.substring(1, rule.length()); if ("array".equalsIgnoreCase(containerType)) { arrayNullableDefaultToEmpty = true; } else if ("map".equalsIgnoreCase(containerType)) { @@ -6172,7 +6175,7 @@ protected void addVars(IJsonSchemaValidationProperties m, List } else { final CodegenProperty cp; - if (cm != null && cm.allVars == vars && varsMap.containsKey(key)) { + if (cm != null && cm.allVars == vars && varsMap.keySet().contains(key)) { // when updating allVars, reuse the codegen property from the child model if it's already present // the goal is to avoid issues when the property is defined in both child, parent but the // definition is not identical, e.g. required vs optional, integer vs string @@ -6339,7 +6342,7 @@ protected String removeNonNameElementToCamelCase(final String name, final String * Not all operating systems support case-sensitive paths */ private String uniqueCaseInsensitiveString(String value, Map seenValues) { - if (seenValues.containsKey(value)) { + if (seenValues.keySet().contains(value)) { return seenValues.get(value); } @@ -8009,8 +8012,8 @@ protected LinkedHashMap getContent(Content content, Se enc.getContentType(), headers, enc.getStyle().toString(), - enc.getExplode() != null && enc.getExplode(), - enc.getAllowReserved() != null && enc.getAllowReserved() + enc.getExplode() == null ? false : enc.getExplode().booleanValue(), + enc.getAllowReserved() == null ? false : enc.getAllowReserved().booleanValue() ); if (enc.getExtensions() != null) { @@ -8239,6 +8242,7 @@ protected void addRequiredVarsMap(Schema schema, IJsonSchemaValidationProperties } if (!found) { LOGGER.warn("Property {} is not processed correctly (missing from getVars). Maybe it's a const (not yet supported) in openapi v3.1 spec.", requiredPropertyName); + continue; } } else if (schema.getAdditionalProperties() instanceof Boolean && Boolean.FALSE.equals(schema.getAdditionalProperties())) { // TODO add processing for requiredPropertyName @@ -8583,7 +8587,7 @@ public void setRemoveEnumValuePrefix(final boolean removeEnumValuePrefix) { * @param name name of the parent oneOf schema */ public void addOneOfNameExtension(Schema schema, String name) { - if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) { + if (schema.getOneOf() != null && schema.getOneOf().size() > 0) { schema.addExtension(X_ONE_OF_NAME, name); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 2dc98ec1b7f9..855344cdb0c7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -86,7 +86,7 @@ public class DefaultGenerator implements Generator { private String basePath; private String basePathWithoutHost; private String contextPath; - private final Map generatorPropertyDefaults = new HashMap<>(); + private Map generatorPropertyDefaults = new HashMap<>(); /** * Retrieves an instance to the configured template processor, available after user-defined options are * applied via @@ -95,8 +95,8 @@ public class DefaultGenerator implements Generator { protected TemplateProcessor templateProcessor = null; private List userDefinedTemplates = new ArrayList<>(); - private final String generatorCheck = "spring"; - private final String templateCheck = "apiController.mustache"; + private String generatorCheck = "spring"; + private String templateCheck = "apiController.mustache"; public DefaultGenerator() { @@ -1392,7 +1392,7 @@ private void processUserDefinedTemplates() { // hack: destination filename in this scenario might be a suffix like Impl.java templateExt = userDefinedTemplate.getDestinationFilename(); } else { - templateExt = Strings.CS.prependIfMissing(templateExt, "."); + templateExt = StringUtils.prependIfMissing(templateExt, "."); } String templateOutputFolder = userDefinedTemplate.getFolder(); if (!templateOutputFolder.isEmpty()) { @@ -1994,7 +1994,7 @@ private void generateFilesMetadata(List files) { // Some implementations make the output ./c/d which seems to mix the logic // as documented for symlinks. So we need to trim any / or ./ from the start, // as nobody should be generating into system root and our expectation is no ./ - String relativePath = Strings.CS.removeStart(Strings.CS.removeStart(f.toString(), "." + File.separator), File.separator); + String relativePath = removeStart(removeStart(f.toString(), "." + File.separator), File.separator); if (File.separator.equals("\\")) { // ensure that windows outputs same FILES format relativePath = relativePath.replace(File.separator, "/"); @@ -2004,8 +2004,10 @@ private void generateFilesMetadata(List files) { } }); - relativePaths.sort(IOCase.SENSITIVE::checkCompareTo); - relativePaths.forEach(relativePath -> sb.append(relativePath).append(System.lineSeparator())); + Collections.sort(relativePaths, (a, b) -> IOCase.SENSITIVE.checkCompareTo(a, b)); + relativePaths.forEach(relativePath -> { + sb.append(relativePath).append(System.lineSeparator()); + }); String targetFile = config.outputFolder() + File.separator + METADATA_DIR + File.separator + config.getFilesMetadataFilename(); @@ -2020,7 +2022,7 @@ private void generateFilesMetadata(List files) { } private String removeTrailingSlash(String value) { - return Strings.CS.removeEnd(value, "/"); + return StringUtils.removeEnd(value, "/"); } } From 607c678874a344729201cfa80835e40133338b90 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 07:58:30 +0100 Subject: [PATCH 06/22] fix --- .../main/java/org/openapitools/codegen/DefaultGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 855344cdb0c7..f89068e7c065 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -31,7 +31,6 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Strings; import org.openapitools.codegen.api.*; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; @@ -61,6 +60,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.removeStart; import static org.openapitools.codegen.CodegenConstants.X_INTERNAL; import static org.openapitools.codegen.utils.OnceLogger.once; From 8297e272b1b5f3557ac7a48fbd59dc3b4c3c81d8 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 08:06:48 +0100 Subject: [PATCH 07/22] Revert "fix" This reverts commit 607c678874a344729201cfa80835e40133338b90. --- .../main/java/org/openapitools/codegen/DefaultGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index f89068e7c065..855344cdb0c7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -31,6 +31,7 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.openapitools.codegen.api.*; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; @@ -60,7 +61,6 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.removeStart; import static org.openapitools.codegen.CodegenConstants.X_INTERNAL; import static org.openapitools.codegen.utils.OnceLogger.once; From 2d00db246657c7e1a0480e6db03f68b2f5e957de Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 08:06:49 +0100 Subject: [PATCH 08/22] Revert "revert some changes" This reverts commit 3b8e4c28389f62817bf22fe23a6f417248106da1. --- .../openapitools/codegen/DefaultCodegen.java | 40 +++++++++---------- .../codegen/DefaultGenerator.java | 18 ++++----- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index a720f49b4c18..1ff331cbe6dd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -603,11 +603,9 @@ public Map postProcessAllModels(Map objs) * @return */ private boolean codegenPropertyIsNew(CodegenModel model, CodegenProperty property) { - return model.parentModel == null - ? false - : model.parentModel.allVars.stream().anyMatch(p -> + return model.parentModel != null && model.parentModel.allVars.stream().anyMatch(p -> p.name.equals(property.name) && - (p.dataType.equals(property.dataType) == false || p.datatypeWithEnum.equals(property.datatypeWithEnum) == false)); + (!p.dataType.equals(property.dataType) || !p.datatypeWithEnum.equals(property.datatypeWithEnum))); } /** @@ -751,7 +749,7 @@ protected void removeSelfReferenceImports(CodegenModel model) { for (CodegenProperty cp : model.allVars) { // detect self import if (cp.dataType.equalsIgnoreCase(model.classname) || - (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { + (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) { model.imports.remove(model.classname); // remove self import cp.isSelfReference = true; } @@ -793,7 +791,7 @@ private List getModelDependencies(List vars) { } private void setCircularReferencesOnProperties(final String root, - final Map> dependencyMap) { + final Map> dependencyMap) { dependencyMap.getOrDefault(root, new ArrayList<>()) .forEach(prop -> { final List unvisited = @@ -841,7 +839,7 @@ public ModelsMap postProcessModelsEnum(ModelsMap objs) { CodegenModel cm = mo.getModel(); // for enum model - if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) { + if (cm.isEnum && cm.allowableValues != null) { Map allowableValues = cm.allowableValues; List values = (List) allowableValues.get("values"); List> enumVars = buildEnumVars(values, cm.dataType); @@ -1132,7 +1130,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } else if (ModelUtils.isMapSchema(s)) { Schema addProps = ModelUtils.getAdditionalProperties(s); - if (addProps != null && ModelUtils.isComposedSchema(addProps)) { + if (ModelUtils.isComposedSchema(addProps)) { addOneOfNameExtension(addProps, nOneOf); addOneOfInterfaceModel(addProps, nOneOf); } @@ -2995,7 +2993,7 @@ private HashMap extractSchemaTestCases(String refToTestC } LinkedHashMap> testNameToTesCase = (LinkedHashMap>) schemaNameToTestCases.get(schemaName); for (Entry> entry : testNameToTesCase.entrySet()) { - LinkedHashMap testExample = (LinkedHashMap) entry.getValue(); + LinkedHashMap testExample = entry.getValue(); String nameInSnakeCase = toTestCaseName(entry.getKey()); Object data = processTestExampleData(testExample.get("data")); SchemaTestCase testCase = new SchemaTestCase( @@ -3557,7 +3555,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, } CodegenProperty df = discriminatorFound(composedSchemaName, sc, discPropName, new TreeSet()); String modelName = ModelUtils.getSimpleRef(ref); - if (df == null || !df.isString || df.required != true) { + if (df == null || !df.isString || !df.required) { String msgSuffix = ""; if (df == null) { msgSuffix += discPropName + " is missing from the schema, define it as required and type string"; @@ -3565,7 +3563,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, if (!df.isString) { msgSuffix += "invalid type for " + discPropName + ", set it to string"; } - if (df.required != true) { + if (!df.required) { String spacer = ""; if (msgSuffix.length() != 0) { spacer = ". "; @@ -3708,7 +3706,7 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch } } - if (matched == false) { + if (!matched) { uniqueDescendants.add(otherDescendant); } } @@ -4218,11 +4216,10 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo property.isContainer = true; if (ModelUtils.isSet(p)) { property.containerType = "set"; - property.containerTypeMapped = typeMapping.get(property.containerType); } else { property.containerType = "array"; - property.containerTypeMapped = typeMapping.get(property.containerType); } + property.containerTypeMapped = typeMapping.get(property.containerType); property.baseType = getSchemaType(p); // handle inner property @@ -4363,7 +4360,7 @@ void updateDefaultToEmptyContainer(CodegenProperty cp, Schema p) { * @param input a set of rules separated by `|` */ void parseDefaultToEmptyContainer(String input) { - String[] inputs = ((String) input).split("[|]"); + String[] inputs = input.split("[|]"); String containerType; for (String rule : inputs) { if (StringUtils.isEmpty(rule)) { @@ -4381,7 +4378,7 @@ void parseDefaultToEmptyContainer(String input) { LOGGER.error("Skipped invalid container type `{}` in `{}`.", containerType, input); } } else if (rule.startsWith("?")) { // nullable (required) - containerType = rule.substring(1, rule.length()); + containerType = rule.substring(1); if ("array".equalsIgnoreCase(containerType)) { arrayNullableDefaultToEmpty = true; } else if ("map".equalsIgnoreCase(containerType)) { @@ -6175,7 +6172,7 @@ protected void addVars(IJsonSchemaValidationProperties m, List } else { final CodegenProperty cp; - if (cm != null && cm.allVars == vars && varsMap.keySet().contains(key)) { + if (cm != null && cm.allVars == vars && varsMap.containsKey(key)) { // when updating allVars, reuse the codegen property from the child model if it's already present // the goal is to avoid issues when the property is defined in both child, parent but the // definition is not identical, e.g. required vs optional, integer vs string @@ -6342,7 +6339,7 @@ protected String removeNonNameElementToCamelCase(final String name, final String * Not all operating systems support case-sensitive paths */ private String uniqueCaseInsensitiveString(String value, Map seenValues) { - if (seenValues.keySet().contains(value)) { + if (seenValues.containsKey(value)) { return seenValues.get(value); } @@ -8012,8 +8009,8 @@ protected LinkedHashMap getContent(Content content, Se enc.getContentType(), headers, enc.getStyle().toString(), - enc.getExplode() == null ? false : enc.getExplode().booleanValue(), - enc.getAllowReserved() == null ? false : enc.getAllowReserved().booleanValue() + enc.getExplode() != null && enc.getExplode(), + enc.getAllowReserved() != null && enc.getAllowReserved() ); if (enc.getExtensions() != null) { @@ -8242,7 +8239,6 @@ protected void addRequiredVarsMap(Schema schema, IJsonSchemaValidationProperties } if (!found) { LOGGER.warn("Property {} is not processed correctly (missing from getVars). Maybe it's a const (not yet supported) in openapi v3.1 spec.", requiredPropertyName); - continue; } } else if (schema.getAdditionalProperties() instanceof Boolean && Boolean.FALSE.equals(schema.getAdditionalProperties())) { // TODO add processing for requiredPropertyName @@ -8587,7 +8583,7 @@ public void setRemoveEnumValuePrefix(final boolean removeEnumValuePrefix) { * @param name name of the parent oneOf schema */ public void addOneOfNameExtension(Schema schema, String name) { - if (schema.getOneOf() != null && schema.getOneOf().size() > 0) { + if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) { schema.addExtension(X_ONE_OF_NAME, name); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 855344cdb0c7..2dc98ec1b7f9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -86,7 +86,7 @@ public class DefaultGenerator implements Generator { private String basePath; private String basePathWithoutHost; private String contextPath; - private Map generatorPropertyDefaults = new HashMap<>(); + private final Map generatorPropertyDefaults = new HashMap<>(); /** * Retrieves an instance to the configured template processor, available after user-defined options are * applied via @@ -95,8 +95,8 @@ public class DefaultGenerator implements Generator { protected TemplateProcessor templateProcessor = null; private List userDefinedTemplates = new ArrayList<>(); - private String generatorCheck = "spring"; - private String templateCheck = "apiController.mustache"; + private final String generatorCheck = "spring"; + private final String templateCheck = "apiController.mustache"; public DefaultGenerator() { @@ -1392,7 +1392,7 @@ private void processUserDefinedTemplates() { // hack: destination filename in this scenario might be a suffix like Impl.java templateExt = userDefinedTemplate.getDestinationFilename(); } else { - templateExt = StringUtils.prependIfMissing(templateExt, "."); + templateExt = Strings.CS.prependIfMissing(templateExt, "."); } String templateOutputFolder = userDefinedTemplate.getFolder(); if (!templateOutputFolder.isEmpty()) { @@ -1994,7 +1994,7 @@ private void generateFilesMetadata(List files) { // Some implementations make the output ./c/d which seems to mix the logic // as documented for symlinks. So we need to trim any / or ./ from the start, // as nobody should be generating into system root and our expectation is no ./ - String relativePath = removeStart(removeStart(f.toString(), "." + File.separator), File.separator); + String relativePath = Strings.CS.removeStart(Strings.CS.removeStart(f.toString(), "." + File.separator), File.separator); if (File.separator.equals("\\")) { // ensure that windows outputs same FILES format relativePath = relativePath.replace(File.separator, "/"); @@ -2004,10 +2004,8 @@ private void generateFilesMetadata(List files) { } }); - Collections.sort(relativePaths, (a, b) -> IOCase.SENSITIVE.checkCompareTo(a, b)); - relativePaths.forEach(relativePath -> { - sb.append(relativePath).append(System.lineSeparator()); - }); + relativePaths.sort(IOCase.SENSITIVE::checkCompareTo); + relativePaths.forEach(relativePath -> sb.append(relativePath).append(System.lineSeparator())); String targetFile = config.outputFolder() + File.separator + METADATA_DIR + File.separator + config.getFilesMetadataFilename(); @@ -2022,7 +2020,7 @@ private void generateFilesMetadata(List files) { } private String removeTrailingSlash(String value) { - return StringUtils.removeEnd(value, "/"); + return Strings.CS.removeEnd(value, "/"); } } From 9941a95eeb58eb1ea7cfb8195bb1dc085aef9c7a Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 08:08:54 +0100 Subject: [PATCH 09/22] fix --- .../java/org/openapitools/codegen/DefaultGenerator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 2dc98ec1b7f9..0fa4b0dceea5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -31,7 +31,6 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Strings; import org.openapitools.codegen.api.*; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; @@ -61,6 +60,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.removeStart; import static org.openapitools.codegen.CodegenConstants.X_INTERNAL; import static org.openapitools.codegen.utils.OnceLogger.once; @@ -1392,7 +1392,7 @@ private void processUserDefinedTemplates() { // hack: destination filename in this scenario might be a suffix like Impl.java templateExt = userDefinedTemplate.getDestinationFilename(); } else { - templateExt = Strings.CS.prependIfMissing(templateExt, "."); + templateExt = StringUtils.prependIfMissing(templateExt, "."); } String templateOutputFolder = userDefinedTemplate.getFolder(); if (!templateOutputFolder.isEmpty()) { @@ -1994,7 +1994,7 @@ private void generateFilesMetadata(List files) { // Some implementations make the output ./c/d which seems to mix the logic // as documented for symlinks. So we need to trim any / or ./ from the start, // as nobody should be generating into system root and our expectation is no ./ - String relativePath = Strings.CS.removeStart(Strings.CS.removeStart(f.toString(), "." + File.separator), File.separator); + String relativePath = removeStart(removeStart(f.toString(), "." + File.separator), File.separator); if (File.separator.equals("\\")) { // ensure that windows outputs same FILES format relativePath = relativePath.replace(File.separator, "/"); @@ -2020,7 +2020,7 @@ private void generateFilesMetadata(List files) { } private String removeTrailingSlash(String value) { - return Strings.CS.removeEnd(value, "/"); + return StringUtils.removeEnd(value, "/"); } } From 4a5e1d24b8ab05cc4c895ba82eb69f58ef65c779 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 09:11:59 +0100 Subject: [PATCH 10/22] fix working with regex patterns --- .../openapitools/codegen/DefaultCodegen.java | 13 +-- .../languages/AbstractCSharpCodegen.java | 7 +- .../languages/AbstractJavaCodegen.java | 8 +- .../languages/AbstractKotlinCodegen.java | 4 +- .../languages/AbstractPythonCodegen.java | 8 +- .../AbstractPythonPydanticV1Codegen.java | 8 +- .../languages/ElixirClientCodegen.java | 4 +- .../languages/ErlangClientCodegen.java | 5 +- .../languages/HaskellHttpClientCodegen.java | 4 +- .../codegen/languages/JavaClientCodegen.java | 6 +- .../languages/JavaHelidonCommonCodegen.java | 13 ++- .../languages/JavaPlayFrameworkCodegen.java | 6 +- .../languages/JavaVertXServerCodegen.java | 13 ++- .../codegen/languages/KtormSchemaCodegen.java | 7 +- .../codegen/languages/MysqlSchemaCodegen.java | 15 ++- .../languages/PostgresqlSchemaCodegen.java | 8 +- .../languages/PostmanCollectionCodegen.java | 6 +- .../languages/ProtobufSchemaCodegen.java | 4 +- .../ScalaPlayFrameworkServerCodegen.java | 6 +- .../codegen/utils/PatternCache.java | 92 ++++++++++++++++ .../codegen/utils/PatternCacheTest.java | 104 ++++++++++++++++++ 21 files changed, 278 insertions(+), 63 deletions(-) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/PatternCache.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 1ff331cbe6dd..a87d370b165a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -69,6 +69,7 @@ import org.openapitools.codegen.utils.ExamplesUtils; import org.openapitools.codegen.utils.ModelUtils; import org.openapitools.codegen.utils.OneOfImplementorAdditionalData; +import org.openapitools.codegen.utils.PatternCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,7 +80,6 @@ import java.nio.charset.StandardCharsets; import java.util.*; import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -251,12 +251,7 @@ apiTemplateFiles are for API outputs only (controllers/handlers). private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); /** Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. */ private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); - /** - * Cache of removeCharRegEx string → compiled {@link Pattern} with {@link Pattern#UNICODE_CHARACTER_CLASS}. - * {@code sanitizeName} is called once per unique (name, regex, exceptions) tuple, so the regex string - * (almost always {@code "\\W"}) would otherwise be recompiled for every unique property/model name. - */ - private static final ConcurrentHashMap REMOVE_CHAR_UNICODE_PATTERN_CACHE = new ConcurrentHashMap<>(); + // Dynamic patterns keyed by user-supplied removeCharRegEx strings are cached via PatternCache. /** * True if the code generator supports multiple class inheritance. @@ -6763,9 +6758,7 @@ public String sanitizeName(final String name, String removeCharRegEx, ArrayList< // remove everything else other than word, number and _ // $php_variable => php_variable if (allowUnicodeIdentifiers) { //could be converted to a single line with ?: operator - modifiable = REMOVE_CHAR_UNICODE_PATTERN_CACHE - .computeIfAbsent(sanitizeNameOptions.getRemoveCharRegEx(), - regex -> Pattern.compile(regex, Pattern.UNICODE_CHARACTER_CLASS)) + modifiable = PatternCache.get(sanitizeNameOptions.getRemoveCharRegEx(), Pattern.UNICODE_CHARACTER_CLASS) .matcher(modifiable).replaceAll(""); } else { modifiable = modifiable.replaceAll(sanitizeNameOptions.getRemoveCharRegEx(), ""); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java index ec07161ff199..d11b3b529752 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java @@ -54,6 +54,9 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen { + /** Matches a regex literal that carries modifier flags after the closing slash, e.g. {@code /foo/i}. */ + private static final Pattern HAS_MODIFIERS = Pattern.compile(".*/[gmiyuvsdlnx]+$"); + protected boolean optionalAssemblyInfoFlag = true; protected boolean optionalEmitDefaultValuesFlag = false; protected boolean conditionalSerialization = false; @@ -2039,9 +2042,7 @@ public void postProcessPattern(String pattern, Map vendorExtensi // gmiyuvsd - ecma modifiers // l - legacy modifier provided by this library, provides a way to opt out of culture invariant // nx - c# modifiers https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options - Pattern hasModifiers = Pattern.compile(".*/[gmiyuvsdlnx]+$"); - - int end = hasModifiers.matcher(pattern).find() + int end = HAS_MODIFIERS.matcher(pattern).find() ? pattern.lastIndexOf('/') : pattern.length() - 1; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index 58f7d0e1f226..15e53ad7100d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -83,6 +83,8 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code private final Logger LOGGER = LoggerFactory.getLogger(AbstractJavaCodegen.class); private static final String ARTIFACT_VERSION_DEFAULT_VALUE = "1.0.0"; private static final ZoneId UTC = ZoneId.of("UTC"); + private static final Pattern LOMBOK_ANNOTATION = Pattern.compile("@lombok.(\\w+\\.)*(?\\w+)(\\(.*?\\))?"); + private static final Pattern JAVA_UTIL_IMPORT = Pattern.compile("java\\.util\\.(List|ArrayList|Map|HashMap)"); public static final String DEFAULT_LIBRARY = ""; public static final String DATE_LIBRARY = "dateLibrary"; @@ -2085,9 +2087,8 @@ public ModelsMap postProcessModels(ModelsMap objs) { // parse lombok additional model type annotations Map lombokOptions = new HashMap<>(); String regexp = "@lombok.(\\w+\\.)*(?\\w+)(\\(.*?\\))?"; - Pattern pattern = Pattern.compile(regexp); for (String annotation : additionalModelTypeAnnotations) { - Matcher matcher = pattern.matcher(annotation); + Matcher matcher = LOMBOK_ANNOTATION.matcher(annotation); if (matcher.find()) { String className = matcher.group("ClassName"); lombokOptions.put(className, true); @@ -2106,10 +2107,9 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List> imports = objs.getImports(); - Pattern pattern = Pattern.compile("java\\.util\\.(List|ArrayList|Map|HashMap)"); for (Iterator> itr = imports.iterator(); itr.hasNext(); ) { String itrImport = itr.next().get("import"); - if (pattern.matcher(itrImport).matches()) { + if (JAVA_UTIL_IMPORT.matcher(itrImport).matches()) { itr.remove(); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index 7fce10e78f53..a50f3111f675 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -50,6 +50,8 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements CodegenConfig { + private static final Pattern NON_WORD_UNICODE = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); + public static final String MODEL_MUTABLE = "modelMutable"; public static final String MODEL_MUTABLE_DESC = "Create mutable models"; public static final String ADDITIONAL_MODEL_TYPE_ANNOTATIONS = "additionalModelTypeAnnotations"; @@ -803,7 +805,7 @@ private String sanitizeKotlinSpecificNames(final String name) { } // Fallback, replace unknowns with underscore. - word = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS).matcher(word).replaceAll("_"); + word = NON_WORD_UNICODE.matcher(word).replaceAll("_"); if (word.matches("\\d.*")) { word = "_" + word; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index a4c775057c47..c4f46d335bfa 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -49,6 +49,9 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractPythonCodegen.class); + private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); + private static final Pattern REGEX_VALUE_EXTRACTOR = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); + public static final String MAP_NUMBER_TO = "mapNumberTo"; protected String packageName = "openapi_client"; @@ -220,7 +223,7 @@ public String toDefaultValue(Schema p) { if (defaultValue != null) { defaultValue = defaultValue.replace("\\", "\\\\") .replace("'", "\\'"); - if (Pattern.compile("\r\n|\r|\n").matcher(defaultValue).find()) { + if (MULTILINE_STRING.matcher(defaultValue).find()) { return "'''" + defaultValue + "'''"; } else { return "'" + defaultValue + "'"; @@ -438,8 +441,7 @@ private String toExampleValueRecursive(Schema schema, List includedSchem Random random = new Random(18); String sample = rgxGen.generate(random); // omit leading / and trailing /, omit trailing /i - Pattern valueExtractor = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); - Matcher m = valueExtractor.matcher(sample); + Matcher m = REGEX_VALUE_EXTRACTOR.matcher(sample); if (m.find()) { example = m.group(m.groupCount()); } else { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java index 7591c1df7c37..ed679042a4e3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java @@ -46,6 +46,9 @@ public abstract class AbstractPythonPydanticV1Codegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractPythonPydanticV1Codegen.class); + private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); + private static final Pattern REGEX_VALUE_EXTRACTOR = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); + public static final String MAP_NUMBER_TO = "mapNumberTo"; protected String packageName = "openapi_client"; @@ -190,7 +193,7 @@ public String toDefaultValue(Schema p) { if (defaultValue != null) { defaultValue = defaultValue.replace("\\", "\\\\") .replace("'", "\'"); - if (Pattern.compile("\r\n|\r|\n").matcher(defaultValue).find()) { + if (MULTILINE_STRING.matcher(defaultValue).find()) { return "'''" + defaultValue + "'''"; } else { return "'" + defaultValue + "'"; @@ -409,8 +412,7 @@ private String toExampleValueRecursive(Schema schema, List includedSchem Random random = new Random(18); String sample = rgxGen.generate(random); // omit leading / and trailing /, omit trailing /i - Pattern valueExtractor = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); - Matcher m = valueExtractor.matcher(sample); + Matcher m = REGEX_VALUE_EXTRACTOR.matcher(sample); if (m.find()) { example = m.group(m.groupCount()); } else { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java index c68ad41f8a08..6be26abf569c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java @@ -48,6 +48,7 @@ public class ElixirClientCodegen extends DefaultCodegen { private final Logger LOGGER = LoggerFactory.getLogger(ElixirClientCodegen.class); + private static final Pattern PATH_TEMPLATE_PATTERN = Pattern.compile("\\{([^\\}]+)\\}([^\\{]*)"); private final Pattern simpleAtomPattern = Pattern.compile("\\A(?:(?:[_\\p{Alpha}][_@\\p{Alnum}]*[?!]?)|-|@)\\z"); @Setter protected String packageVersion = "1.0.0"; @@ -348,10 +349,9 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List os = operations.getOperation(); List newOs = new ArrayList<>(); - Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}([^\\{]*)"); for (CodegenOperation o : os) { ArrayList pathTemplateNames = new ArrayList<>(); - Matcher matcher = pattern.matcher(o.path); + Matcher matcher = PATH_TEMPLATE_PATTERN.matcher(o.path); StringBuffer buffer = new StringBuffer(); while (matcher.find()) { String pathTemplateName = matcher.group(1); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java index a596660a9c60..018c8d2777f4 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java @@ -44,6 +44,8 @@ public class ErlangClientCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(ErlangClientCodegen.class); + private static final Pattern PATH_TEMPLATE_PATTERN = Pattern.compile("\\{([^\\}]+)\\}"); + @Setter protected String packageName = "openapi"; @Setter protected String packageVersion = "1.0.0"; protected String sourceFolder = "src"; @@ -328,7 +330,6 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List os = operations.getOperation(); List newOs = new ArrayList<>(); - Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}"); for (CodegenOperation o : os) { // force http method to lower case o.httpMethod = o.httpMethod.toLowerCase(Locale.ROOT); @@ -338,7 +339,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List pathTemplateNames = new ArrayList<>(); - Matcher matcher = pattern.matcher(o.path); + Matcher matcher = PATH_TEMPLATE_PATTERN.matcher(o.path); StringBuffer buffer = new StringBuffer(); while (matcher.find()) { String pathTemplateName = matcher.group(1); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java index 00d1a75e9673..14693a3b067c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java @@ -88,6 +88,7 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC // protected String MODEL_EXTENSIONS = "modelExtensions"; private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); + private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); static final String MEDIA_TYPE = "mediaType"; static final String MIME_NO_CONTENT = "MimeNoContent"; @@ -1159,8 +1160,7 @@ private String getMimeDataType(String mimeType) { } private static String generateNextName(String name) { - Pattern pattern = Pattern.compile("\\d+\\z"); - Matcher matcher = pattern.matcher(name); + Matcher matcher = TRAILING_DIGITS.matcher(name); if (matcher.find()) { String numStr = matcher.group(); int num = Integer.parseInt(numStr) + 1; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index b2faed874176..39e72aac1efb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -59,6 +59,9 @@ public class JavaClientCodegen extends AbstractJavaCodegen private final Logger LOGGER = LoggerFactory.getLogger(JavaClientCodegen.class); + /** Matches a Feign-style custom HTTP method suffix, e.g. {@code /path:customMethod}. */ + private static final Pattern FEIGN_CUSTOM_METHOD = Pattern.compile("^(.*):([^:]*)$"); + public static final String USE_RX_JAVA2 = "useRxJava2"; public static final String USE_RX_JAVA3 = "useRxJava3"; public static final String DO_NOT_USE_RX = "doNotUseRx"; @@ -942,13 +945,12 @@ public int compare(CodegenParameter one, CodegenParameter another) { if (isLibrary(FEIGN) || isLibrary(FEIGN_HC5)) { OperationMap operations = objs.getOperations(); List operationList = operations.getOperation(); - Pattern methodPattern = Pattern.compile("^(.*):([^:]*)$"); for (CodegenOperation op : operationList) { String path = op.path; String method = ""; // if a custom method is found at the end of the path, cut it off for later - Matcher m = methodPattern.matcher(path); + Matcher m = FEIGN_CUSTOM_METHOD.matcher(path); if (m.find()) { path = m.group(1); method = m.group(2); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonCommonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonCommonCodegen.java index 3ca11c46b991..98359063a074 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonCommonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonCommonCodegen.java @@ -62,6 +62,9 @@ public abstract class JavaHelidonCommonCodegen extends AbstractJavaCodegen implements BeanValidationFeatures, PerformBeanValidationFeatures { + private static final Pattern STATUS_PATTERN = Pattern.compile( + "public static final Status (\\w+)\\s*=\\s*new\\s*Status\\((\\d+)", Pattern.MULTILINE); + static final String HELIDON_MP = "mp"; static final String HELIDON_SE = "se"; @@ -488,10 +491,8 @@ private HashMap loadKnownHttpStatusMap() { if (is == null) { throw new RuntimeException("Unable to locate /java-helidon/common/Status.java to discover known HTTP statuses"); } - Pattern statusPattern = Pattern.compile("public static final Status (\\w+)\\s*=\\s*new\\s*Status\\((\\d+)", - Pattern.MULTILINE); return new Scanner(is, StandardCharsets.UTF_8) - .findAll(statusPattern) + .findAll(STATUS_PATTERN) .collect(HashMap::new, (map, match) -> map.put(match.group(2), match.group(1)), Map::putAll); @@ -937,10 +938,10 @@ private static List localDefaultVersions() throws IOException { return Files.readAllLines(versionsFile.toPath()); } - private static List extractVersions(String xmlContent) { - Pattern versionPattern = Pattern.compile("]*>([^>]+)"); + private static final Pattern VERSION_PATTERN = Pattern.compile("]*>([^>]+)"); - Matcher matcher = versionPattern.matcher(xmlContent); + private static List extractVersions(String xmlContent) { + Matcher matcher = VERSION_PATTERN.matcher(xmlContent); List result = new ArrayList<>(); while (matcher.find()) { result.add(matcher.group(1)); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java index 9f2743be01de..cc841af76b62 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java @@ -40,6 +40,9 @@ public class JavaPlayFrameworkCodegen extends AbstractJavaCodegen implements BeanValidationFeatures { private final Logger LOGGER = LoggerFactory.getLogger(JavaPlayFrameworkCodegen.class); + + private static final Pattern PATH_VARIABLE = Pattern.compile("\\{([^}]+)}"); + public static final String TITLE = "title"; public static final String CONFIG_PACKAGE = "configPackage"; public static final String BASE_PACKAGE = "basePackage"; @@ -245,8 +248,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List\xFFFF and against ^\x0000. works only on Java7+ - Pattern regexp = Pattern.compile("[^0-9a-zA-z$_\\x0080-\\xFFFF]"); - Matcher matcher = regexp.matcher(identifier); + Matcher matcher = UNSAFE_IDENTIFIER_CHARS.matcher(identifier); if (matcher.find()) { LOGGER.warn("Identifier '{}' contains unsafe characters out of [0-9,a-z,A-Z$_] and U+0080..U+FFFF range", identifier); - identifier = identifier.replaceAll("[^0-9a-zA-z$_\\x0080-\\xFFFF]", ""); + identifier = matcher.reset().replaceAll(""); } return identifier; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java index 1da17d653d06..e80c374f70bf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java @@ -37,6 +37,11 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(MysqlSchemaCodegen.class); + /** Unsafe characters for unquoted MySQL identifiers: outside [0-9,a-z,A-Z,$,_,U+0080..U+FFFF]. */ + private static final Pattern UNSAFE_UNQUOTED_IDENTIFIER_CHARS = Pattern.compile("[^0-9a-zA-Z$_\\u0080-\\uFFFF]"); + /** Unsafe characters for quoted MySQL identifiers: outside [U+0001..U+007F, U+0080..U+FFFF]. */ + private static final Pattern UNSAFE_QUOTED_IDENTIFIER_CHARS = Pattern.compile("[^\\u0001-\\u007F\\u0080-\\uFFFF]"); + public static final String VENDOR_EXTENSION_MYSQL_SCHEMA = "x-mysql-schema"; public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName"; public static final String JSON_DATA_TYPE_ENABLED = "jsonDataTypeEnabled"; @@ -1108,12 +1113,11 @@ public String toMysqlIdentifier(String name, String prefix, String suffix) { */ public String escapeMysqlUnquotedIdentifier(String identifier) { // ASCII: [0-9,a-z,A-Z$_] (basic Latin letters, digits 0-9, dollar, underscore) Extended: U+0080 .. U+FFFF - Pattern regexp = Pattern.compile("[^0-9a-zA-z$_\\u0080-\\uFFFF]"); - Matcher matcher = regexp.matcher(identifier); + Matcher matcher = UNSAFE_UNQUOTED_IDENTIFIER_CHARS.matcher(identifier); if (matcher.find()) { LOGGER.warn("Identifier '{}' contains unsafe characters out of [0-9,a-z,A-Z$_] and U+0080..U+FFFF range", identifier); - identifier = identifier.replaceAll("[^0-9a-zA-z$_\\u0080-\\uFFFF]", ""); + identifier = matcher.reset().replaceAll(""); } // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not permitted in quoted or unquoted identifiers. @@ -1131,12 +1135,11 @@ public String escapeMysqlUnquotedIdentifier(String identifier) { */ public String escapeMysqlQuotedIdentifier(String identifier) { // ASCII: U+0001 .. U+007F Extended: U+0080 .. U+FFFF - Pattern regexp = Pattern.compile("[^\\u0001-\\u007F\\u0080-\\uFFFF]"); - Matcher matcher = regexp.matcher(identifier); + Matcher matcher = UNSAFE_QUOTED_IDENTIFIER_CHARS.matcher(identifier); if (matcher.find()) { LOGGER.warn("Identifier '{}' contains unsafe characters out of U+0001..U+007F and U+0080..U+FFFF range", identifier); - identifier = identifier.replaceAll("[^\\u0001-\\u007F\\u0080-\\uFFFF]", ""); + identifier = matcher.reset().replaceAll(""); } // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not permitted in quoted or unquoted identifiers. diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java index 8630b4bb303c..35b6ac62ef14 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -39,6 +39,9 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen { private final Logger LOGGER = LoggerFactory.getLogger(PostgresqlSchemaCodegen.class); + /** Characters not allowed in an unquoted PostgreSQL identifier: outside [0-9,a-z,A-Z,$,_,U+0080..U+FFFF]. */ + private static final Pattern UNSAFE_IDENTIFIER_CHARS = Pattern.compile("[^0-9a-zA-Z$_\\u0080-\\uFFFF]"); + public static final String VENDOR_EXTENSION_POSTGRESQL_SCHEMA = "x-postgresql-schema"; public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName"; public static final String JSON_DATA_TYPE = "jsonDataType"; @@ -1346,12 +1349,11 @@ public String toPostgresqlIdentifier(String name, String prefix, String suffix) public String escapePostgresqlUnquotedIdentifier(String identifier) { // ASCII: [0-9,a-z,A-Z$_] (basic Latin letters, digits 0-9, dollar, underscore) // Extended: U+0080 .. U+FFFF - Pattern regexp = Pattern.compile("[^0-9a-zA-z$_\\u0080-\\uFFFF]"); - Matcher matcher = regexp.matcher(identifier); + Matcher matcher = UNSAFE_IDENTIFIER_CHARS.matcher(identifier); if (matcher.find()) { LOGGER.warn("Identifier '{}' contains unsafe characters out of [0-9,a-z,A-Z$_] and U+0080..U+FFFF range", identifier); - identifier = identifier.replaceAll("[^0-9a-zA-z$_\\u0080-\\uFFFF]", ""); + identifier = matcher.reset().replaceAll(""); } // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostmanCollectionCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostmanCollectionCodegen.java index 124bc29b7933..012c31c40ba6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostmanCollectionCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostmanCollectionCodegen.java @@ -34,6 +34,9 @@ public class PostmanCollectionCodegen extends DefaultCodegen implements CodegenC private final Logger LOGGER = LoggerFactory.getLogger(PostmanCollectionCodegen.class); + /** Matches Postman double-brace placeholders, e.g. {@code {{variable}}}. */ + private static final Pattern POSTMAN_PLACEHOLDER_PATTERN = Pattern.compile("\\{\\{([^}]*)}}"); + protected String apiVersion = "1.0.0"; // Select whether to create folders according to the spec’s paths or tags. Values: Paths | Tags @@ -702,8 +705,7 @@ String escapeJsonString(String value) { public Set extractPlaceholders(String input) { Set variables = new HashSet<>(); - Pattern pattern = Pattern.compile("\\{\\{([^}]*)\\}\\}"); - Matcher matcher = pattern.matcher(input); + Matcher matcher = POSTMAN_PLACEHOLDER_PATTERN.matcher(input); while (matcher.find()) { if (postmanGuidPlaceholderName.equalsIgnoreCase(matcher.group(1))) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java index 9e1b641b6e9d..9a5d5e2063a9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java @@ -51,6 +51,8 @@ public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConfig { + private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); + private static final String IMPORT = "import"; private static final String IMPORTS = "imports"; @@ -1545,7 +1547,7 @@ public String toDefaultValue(Schema p) { } } else if (ModelUtils.isStringSchema(p)) { if (p.getDefault() != null) { - if (Pattern.compile("\r\n|\r|\n").matcher(String.valueOf(p.getDefault())).find()) + if (MULTILINE_STRING.matcher(String.valueOf(p.getDefault())).find()) return "'''" + p.getDefault() + "'''"; else return "'" + p.getDefault() + "'"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java index 13017961c66f..3fb4c823e9e5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java @@ -40,6 +40,9 @@ import static org.openapitools.codegen.utils.StringUtils.camelize; public class ScalaPlayFrameworkServerCodegen extends AbstractScalaCodegen implements CodegenConfig { + + private static final Pattern PATH_VARIABLE = Pattern.compile("\\{([^}]+)}"); + public static final String TITLE = "title"; public static final String SKIP_STUBS = "skipStubs"; public static final String SUPPORT_ASYNC = "supportAsync"; @@ -214,8 +217,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List ops = operations.getOperation(); for (CodegenOperation operation : ops) { - Pattern pathVariableMatcher = Pattern.compile("\\{([^}]+)}"); - Matcher match = pathVariableMatcher.matcher(operation.path); + Matcher match = PATH_VARIABLE.matcher(operation.path); while (match.find()) { String completeMatch = match.group(); String replacement = ":" + camelize(match.group(1), LOWERCASE_FIRST_LETTER); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/PatternCache.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/PatternCache.java new file mode 100644 index 000000000000..fa68699129ae --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/PatternCache.java @@ -0,0 +1,92 @@ +/* + * 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.utils; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.util.regex.Pattern; + +/** + * A shared, process-wide cache of compiled {@link Pattern} objects, keyed by regex string + * (and optional flags). + * + *

Motivation

+ * {@link Pattern#compile(String)} is non-trivial: it parses and compiles the regex into an + * internal automaton. When the same literal regex is compiled inside a hot loop (e.g. once + * per property name, per operation, or per model), the overhead accumulates. This cache + * ensures each distinct {@code (regex, flags)} pair is compiled at most once. + * + *

Thread safety

+ * Backed by a Caffeine cache; all operations are thread-safe. Caffeine's internal striped + * locking means concurrent requests for the same key result in only one compilation. + * + *

Memory

+ * The cache is bounded at {@value #MAX_SIZE} entries via LRU eviction, which is effectively + * unlimited for any single codegen run (realistic specs have at most dozens of distinct + * patterns) while protecting long-lived server processes (e.g. openapi-generator-online) + * from unbounded growth caused by user-supplied {@code pattern:} values. + * + *

What goes here vs. {@code static final Pattern}

+ * Use this cache only for dynamic pattern keys — i.e. patterns whose regex string + * is not known at compile time (user config, OpenAPI {@code pattern:} field values, etc.). + * Fixed literal patterns that appear in a single class should remain + * {@code private static final Pattern} constants; they are resolved at class-load time and + * accessed via a direct field read, which is faster than any map lookup. + */ +public final class PatternCache { + + /** Maximum number of cached entries before LRU eviction kicks in. */ + static final int MAX_SIZE = 10_000; + + private static final Cache CACHE = Caffeine.newBuilder() + .maximumSize(MAX_SIZE) + .build(); + + private PatternCache() { + // utility class – no instances + } + + /** + * Returns a compiled {@link Pattern} for {@code regex} with no flags, compiling it on the + * first call and returning the cached instance on subsequent calls. + * + * @param regex the regular-expression string (must not be {@code null}) + * @return a compiled {@link Pattern} + */ + public static Pattern get(String regex) { + return CACHE.get(regex, Pattern::compile); + } + + /** + * Returns a compiled {@link Pattern} for {@code regex} with the given {@code flags}, + * compiling it on the first call and returning the cached instance on subsequent calls. + * + *

The cache key encodes both the regex string and the flags value so that the same + * regex compiled with different flags (e.g. {@link Pattern#UNICODE_CHARACTER_CLASS}) + * is stored as a separate entry. + * + * @param regex the regular-expression string (must not be {@code null}) + * @param flags match flags, as accepted by {@link Pattern#compile(String, int)} + * @return a compiled {@link Pattern} + */ + public static Pattern get(String regex, int flags) { + // NUL character as separator is safe because it cannot appear in a valid regex string. + return CACHE.get(regex + '\0' + flags, k -> Pattern.compile(regex, flags)); + } +} + diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java new file mode 100644 index 000000000000..92fbe460855b --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java @@ -0,0 +1,104 @@ +/* + * 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.utils; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.regex.Pattern; + +public class PatternCacheTest { + + @Test + public void testSameInstanceReturnedForSameRegex() { + Pattern first = PatternCache.get("\\W+"); + Pattern second = PatternCache.get("\\W+"); + Assert.assertSame(first, second, + "PatternCache must return the same Pattern instance for the same regex string"); + } + + @Test + public void testSameInstanceReturnedForSameRegexAndFlags() { + Pattern first = PatternCache.get("\\W+", Pattern.UNICODE_CHARACTER_CLASS); + Pattern second = PatternCache.get("\\W+", Pattern.UNICODE_CHARACTER_CLASS); + Assert.assertSame(first, second, + "PatternCache must return the same Pattern instance for identical (regex, flags) pairs"); + } + + @Test + public void testDifferentInstanceForDifferentFlags() { + Pattern noFlags = PatternCache.get("\\W+"); + Pattern withFlags = PatternCache.get("\\W+", Pattern.UNICODE_CHARACTER_CLASS); + Assert.assertNotSame(noFlags, withFlags, + "PatternCache must store separate entries for the same regex compiled with different flags"); + Assert.assertEquals(noFlags.flags() & Pattern.UNICODE_CHARACTER_CLASS, 0, + "Pattern compiled without flags must not have UNICODE_CHARACTER_CLASS set"); + Assert.assertEquals(withFlags.flags() & Pattern.UNICODE_CHARACTER_CLASS, Pattern.UNICODE_CHARACTER_CLASS, + "Pattern compiled with UNICODE_CHARACTER_CLASS must have that flag set " + + "(note: the JVM may also implicitly set UNICODE_CASE=64, so exact equality is not checked)"); + } + + @Test + public void testDifferentInstanceForDifferentRegex() { + Pattern a = PatternCache.get("\\d+"); + Pattern b = PatternCache.get("\\w+"); + Assert.assertNotSame(a, b, + "PatternCache must store separate entries for different regex strings"); + } + + @Test + public void testPatternFunctionallyCorrect() { + Pattern pattern = PatternCache.get("\\d+"); + Assert.assertTrue(pattern.matcher("123").matches()); + Assert.assertFalse(pattern.matcher("abc").matches()); + } + + @Test + public void testConcurrentAccessReturnsSameInstance() throws Exception { + final String regex = "concurrent-test-[a-z]+"; + final int threads = 20; + + ExecutorService executor = Executors.newFixedThreadPool(threads); + List> tasks = new ArrayList<>(threads); + for (int i = 0; i < threads; i++) { + tasks.add(() -> PatternCache.get(regex)); + } + + List> futures = executor.invokeAll(tasks); + executor.shutdown(); + + Pattern expected = futures.get(0).get(); + for (Future future : futures) { + Assert.assertSame(future.get(), expected, + "All concurrent callers must receive the same cached Pattern instance"); + } + } + + @Test + public void testMaxSizeIsReasonable() { + Assert.assertTrue(PatternCache.MAX_SIZE >= 1_000, + "PatternCache.MAX_SIZE should be at least 1000 to cover realistic workloads"); + } +} + + From bbeee7d95ee049165556f878c0c8cedb28ad1bc5 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 09:42:27 +0100 Subject: [PATCH 11/22] fix working with regex patterns --- .../openapitools/codegen/DefaultCodegen.java | 35 +++++++----- .../codegen/InlineModelResolver.java | 9 ++-- .../codegen/languages/AbstractAdaCodegen.java | 18 ++++--- .../languages/AbstractApexCodegen.java | 54 ++++++++++++------- .../languages/AbstractCSharpCodegen.java | 27 +++++----- .../codegen/languages/AbstractGoCodegen.java | 27 ++++++---- .../languages/AbstractKotlinCodegen.java | 27 ++++++---- 7 files changed, 119 insertions(+), 78 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index a87d370b165a..e5efef4cb69e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -251,6 +251,14 @@ apiTemplateFiles are for API outputs only (controllers/handlers). private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); /** Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. */ private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); + /** Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. */ + private static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); + /** Matches a string that starts with a digit; used in {@link #toEnumVarName}. */ + private static final Pattern LEADING_DIGIT = Pattern.compile("\\d.*"); + /** Matches tab, newline, or carriage-return; used in {@link #escapeText}. */ + private static final Pattern CONTROL_WHITESPACE = Pattern.compile("[\\t\\n\\r]"); + /** Matches a callback path-expression parameter like {@code {$request.body#/id}}. */ + private static final Pattern CALLBACK_EXPRESSION_PARAM = Pattern.compile("\\{\\$.*}"); // Dynamic patterns keyed by user-supplied removeCharRegEx strings are cached via PatternCache. /** @@ -956,8 +964,8 @@ public String toEnumVarName(String value, String datatype) { return "EMPTY"; } - String var = value.replaceAll("\\W+", "_").toUpperCase(Locale.ROOT); - if (var.matches("\\d.*")) { + String var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); + if (LEADING_DIGIT.matcher(var).lookingAt()) { var = "_" + var; } @@ -1167,11 +1175,10 @@ public String escapeText(String input) { // replace " with \" // outer unescape to retain the original multi-byte characters // finally escalate characters avoiding code injection + String unescaped = StringEscapeUtils.unescapeJava( + StringEscapeUtils.escapeJava(input).replace("\\/", "/")); return escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( - StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") + CONTROL_WHITESPACE.matcher(unescaped).replaceAll(" ") .replace("\\", "\\\\") .replace("\"", "\\\"")); } @@ -1211,7 +1218,7 @@ public String escapeTextWhileAllowingNewLines(String input) { StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(input) .replace("\\/", "/")) - .replaceAll("\\t", " ") + .replace("\t", " ") .replace("\\", "\\\\") .replace("\"", "\\\"")); } @@ -5256,7 +5263,7 @@ public CodegenCallback fromCallback(String name, Callback callback, List } else { boolean genId = op.getOperationId() == null; if (genId) { - op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + expression.replaceAll("\\{\\$.*}", ""), method)); + op.setOperationId(getOrGenerateOperationId(op, c.name + "_" + CALLBACK_EXPRESSION_PARAM.matcher(expression).replaceAll(""), method)); } if (op.getExtensions() == null) { @@ -5828,8 +5835,8 @@ protected String getOrGenerateOperationId(Operation operation, String path, Stri if (StringUtils.isBlank(operationId)) { String tmpPath = path; - tmpPath = tmpPath.replaceAll("\\{", ""); - tmpPath = tmpPath.replaceAll("\\}", ""); + tmpPath = tmpPath.replace("{", ""); + tmpPath = tmpPath.replace("}", ""); String[] parts = (tmpPath + "/" + httpMethod).split("/"); StringBuilder builder = new StringBuilder(); if ("/".equals(tmpPath)) { @@ -6752,8 +6759,8 @@ public String sanitizeName(final String name, String removeCharRegEx, ArrayList< // /api/films/get => _api_films_get // \api\films\get => _api_films_get - modifiable = modifiable.replaceAll("/", "_"); - modifiable = modifiable.replaceAll("\\\\", "_"); + modifiable = modifiable.replace("/", "_"); + modifiable = modifiable.replace("\\", "_"); // remove everything else other than word, number and _ // $php_variable => php_variable @@ -7046,8 +7053,8 @@ public String addRegularExpressionDelimiter(String pattern) { return pattern; } - if (!pattern.matches("^/.*")) { - return "/" + pattern.replaceAll("/", "\\\\/") + "/"; + if (!pattern.startsWith("/")) { + return "/" + pattern.replace("/", "\\/") + "/"; } return pattern; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java index 777fed9b2cb4..e9ee7b64de0d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java @@ -37,8 +37,11 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.regex.Pattern; public class InlineModelResolver { + private static final Pattern LEADING_DIGIT = Pattern.compile("^[0-9]"); + private static final Pattern NON_ALPHANUMERIC = Pattern.compile("[^A-Za-z0-9]"); private OpenAPI openAPI; private Map addedModels = new HashMap<>(); private Map generatedSignature = new HashMap<>(); @@ -785,9 +788,9 @@ private void addGenerated(String name, Schema model) { * @param name name to be processed to make sure it's sanitized */ private String sanitizeName(final String name) { - return name - .replaceAll("^[0-9]", "_$0") // e.g. 12object => _12object - .replaceAll("[^A-Za-z0-9]", "_"); // e.g. io.schema.User name => io_schema_User_name + return NON_ALPHANUMERIC.matcher( + LEADING_DIGIT.matcher(name).replaceFirst("_$0")) // e.g. 12object => _12object + .replaceAll("_"); // e.g. io.schema.User name => io_schema_User_name } /** diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java index 4762205e7e1b..8fd528c10c92 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java @@ -38,6 +38,7 @@ import java.io.File; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -45,6 +46,9 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractAdaCodegen.class); + private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); + private static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); + public static final String HTTP_SUPPORT_OPTION = "httpSupport"; public static final String OPENAPI_PACKAGE_NAME_OPTION = "openApiName"; @@ -398,7 +402,7 @@ public String toModelName(final String name) { } // model name starts with number - if (result.matches("^\\d.*")) { + if (LEADING_DIGIT.matcher(result).find()) { String modelName = "Model_" + result; // e.g. 200Response => Model_200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -439,16 +443,16 @@ else if (getSymbolName(value) != null) { else if ("Integer".equals(datatype) || "Long".equals(datatype) || "Float".equals(datatype) || "Double".equals(datatype)) { String varName = "NUMBER_" + value; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = varName.replace("-", "MINUS_"); + varName = varName.replace("+", "PLUS_"); + varName = varName.replace(".", "_DOT_"); var = varName; } // string else { - var = value.replaceAll("\\W+", "_").toUpperCase(Locale.ROOT); - if (var.matches("\\d.*")) { + var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); + if (LEADING_DIGIT.matcher(var).find()) { var = "_" + var; } else { var = sanitizeName(var); @@ -1094,7 +1098,7 @@ private List postProcessAuthMethod(List authMe ident = name; } scopeIndex++; - ident = toAdaIdentifier(sanitizeName(ident.replaceAll(":", "_")), "S_"); + ident = toAdaIdentifier(sanitizeName(ident.replace(":", "_")), "S_"); if (operationsScopes.containsValue(ident)) { ident = ident + "_" + scopeIndex; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java index 6a090df03d5a..f4ba65aa9a25 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -39,6 +40,19 @@ public abstract class AbstractApexCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractApexCodegen.class); + private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); + private static final Pattern LEADING_DIGITS_ONLY = Pattern.compile("^\\d.*"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); + private static final Pattern DATE_FORMAT = Pattern.compile("^\\d{4}(-\\d{2}){2}"); + private static final Pattern DATETIME_FORMAT = Pattern.compile("^\\d{4}([-T:]\\d{2}){5}.+"); + private static final Pattern NON_NUMERIC = Pattern.compile("[^-0-9.]"); + private static final Pattern UNSAFE_PACKAGE_CHARS = Pattern.compile("[^a-zA-Z0-9_.]"); + private static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$"); + private static final Pattern UNDERSCORE_CLASS = Pattern.compile("^_*class$"); + private static final Pattern DATE_SEP_ZERO = Pattern.compile("-0?"); + private static final Pattern DATETIME_SEP_ZERO = Pattern.compile("[-T:]0?"); + @Setter protected Boolean serializableModel = false; public AbstractApexCodegen() { @@ -77,10 +91,10 @@ public String escapeReservedWord(String name) { public String sanitizeName(String name) { name = super.sanitizeName(name); if (name.contains("__")) { // Preventing namespacing - name = name.replaceAll("__", "_"); + name = name.replace("__", "_"); } - if (name.matches("^\\d.*")) { // Prevent named credentials with leading number - name = name.replaceAll("^\\d.*", ""); + if (LEADING_DIGIT.matcher(name).find()) { // Prevent named credentials with leading number + name = LEADING_DIGITS_ONLY.matcher(name).replaceAll(""); } return name; } @@ -90,7 +104,7 @@ public String toVarName(String name) { // sanitize name name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - if (name.toLowerCase(Locale.ROOT).matches("^_*class$")) { + if (UNDERSCORE_CLASS.matcher(name.toLowerCase(Locale.ROOT)).matches()) { return "propertyClass"; } @@ -99,7 +113,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { if (isReservedWord(name)) { name = escapeReservedWord(name); } @@ -115,7 +129,7 @@ public String toVarName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || LEADING_DIGIT.matcher(name).find()) { name = escapeReservedWord(name); } @@ -169,7 +183,7 @@ public String toModelName(final String name) { } // model name starts with number - if (camelizedName.matches("^\\d.*")) { + if (LEADING_DIGIT.matcher(camelizedName).find()) { final String modelName = "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -318,8 +332,8 @@ public String toExampleValue(Schema p) { p.setExample(example); example = "EncodingUtil.base64Decode('" + example + "')"; } else if (ModelUtils.isDateSchema(p)) { - if (example.matches("^\\d{4}(-\\d{2}){2}")) { - example = example.substring(0, 10).replaceAll("-0?", ", "); + if (DATE_FORMAT.matcher(example).find()) { + example = DATE_SEP_ZERO.matcher(example.substring(0, 10)).replaceAll(", "); } else if (example.isEmpty()) { example = "2000, 1, 23"; } else { @@ -329,8 +343,8 @@ public String toExampleValue(Schema p) { } example = "Date.newInstance(" + example + ")"; } else if (ModelUtils.isDateTimeSchema(p)) { - if (example.matches("^\\d{4}([-T:]\\d{2}){5}.+")) { - example = example.substring(0, 19).replaceAll("[-T:]0?", ", "); + if (DATETIME_FORMAT.matcher(example).find()) { + example = DATETIME_SEP_ZERO.matcher(example.substring(0, 19)).replaceAll(", "); } else if (example.isEmpty()) { example = "2000, 1, 23, 4, 56, 7"; } else { @@ -340,7 +354,7 @@ public String toExampleValue(Schema p) { } example = "Datetime.newInstanceGmt(" + example + ")"; } else if (ModelUtils.isNumberSchema(p)) { - example = example.replaceAll("[^-0-9.]", ""); + example = NON_NUMERIC.matcher(example).replaceAll(""); example = example.isEmpty() ? "1.3579" : example; } else if (ModelUtils.isFileSchema(p)) { if (example.isEmpty()) { @@ -380,7 +394,7 @@ public String toExampleValue(Schema p) { ? "'046b6c7f-0b8a-43b9-b35d-6489e6daee91'" : "'" + escapeText(example) + "'"; } else if (ModelUtils.isIntegerSchema(p)) { - example = example.matches("^-?\\d+$") ? example : "0"; + example = INTEGER_PATTERN.matcher(example).matches() ? example : "0"; } else if (ModelUtils.isObjectSchema(p)) { example = example.isEmpty() ? "null" : example; } else { @@ -524,15 +538,15 @@ public String toEnumVarName(String value, String datatype) { if ("Integer".equals(datatype) || "Long".equals(datatype) || "Float".equals(datatype) || "Double".equals(datatype)) { String varName = "NUMBER_" + value; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = varName.replace("-", "MINUS_"); + varName = varName.replace("+", "PLUS_"); + varName = varName.replace(".", "_DOT_"); return varName; } // string - String var = value.replaceAll("\\W+", "_").toUpperCase(Locale.ROOT); - if (var.matches("\\d.*")) { + String var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); + if (LEADING_DIGIT.matcher(var).find()) { return "_" + var; } else { return var; @@ -618,7 +632,7 @@ private static CodegenModel reconcileInlineEnums(CodegenModel codegenModel, Code private static String sanitizePackageName(String packageName) { packageName = packageName.trim(); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - packageName = packageName.replaceAll("[^a-zA-Z0-9_\\.]", "_"); + packageName = UNSAFE_PACKAGE_CHARS.matcher(packageName).replaceAll("_"); if (Strings.isNullOrEmpty(packageName)) { return "invalidPackageName"; } @@ -628,7 +642,7 @@ private static String sanitizePackageName(String packageName) { private String sanitizePath(String p) { //prefer replace a ", instead of a fuLL URL encode for readability - return p.replaceAll("\"", "%22"); + return p.replace("\"", "%22"); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java index d11b3b529752..18d92aeb6b6b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java @@ -56,6 +56,9 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen { /** Matches a regex literal that carries modifier flags after the closing slash, e.g. {@code /foo/i}. */ private static final Pattern HAS_MODIFIERS = Pattern.compile(".*/[gmiyuvsdlnx]+$"); + private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern UNESCAPED_DOUBLE_QUOTE = Pattern.compile("(? created_at - name = name.replaceAll("-", "_"); + name = name.replace("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -1498,7 +1501,7 @@ public String escapeReservedWord(CodegenModel model, String name) { @Override public String escapeReservedWord(String name) { if (isReservedWord(name) || - name.matches("^\\d.*")) { + LEADING_DIGIT.matcher(name).find()) { name = AbstractCSharpCodegen.invalidParameterNamePrefix + camelize(name); } return name; @@ -1676,7 +1679,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (LEADING_DIGIT.matcher(name).find()) { 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) @@ -1771,11 +1774,11 @@ public String toEnumValue(String value, String datatype) { return value; } - final String partiallyEscaped = value + final String partiallyEscaped = UNESCAPED_DOUBLE_QUOTE.matcher(value .replace("\n", "\\n") .replace("\t", "\\t") - .replace("\r", "\\r") - .replaceAll("(? Model200Response (after camelize) @@ -348,7 +353,7 @@ public String toModel(String name, boolean doUnderscore) { public String toApiFilename(String name) { final String apiName; // replace - with _ e.g. created-at => created_at - String api = name.replaceAll("-", "_"); + String api = name.replace("-", "_"); // e.g. PetApi.go => pet_api.go api = "api_" + underscore(api); if (isReservedFilename(api)) { @@ -498,7 +503,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (sanitizedOperationId.matches("^\\d.*")) { + if (LEADING_DIGIT.matcher(sanitizedOperationId).find()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize("call_" + sanitizedOperationId)); sanitizedOperationId = "call_" + sanitizedOperationId; } @@ -789,7 +794,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { if (cp.pattern != null) { cp.vendorExtensions.put("x-go-custom-tag", "validate:\"regexp=" + - cp.pattern.replace("\\", "\\\\").replaceAll("^/|/$", "") + + LEADING_TRAILING_SLASH.matcher(cp.pattern.replace("\\", "\\\\")).replaceAll("") + "\""); } @@ -931,9 +936,9 @@ public String toEnumVarName(String name, String datatype) { // number if (isNumberType(datatype)) { String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = varName.replace("-", "MINUS_"); + varName = varName.replace("+", "PLUS_"); + varName = varName.replace(".", "_DOT_"); return NUMERIC_ENUM_PREFIX + varName; } @@ -949,7 +954,7 @@ public String toEnumVarName(String name, String datatype) { if (isReservedWord(enumName)) { // reserved word return escapeReservedWord(enumName); - } else if (enumName.matches("\\d.*")) { // starts with a number + } else if (LEADING_DIGIT.matcher(enumName).find()) { // starts with a number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -967,7 +972,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).find()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index a50f3111f675..8669e7b9ea9f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -50,7 +50,12 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements CodegenConfig { - private static final Pattern NON_WORD_UNICODE = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); + private static final Pattern NON_WORD_UNICODE = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); + private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); + private static final Pattern ALL_UPPER_ALPHANUMERIC_US = Pattern.compile("^[A-Z0-9_]*$"); + private static final Pattern ALL_UNDERSCORES = Pattern.compile("^_*$"); + private static final Pattern LEADING_DIGIT_OR_DOLLAR = Pattern.compile("(^\\d.*)|(.*\\$.*)"); + private static final Pattern UNDERSCORE_CLASS = Pattern.compile("^_*class$"); public static final String MODEL_MUTABLE = "modelMutable"; public static final String MODEL_MUTABLE_DESC = "Create mutable models"; @@ -622,7 +627,7 @@ public String toEnumVarName(String value, String datatype) { if (value.isEmpty()) { modified = "EMPTY"; } else { - modified = value.replaceAll("-", "_"); + modified = value.replace("-", "_"); modified = sanitizeKotlinSpecificNames(modified); } @@ -721,7 +726,7 @@ public String toModelName(final String name) { return importMapping.get(name); } - String modifiedName = name.replaceAll("\\.", "").replaceAll("-", "_"); + String modifiedName = name.replace(".", "").replace("-", "_"); String nameWithPrefixSuffix = sanitizeKotlinSpecificNames(modifiedName); if (!StringUtils.isEmpty(modelNamePrefix)) { @@ -745,7 +750,7 @@ public String toModelName(final String name) { } // model name starts with number - if (modifiedName.matches("^\\d.*")) { + if (LEADING_DIGIT.matcher(modifiedName).find()) { final String modelName = "Model" + modifiedName; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -778,7 +783,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (LEADING_DIGIT.matcher(operationId).find()) { LOGGER.warn(operationId + " (starting with a number) cannot be used as method name. Renamed to " + camelize("call_" + operationId), LOWERCASE_FIRST_LETTER); operationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); } @@ -806,13 +811,13 @@ private String sanitizeKotlinSpecificNames(final String name) { // Fallback, replace unknowns with underscore. word = NON_WORD_UNICODE.matcher(word).replaceAll("_"); - if (word.matches("\\d.*")) { + if (LEADING_DIGIT.matcher(word).find()) { word = "_" + word; } // _, __, and ___ are reserved in Kotlin. Treat all names with only underscores consistently, regardless of count. - if (word.matches("^_*$")) { - word = word.replaceAll("\\Q_\\E", "Underscore"); + if (ALL_UNDERSCORES.matcher(word).matches()) { + word = word.replace("_", "Underscore"); } return word; @@ -1019,7 +1024,7 @@ protected String toVariableName(String name) { name = sanitizeName(name, "\\W-[\\$]"); name = sanitizeKotlinSpecificNames(name); - if (name.toLowerCase(Locale.ROOT).matches("^_*class$")) { + if (UNDERSCORE_CLASS.matcher(name.toLowerCase(Locale.ROOT)).matches()) { return "propertyClass"; } @@ -1028,7 +1033,7 @@ protected String toVariableName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z0-9_]*$")) { + if (ALL_UPPER_ALPHANUMERIC_US.matcher(name).matches()) { return name; } @@ -1049,7 +1054,7 @@ protected String toVariableName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number or containing dollar symbol, escape it - if (isReservedWord(name) || name.matches("(^\\d.*)|(.*[$].*)")) { + if (isReservedWord(name) || LEADING_DIGIT_OR_DOLLAR.matcher(name).matches()) { name = escapeReservedWord(name); } From d74b79662b72a290066c8595301115e07ead875a Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 17 Mar 2026 10:05:11 +0100 Subject: [PATCH 12/22] fix working with regex patterns --- .../codegen/languages/AbstractCppCodegen.java | 11 +++-- .../languages/AbstractDartCodegen.java | 18 ++++--- .../languages/AbstractEiffelCodegen.java | 21 +++++--- .../languages/AbstractFSharpCodegen.java | 23 ++++++--- .../languages/AbstractGraphQLCodegen.java | 25 ++++++---- .../languages/AbstractJavaCodegen.java | 49 +++++++++++-------- .../languages/AbstractJuliaCodegen.java | 39 +++++++++------ .../codegen/languages/AbstractPhpCodegen.java | 41 ++++++++++------ .../languages/AbstractPythonCodegen.java | 27 ++++++---- 9 files changed, 156 insertions(+), 98 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java index 8950c1d4b761..7ee79fb43d2c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java @@ -38,10 +38,13 @@ import java.io.File; import java.net.URL; import java.util.*; +import java.util.regex.Pattern; abstract public class AbstractCppCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractCppCodegen.class); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + protected static final String RESERVED_WORD_PREFIX_OPTION = "reservedWordPrefix"; protected static final String RESERVED_WORD_PREFIX_DESC = "Prefix to prepend to reserved words in order to avoid conflicts"; protected String reservedWordPrefix = "r_"; @@ -159,7 +162,7 @@ public AbstractCppCodegen() { @SuppressWarnings("static-method") public String sanitizeName(String name) { String sanitizedName = super.sanitizeName(name); - sanitizedName = sanitizedName.replaceAll("-", ""); + sanitizedName = sanitizedName.replace("-", ""); return sanitizedName; } @@ -220,7 +223,7 @@ public String toVarName(String name) { return sanitizeName(name); } - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { return escapeReservedWord(name); } @@ -262,7 +265,7 @@ public String toParamName(String name) { return parameterNameMapping.get(name); } - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { return escapeReservedWord(name); } @@ -279,7 +282,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required) { } else { nameInCamelCase = sanitizeName(nameInCamelCase); } - if (isReservedWord(nameInCamelCase) || nameInCamelCase.matches("^\\d.*")) { + if (isReservedWord(nameInCamelCase) || STARTS_WITH_DIGIT.matcher(nameInCamelCase).matches()) { nameInCamelCase = escapeReservedWord(nameInCamelCase); } property.nameInCamelCase = nameInCamelCase; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java index 4d7eb4dbe7c1..b1d26462f142 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractDartCodegen.java @@ -26,6 +26,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.regex.Pattern; import java.util.stream.Stream; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; @@ -35,6 +36,11 @@ public abstract class AbstractDartCodegen extends DefaultCodegen { private final Logger LOGGER = LoggerFactory.getLogger(AbstractDartCodegen.class); + private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern STARTS_WITH_OPT_NEG_DIGIT = Pattern.compile("^-?\\d.*"); + protected static final List DEFAULT_SUPPORTED_CONTENT_TYPES = Arrays.asList( "application/json", "application/x-www-form-urlencoded", "multipart/form-data"); @@ -413,10 +419,10 @@ public String toVarName(String name) { if (name.equals("_")) { return "underscore"; } - name = name.replaceAll("^_", ""); + name = LEADING_UNDERSCORE.matcher(name).replaceAll(""); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -432,7 +438,7 @@ public String toVarName(String name) { // pet_id => petId name = camelize(name, LOWERCASE_FIRST_LETTER); - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "n" + name; } @@ -496,7 +502,7 @@ public String toModelName(final String name) { } // model name starts with number - if (camelizedName.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(camelizedName).matches()) { final String modelName = "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); return modelName; @@ -874,7 +880,7 @@ public String toEnumVarName(String value, String datatype) { if (("number".equalsIgnoreCase(datatype) || "double".equalsIgnoreCase(datatype) || "int".equalsIgnoreCase(datatype)) && - value.matches("^-?\\d.*")) { + STARTS_WITH_OPT_NEG_DIGIT.matcher(value).matches()) { // Only rename numeric values when the datatype is numeric // AND the name is not changed by enum extensions (matches a numeric value). boolean isNegative = value.startsWith("-"); @@ -907,7 +913,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { String newOperationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, newOperationId); operationId = newOperationId; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java index 83f3fac174f5..ca210b451526 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -40,6 +41,10 @@ public abstract class AbstractEiffelCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractEiffelCodegen.class); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern UNCAMELIZE_UPPER = Pattern.compile("(.)(\\p{Upper})"); + private final Set parentModels = new HashSet<>(); private final Multimap childrenByParent = ArrayListMultimap.create(); @@ -126,7 +131,7 @@ public String escapeReservedWord(String name) { if (this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } - if (name.matches("^\\d.*")) {// prepend var_ + if (STARTS_WITH_DIGIT.matcher(name).matches()) {// prepend var_ return "var_" + name; } return "var_" + name; @@ -135,10 +140,10 @@ public String escapeReservedWord(String name) { @Override public String toVarName(String name) { // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(name.replace("-", "_")); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -156,7 +161,7 @@ public String toVarName(String name) { } // for reserved word or word starting with number, append - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -201,7 +206,7 @@ public String toModelFilename(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, "model_" + name); name = "model_" + name; // e.g. 200Response => Model200Response @@ -221,7 +226,7 @@ public String toModelFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be + name = name.replace("-", "_"); // FIXME: a parameter should not be // assigned. Also declare the // methods parameters as 'final'. @@ -348,7 +353,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn(operationId + " (starting with a number) cannot be used as method sname. Renamed to " + camelize("call_" + operationId), true); sanitizedOperationId = camelize("call_" + sanitizedOperationId, LOWERCASE_FIRST_LETTER); } @@ -584,7 +589,7 @@ public String toInstantiationType(Schema p) { } public String unCamelize(String name) { - return name.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase(Locale.ROOT); + return UNCAMELIZE_UPPER.matcher(name).replaceAll("$1_$2").toLowerCase(Locale.ROOT); } public String toEiffelFeatureStyle(String operationId) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java index 7ca5e99015c2..69e57dbf173a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java @@ -36,6 +36,7 @@ import java.io.File; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.CodegenConstants.X_ENUM_BYTE; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; @@ -44,6 +45,12 @@ public abstract class AbstractFSharpCodegen extends DefaultCodegen implements CodegenConfig { + // ...existing code... + + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); + protected boolean optionalAssemblyInfoFlag = true; protected boolean optionalProjectFileFlag = true; @@ -627,7 +634,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } @@ -670,14 +677,14 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } name = getNameUsingModelPropertyNaming(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } return name; @@ -689,10 +696,10 @@ public String toParamName(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = name.replace("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -701,7 +708,7 @@ public String toParamName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -895,7 +902,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name)); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -964,7 +971,7 @@ public String toEnumVarName(String name, String datatype) { enumName = camelize(enumName) + "Enum"; - if (enumName.matches("\\d.*")) { // starts with number + if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java index 0d383f375a61..939574d7784c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java @@ -29,6 +29,7 @@ import java.io.File; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -38,6 +39,10 @@ public abstract class AbstractGraphQLCodegen extends DefaultCodegen implements C private final Logger LOGGER = LoggerFactory.getLogger(AbstractGraphQLCodegen.class); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); + protected String specFolder = "spec"; @Setter protected String packageName = "openapi2graphql"; @Setter protected String packageVersion = "1.0.0"; @@ -147,10 +152,10 @@ public String modelFileFolder() { @Override public String toVarName(String name) { // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(name.replace("-", "_")); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) return name; name = camelize(name, LOWERCASE_FIRST_LETTER); @@ -160,7 +165,7 @@ public String toVarName(String name) { name = escapeReservedWord(name); // for reserved word or word starting with number, append _ - if (name.matches("^\\d.*")) + if (STARTS_WITH_DIGIT.matcher(name).matches()) name = camelize("var_" + name); return name; @@ -195,7 +200,7 @@ public String toModelFilename(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, "model_" + name); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -207,7 +212,7 @@ public String toModelFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = name.replace("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. PetApi.graphql => pet_api.graphql return underscore(name) + "_api"; @@ -371,9 +376,9 @@ public String toEnumVarName(String name, String datatype) { // number if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = varName.replace("-", "MINUS_"); + varName = varName.replace("+", "PLUS_"); + varName = varName.replace(".", "_DOT_"); return varName; } @@ -387,7 +392,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + if (isReservedWord(enumName) || STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -401,7 +406,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index 15e53ad7100d..bda30d498aca 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -85,6 +85,15 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code private static final ZoneId UTC = ZoneId.of("UTC"); private static final Pattern LOMBOK_ANNOTATION = Pattern.compile("@lombok.(\\w+\\.)*(?\\w+)(\\(.*?\\))?"); private static final Pattern JAVA_UTIL_IMPORT = Pattern.compile("java\\.util\\.(List|ArrayList|Map|HashMap)"); + private static final Pattern STARTS_WITH_UNDERSCORE_CLASS = Pattern.compile("^_*class$"); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern ALL_UPPER_CASE_DIGITS_UNDERSCORE = Pattern.compile("^[A-Z0-9_]*$"); + private static final Pattern TAB_NEWLINE_RETURN = Pattern.compile("[\\t\\n\\r]"); + private static final Pattern ANNOTATION_IN_TYPE = Pattern.compile("(?:(?i)@[a-z0-9]*+([(].*[)]|\\s*))*+"); + private static final Pattern NON_ALPHANUMERIC = Pattern.compile("\\P{Alnum}"); + private static final Pattern NON_WORD_CHARS = Pattern.compile("\\W+"); + private static final Pattern INVALID_PACKAGE_CHARS = Pattern.compile("[^a-zA-Z0-9_.]"); + private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); public static final String DEFAULT_LIBRARY = ""; public static final String DATE_LIBRARY = "dateLibrary"; @@ -922,7 +931,7 @@ public String toVarName(String name) { // sanitize name name = sanitizeName(name, "\\W-[\\$]"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - if (name.toLowerCase(Locale.ROOT).matches("^_*class$")) { + if (STARTS_WITH_UNDERSCORE_CLASS.matcher(name.toLowerCase(Locale.ROOT)).matches()) { return "propertyClass"; } @@ -931,12 +940,12 @@ public String toVarName(String name) { } // numbers are not allowed at the beginning - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } // if it's all upper case, do nothing - if (name.matches("^[A-Z0-9_]*$")) { + if (ALL_UPPER_CASE_DIGITS_UNDERSCORE.matcher(name).matches()) { return name; } @@ -961,7 +970,7 @@ public String toVarName(String name) { } // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -1037,7 +1046,7 @@ public String toModelName(final String name) { } // model name starts with number - if (camelizedName.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(camelizedName).matches()) { final String modelName = "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) schemaKeyToModelNameCache.put(origName, modelName); LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, @@ -1211,10 +1220,10 @@ private String getStringBeanValidation(Schema items) { if (StringUtils.isNotEmpty(items.getPattern())) { final String pattern = escapeUnsafeCharacters( + TAB_NEWLINE_RETURN.matcher( StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(items.getPattern()) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") + .replace("\\/", "/"))).replaceAll(" ") .replace("\\", "\\\\") .replace("\"", "\\\"")); @@ -1834,7 +1843,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn(operationId + " (starting with a number) cannot be used as method name. Renamed to " + camelize("call_" + operationId), true); operationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); } @@ -1985,7 +1994,7 @@ public void postProcessResponseWithProperty(CodegenResponse response, CodegenPro */ public String removeAnnotations(String dataType) { if (dataType != null && dataType.contains("@")) { - return dataType.replaceAll("(?:(?i)@[a-z0-9]*+([(].*[)]|\\s*))*+", ""); + return ANNOTATION_IN_TYPE.matcher(dataType).replaceAll(""); } return dataType; } @@ -1999,8 +2008,8 @@ public String removeAnnotations(String dataType) { */ public String sanitizeDataType(String dataType) { String content = removeAnnotations(dataType); - if (content != null && content.matches(".*\\P{Alnum}.*")) { - content = content.replaceAll("\\P{Alnum}", ""); + if (content != null && NON_ALPHANUMERIC.matcher(content).find()) { + content = NON_ALPHANUMERIC.matcher(content).replaceAll(""); } return content; } @@ -2253,9 +2262,9 @@ public String toEnumVarName(String value, String datatype) { if ("Integer".equals(datatype) || "Long".equals(datatype) || "Float".equals(datatype) || "Double".equals(datatype) || "BigDecimal".equals(datatype)) { String varName = "NUMBER_" + value; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = varName.replace("-", "MINUS_"); + varName = varName.replace("+", "PLUS_"); + varName = varName.replace(".", "_DOT_"); return varName; } @@ -2264,7 +2273,7 @@ public String toEnumVarName(String value, String datatype) { switch (getEnumPropertyNaming()) { case legacy: // legacy ,e.g. WITHNUMBER1 - var = value.replaceAll("\\W+", "_").toUpperCase(Locale.ROOT); + var = NON_WORD_CHARS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); break; case original: // keep value as it is, if meets language naming convention @@ -2277,10 +2286,10 @@ public String toEnumVarName(String value, String datatype) { break; default: // default to MACRO_CASE, e.g. WITH_NUMBER1 - var = underscore(value.replaceAll("\\W+", "_")).toUpperCase(Locale.ROOT); + var = underscore(NON_WORD_CHARS.matcher(value).replaceAll("_")).toUpperCase(Locale.ROOT); break; } - if (var.matches("\\d.*")) { + if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(var).matches()) { var = "_" + var; } return this.toVarName(var); @@ -2357,7 +2366,7 @@ private static CodegenModel reconcileInlineEnums(CodegenModel codegenModel, Code private static String sanitizePackageName(String packageName) { packageName = packageName.trim(); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - packageName = packageName.replaceAll("[^a-zA-Z0-9_\\.]", "_"); + packageName = INVALID_PACKAGE_CHARS.matcher(packageName).replaceAll("_"); if (Strings.isNullOrEmpty(packageName)) { return "invalidPackageName"; } @@ -2366,7 +2375,7 @@ private static String sanitizePackageName(String packageName) { private String sanitizePath(String p) { //prefer replace a ", instead of a fuLL URL encode for readability - return p.replaceAll("\"", "%22"); + return p.replace("\"", "%22"); } @Override @@ -2468,7 +2477,7 @@ public String sanitizeTag(String tag) { tag = camelize(underscore(sanitizeName(tag))); // tag starts with numbers - if (tag.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(tag).matches()) { tag = "Class" + tag; } return tag; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java index ea5a868b2fa0..0cf3632bb441 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java @@ -40,12 +40,19 @@ import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.StringUtils.camelize; public abstract class AbstractJuliaCodegen extends DefaultCodegen { protected final Logger LOGGER = LoggerFactory.getLogger(AbstractJuliaCodegen.class); + private static final Pattern SANITIZE_NON_ALPHANUM_BRACE = Pattern.compile("[^a-zA-Z0-9_{}]"); + private static final Pattern ALNUM_UNDERSCORE = Pattern.compile("[a-zA-Z0-9_]*"); + private static final Pattern VAR_QUOTED = Pattern.compile("var\".*\""); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern DOUBLE_BACKSLASH = Pattern.compile("\\\\\\\\"); + protected String srcPath = "src"; protected String apiSrcPath = srcPath + "/apis/"; protected String modelSrcPath = srcPath + "/models/"; @@ -157,7 +164,7 @@ public GeneratorLanguage generatorLanguage() { } protected static String dropDots(String str) { - return str.replaceAll("\\.", "_"); + return str.replace(".", "_"); } /** @@ -213,7 +220,7 @@ public String toModelDocFilename(String name) { @Override public String toApiFilename(String name) { - name = name.replaceAll("-", "_"); + name = name.replace("-", "_"); return "api_" + camelize(name) + "Api"; } @@ -278,20 +285,20 @@ public String sanitizeName(String name) { return "value"; } - name = name.replaceAll("\\[\\]", ""); - name = name.replaceAll("\\[", "_"); - name = name.replaceAll("\\]", ""); - name = name.replaceAll("\\(", "_"); - name = name.replaceAll("\\)", ""); - name = name.replaceAll("\\.", "_"); - name = name.replaceAll("-", "_"); - name = name.replaceAll(" ", "_"); - name = name.replaceAll("/", "_"); - return name.replaceAll("[^a-zA-Z0-9_{}]", ""); + name = name.replace("[]", ""); + name = name.replace("[", "_"); + name = name.replace("]", ""); + name = name.replace("(", "_"); + name = name.replace(")", ""); + name = name.replace(".", "_"); + name = name.replace("-", "_"); + name = name.replace(" ", "_"); + name = name.replace("/", "_"); + return SANITIZE_NON_ALPHANUM_BRACE.matcher(name).replaceAll(""); } protected boolean needsVarEscape(String name) { - return (!name.matches("[a-zA-Z0-9_]*") && !name.matches("var\".*\"")) || reservedWords.contains(name); + return (!ALNUM_UNDERSCORE.matcher(name).matches() && !VAR_QUOTED.matcher(name).matches()) || reservedWords.contains(name); } /** @@ -310,7 +317,7 @@ public String toModelName(final String name) { String result = sanitizeName(name); // remove dollar sign - result = result.replaceAll("$", ""); + result = result.replace("$", ""); // model name cannot use reserved keyword, e.g. return if (isReservedWord(result)) { @@ -319,7 +326,7 @@ public String toModelName(final String name) { } // model name starts with number - if (result.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(result).matches()) { LOGGER.warn(result + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + result)); result = "model_" + result; // e.g. 200Response => Model200Response (after camelize) } @@ -514,7 +521,7 @@ public String toRegularExpression(String pattern) { pattern = escapeText(pattern); // escapeText unnecessarily escapes `\` such that `\.` in the regex ends up as `\\.` for example. // we need to restore it back by converting `\\` to `\` - pattern = pattern.replaceAll("\\\\\\\\", "\\\\"); + pattern = DOUBLE_BACKSLASH.matcher(pattern).replaceAll("\\\\"); return pattern; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java index 68520b123d99..2f4aa9c5ce2e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java @@ -46,6 +46,15 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg private final Logger LOGGER = LoggerFactory.getLogger(AbstractPhpCodegen.class); + private static final Pattern TRAILING_SLASH_BACKSLASH = Pattern.compile("[\\\\/]?$"); + private static final Pattern PATH_SEPARATOR_CHARS = Pattern.compile("[\\.\\\\/]"); + private static final Pattern LEADING_AT = Pattern.compile("^@"); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern NON_WORD_BACKSLASH = Pattern.compile("[^\\w\\\\]+"); + private static final Pattern STARTS_WITH_BACKSLASH = Pattern.compile("^\\\\.*"); + private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); + private static final Pattern COMPOSER_PACKAGE_NAME = Pattern.compile("^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$"); + public static final String VARIABLE_NAMING_CONVENTION = "variableNamingConvention"; public static final String PACKAGE_NAME = "packageName"; public static final String SRC_BASE_PATH = "srcBasePath"; @@ -295,13 +304,13 @@ public String toSrcPath(final String packageName, final String basePath) { String modifiedPackageName = packageName.replace(invokerPackage, ""); String modifiedBasePath = basePath; if (basePath != null && !basePath.isEmpty()) { - modifiedBasePath = basePath.replaceAll("[\\\\/]?$", "") + File.separator; + modifiedBasePath = TRAILING_SLASH_BACKSLASH.matcher(basePath).replaceAll("") + File.separator; } // Trim prefix file separators from package path String packagePath = StringUtils.removeStart( // Replace period, backslash, forward slash with file separator in package name - modifiedPackageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), + PATH_SEPARATOR_CHARS.matcher(modifiedPackageName).replaceAll(Matcher.quoteReplacement("/")), File.separator ); @@ -444,7 +453,7 @@ public String toVarName(String name) { // translate @ for properties (like @type) to at_. // Otherwise an additional "type" property will lead to duplicates - name = name.replaceAll("^@", "at_"); + name = LEADING_AT.matcher(name).replaceAll("at_"); // sanitize name name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. @@ -465,7 +474,7 @@ public String toVarName(String name) { // parameter name starting with number won't compile // need to escape it by appending _ at the beginning - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -485,10 +494,10 @@ public String toParamName(String name) { private String toGenericName(String name) { // remove [ - name = name.replaceAll("\\]", ""); + name = name.replace("]", ""); // Note: backslash ("\\") is allowed for e.g. "\\DateTime" - name = name.replaceAll("[^\\w\\\\]+", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = NON_WORD_BACKSLASH.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // remove dollar sign name = name.replace("$", ""); @@ -500,7 +509,7 @@ private String toGenericName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name)); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -525,7 +534,7 @@ public String toModelName(String name) { name = toGenericName(name); // add prefix and/or suffix only if name does not start with \ (e.g. \DateTime) - if (!name.matches("^\\\\.*")) { + if (!STARTS_WITH_BACKSLASH.matcher(name).matches()) { if (!StringUtils.isEmpty(modelNamePrefix)) { name = modelNamePrefix + "_" + name; } @@ -598,7 +607,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER)); operationId = "call_" + operationId; } @@ -764,12 +773,12 @@ public String toEnumVarName(String name, String datatype) { // number if ("int".equals(datatype) || "float".equals(datatype)) { - if (name.matches("\\d.*")) { // starts with number + if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(name).matches()) { // starts with number name = "NUMBER_" + name; } - name = name.replaceAll("-", "MINUS_"); - name = name.replaceAll("\\+", "PLUS_"); - name = name.replaceAll("\\.", "_DOT_"); + name = name.replace("-", "MINUS_"); + name = name.replace("+", "PLUS_"); + name = name.replace(".", "_DOT_"); } // string @@ -777,7 +786,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + if (isReservedWord(enumName) || STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -795,7 +804,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; @@ -907,7 +916,7 @@ public String getComposerPackageName() { if ( packageName.contentEquals("/") || packageName.contentEquals("null/null") - || !Pattern.matches("^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$", packageName) + || !COMPOSER_PACKAGE_NAME.matcher(packageName).matches() ) { return ""; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index c4f46d335bfa..27cb92468c12 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -51,6 +51,13 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); private static final Pattern REGEX_VALUE_EXTRACTOR = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); + private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); + private static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); + private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + private static final Pattern WHITESPACE = Pattern.compile("\\s+"); + private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); + private static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); + private static final Pattern UNESCAPED_SLASH = Pattern.compile("(? ModelReturn (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", camelizedName, modelName); schemaKeyToModelNameCache.put(origName, modelName); @@ -823,7 +830,7 @@ public String toApiVarName(String name) { } protected static String dropDots(String str) { - return str.replaceAll("\\.", "_"); + return str.replace(".", "_"); } @Override @@ -1114,7 +1121,7 @@ public String toEnumVariableName(String name, String datatype) { name = name.replaceFirst("^_", ""); name = name.replaceFirst("_$", ""); - if (name.matches("\\d.*")) { + if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(name).matches()) { name = "ENUM_" + name.toUpperCase(Locale.ROOT); } @@ -1383,9 +1390,9 @@ public String addRegularExpressionDelimiter(String pattern) { return pattern; } - if (!pattern.matches("^/.*")) { + if (!STARTS_WITH_SLASH.matcher(pattern).matches()) { // Perform a negative lookbehind on each `/` to ensure that it is escaped. - return "/" + pattern.replaceAll("(? Date: Tue, 17 Mar 2026 10:25:12 +0100 Subject: [PATCH 13/22] fix working with regex patterns --- .../org/openapitools/codegen/DefaultCodegen.java | 11 ++++++++--- .../codegen/languages/AbstractApexCodegen.java | 5 +---- .../codegen/languages/AbstractCSharpCodegen.java | 1 - .../codegen/languages/AbstractCppCodegen.java | 2 -- .../codegen/languages/AbstractDartCodegen.java | 2 -- .../codegen/languages/AbstractEiffelCodegen.java | 2 -- .../codegen/languages/AbstractFSharpCodegen.java | 6 +----- .../codegen/languages/AbstractGoCodegen.java | 1 - .../codegen/languages/AbstractGraphQLCodegen.java | 8 ++------ .../codegen/languages/AbstractJavaCodegen.java | 12 ++++-------- .../codegen/languages/AbstractJuliaCodegen.java | 1 - .../codegen/languages/AbstractPhpCodegen.java | 8 +++----- .../codegen/languages/AbstractPythonCodegen.java | 5 +---- 13 files changed, 20 insertions(+), 44 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index e5efef4cb69e..5681c6c023c5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -252,11 +252,16 @@ apiTemplateFiles are for API outputs only (controllers/handlers). /** Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. */ private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); /** Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. */ - private static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); + protected static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); /** Matches a string that starts with a digit; used in {@link #toEnumVarName}. */ - private static final Pattern LEADING_DIGIT = Pattern.compile("\\d.*"); + protected static final Pattern LEADING_DIGIT = Pattern.compile("\\d.*"); + + /** Matches a string that starts with a digit (anchored); used across language generators. */ + protected static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + /** Matches a string consisting entirely of uppercase letters and underscores. */ + protected static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); /** Matches tab, newline, or carriage-return; used in {@link #escapeText}. */ - private static final Pattern CONTROL_WHITESPACE = Pattern.compile("[\\t\\n\\r]"); + protected static final Pattern CONTROL_WHITESPACE = Pattern.compile("[\\t\\n\\r]"); /** Matches a callback path-expression parameter like {@code {$request.body#/id}}. */ private static final Pattern CALLBACK_EXPRESSION_PARAM = Pattern.compile("\\{\\$.*}"); // Dynamic patterns keyed by user-supplied removeCharRegEx strings are cached via PatternCache. diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java index f4ba65aa9a25..500e0df36ad3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java @@ -41,9 +41,6 @@ public abstract class AbstractApexCodegen extends DefaultCodegen implements Code private final Logger LOGGER = LoggerFactory.getLogger(AbstractApexCodegen.class); private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); - private static final Pattern LEADING_DIGITS_ONLY = Pattern.compile("^\\d.*"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); - private static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); private static final Pattern DATE_FORMAT = Pattern.compile("^\\d{4}(-\\d{2}){2}"); private static final Pattern DATETIME_FORMAT = Pattern.compile("^\\d{4}([-T:]\\d{2}){5}.+"); private static final Pattern NON_NUMERIC = Pattern.compile("[^-0-9.]"); @@ -94,7 +91,7 @@ public String sanitizeName(String name) { name = name.replace("__", "_"); } if (LEADING_DIGIT.matcher(name).find()) { // Prevent named credentials with leading number - name = LEADING_DIGITS_ONLY.matcher(name).replaceAll(""); + name = STARTS_WITH_DIGIT.matcher(name).replaceAll(""); } return name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java index 18d92aeb6b6b..3cdd95fce352 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java @@ -57,7 +57,6 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen { /** Matches a regex literal that carries modifier flags after the closing slash, e.g. {@code /foo/i}. */ private static final Pattern HAS_MODIFIERS = Pattern.compile(".*/[gmiyuvsdlnx]+$"); private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); private static final Pattern UNESCAPED_DOUBLE_QUOTE = Pattern.compile("(? DEFAULT_SUPPORTED_CONTENT_TYPES = Arrays.asList( diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java index ca210b451526..a68977d1e297 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractEiffelCodegen.java @@ -41,8 +41,6 @@ public abstract class AbstractEiffelCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractEiffelCodegen.class); - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); private static final Pattern UNCAMELIZE_UPPER = Pattern.compile("(.)(\\p{Upper})"); private final Set parentModels = new HashSet<>(); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java index 69e57dbf173a..65f17da72b56 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java @@ -36,7 +36,6 @@ import java.io.File; import java.util.*; -import java.util.regex.Pattern; import static org.openapitools.codegen.CodegenConstants.X_ENUM_BYTE; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; @@ -47,9 +46,6 @@ public abstract class AbstractFSharpCodegen extends DefaultCodegen implements Co // ...existing code... - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); - private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); protected boolean optionalAssemblyInfoFlag = true; protected boolean optionalProjectFileFlag = true; @@ -971,7 +967,7 @@ public String toEnumVarName(String name, String datatype) { enumName = camelize(enumName) + "Enum"; - if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java index 6bee38a6d802..5a15fecf6029 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java @@ -41,7 +41,6 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege private static final String NUMERIC_ENUM_PREFIX = "_"; private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); private static final Pattern LEADING_TRAILING_SLASH = Pattern.compile("^/|/$"); @Setter diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java index 939574d7784c..35d799005072 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java @@ -29,7 +29,6 @@ import java.io.File; import java.util.*; -import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -39,9 +38,6 @@ public abstract class AbstractGraphQLCodegen extends DefaultCodegen implements C private final Logger LOGGER = LoggerFactory.getLogger(AbstractGraphQLCodegen.class); - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); - private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); protected String specFolder = "spec"; @Setter protected String packageName = "openapi2graphql"; @@ -392,7 +388,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // reserved word or starts with number + if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -406,7 +402,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index bda30d498aca..0258571fd09c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -86,14 +86,10 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code private static final Pattern LOMBOK_ANNOTATION = Pattern.compile("@lombok.(\\w+\\.)*(?\\w+)(\\(.*?\\))?"); private static final Pattern JAVA_UTIL_IMPORT = Pattern.compile("java\\.util\\.(List|ArrayList|Map|HashMap)"); private static final Pattern STARTS_WITH_UNDERSCORE_CLASS = Pattern.compile("^_*class$"); - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); private static final Pattern ALL_UPPER_CASE_DIGITS_UNDERSCORE = Pattern.compile("^[A-Z0-9_]*$"); - private static final Pattern TAB_NEWLINE_RETURN = Pattern.compile("[\\t\\n\\r]"); private static final Pattern ANNOTATION_IN_TYPE = Pattern.compile("(?:(?i)@[a-z0-9]*+([(].*[)]|\\s*))*+"); private static final Pattern NON_ALPHANUMERIC = Pattern.compile("\\P{Alnum}"); - private static final Pattern NON_WORD_CHARS = Pattern.compile("\\W+"); private static final Pattern INVALID_PACKAGE_CHARS = Pattern.compile("[^a-zA-Z0-9_.]"); - private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); public static final String DEFAULT_LIBRARY = ""; public static final String DATE_LIBRARY = "dateLibrary"; @@ -1220,7 +1216,7 @@ private String getStringBeanValidation(Schema items) { if (StringUtils.isNotEmpty(items.getPattern())) { final String pattern = escapeUnsafeCharacters( - TAB_NEWLINE_RETURN.matcher( + CONTROL_WHITESPACE.matcher( StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(items.getPattern()) .replace("\\/", "/"))).replaceAll(" ") @@ -2273,7 +2269,7 @@ public String toEnumVarName(String value, String datatype) { switch (getEnumPropertyNaming()) { case legacy: // legacy ,e.g. WITHNUMBER1 - var = NON_WORD_CHARS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); + var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); break; case original: // keep value as it is, if meets language naming convention @@ -2286,10 +2282,10 @@ public String toEnumVarName(String value, String datatype) { break; default: // default to MACRO_CASE, e.g. WITH_NUMBER1 - var = underscore(NON_WORD_CHARS.matcher(value).replaceAll("_")).toUpperCase(Locale.ROOT); + var = underscore(NON_WORD_PLUS.matcher(value).replaceAll("_")).toUpperCase(Locale.ROOT); break; } - if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(var).matches()) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { var = "_" + var; } return this.toVarName(var); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java index 0cf3632bb441..c486279217eb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJuliaCodegen.java @@ -50,7 +50,6 @@ public abstract class AbstractJuliaCodegen extends DefaultCodegen { private static final Pattern SANITIZE_NON_ALPHANUM_BRACE = Pattern.compile("[^a-zA-Z0-9_{}]"); private static final Pattern ALNUM_UNDERSCORE = Pattern.compile("[a-zA-Z0-9_]*"); private static final Pattern VAR_QUOTED = Pattern.compile("var\".*\""); - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); private static final Pattern DOUBLE_BACKSLASH = Pattern.compile("\\\\\\\\"); protected String srcPath = "src"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java index 2f4aa9c5ce2e..71dfb363ac40 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java @@ -49,10 +49,8 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg private static final Pattern TRAILING_SLASH_BACKSLASH = Pattern.compile("[\\\\/]?$"); private static final Pattern PATH_SEPARATOR_CHARS = Pattern.compile("[\\.\\\\/]"); private static final Pattern LEADING_AT = Pattern.compile("^@"); - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); private static final Pattern NON_WORD_BACKSLASH = Pattern.compile("[^\\w\\\\]+"); private static final Pattern STARTS_WITH_BACKSLASH = Pattern.compile("^\\\\.*"); - private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); private static final Pattern COMPOSER_PACKAGE_NAME = Pattern.compile("^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$"); public static final String VARIABLE_NAMING_CONVENTION = "variableNamingConvention"; @@ -773,7 +771,7 @@ public String toEnumVarName(String name, String datatype) { // number if ("int".equals(datatype) || "float".equals(datatype)) { - if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(name).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(name).matches()) { // starts with number name = "NUMBER_" + name; } name = name.replace("-", "MINUS_"); @@ -786,7 +784,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // reserved word or starts with number + if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -804,7 +802,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (STARTS_WITH_DIGIT_NO_ANCHOR.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index 27cb92468c12..fc5f24d8ff4f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -51,11 +51,8 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); private static final Pattern REGEX_VALUE_EXTRACTOR = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); - private static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); private static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); - private static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); private static final Pattern WHITESPACE = Pattern.compile("\\s+"); - private static final Pattern STARTS_WITH_DIGIT_NO_ANCHOR = Pattern.compile("\\d.*"); private static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); private static final Pattern UNESCAPED_SLASH = Pattern.compile("(? Date: Wed, 18 Mar 2026 02:09:16 +0100 Subject: [PATCH 14/22] fix working with regex patterns. Add duration logging to GenerateBatch --- .../codegen/cmd/GenerateBatch.java | 12 +++++++-- .../openapitools/codegen/DefaultCodegen.java | 13 ++++++++-- .../codegen/languages/AbstractAdaCodegen.java | 1 - .../languages/AbstractCSharpCodegen.java | 11 ++++---- .../languages/AbstractJavaCodegen.java | 4 +-- .../languages/AbstractPythonCodegen.java | 5 ---- .../AbstractPythonPydanticV1Codegen.java | 25 ++++++++++--------- .../languages/AbstractRubyCodegen.java | 8 +++--- .../languages/AbstractRustCodegen.java | 2 +- .../languages/AbstractScalaCodegen.java | 6 ++--- .../AbstractTypeScriptClientCodegen.java | 6 ++--- .../languages/AndroidClientCodegen.java | 6 ++--- .../codegen/languages/BashClientCodegen.java | 2 +- .../languages/CLibcurlClientCodegen.java | 16 ++++++------ .../languages/CSharpClientCodegen.java | 8 +++--- .../languages/CSharpReducedClientCodegen.java | 8 +++--- .../languages/CppQtAbstractCodegen.java | 4 +-- .../languages/CppUE4ClientCodegen.java | 4 +-- .../languages/CrystalClientCodegen.java | 12 ++++----- .../languages/EiffelClientCodegen.java | 4 +-- .../languages/ElixirClientCodegen.java | 4 +-- .../codegen/languages/ElmClientCodegen.java | 4 +-- .../GraphQLNodeJSExpressServerCodegen.java | 2 +- .../languages/HaskellHttpClientCodegen.java | 11 ++++---- .../languages/JavaDubboServerCodegen.java | 3 +-- .../JavascriptApolloClientCodegen.java | 8 +++--- .../languages/JavascriptClientCodegen.java | 8 +++--- ...JavascriptClosureAngularClientCodegen.java | 4 +-- .../codegen/languages/K6ClientCodegen.java | 4 +-- .../codegen/languages/LuaClientCodegen.java | 10 ++++---- .../codegen/languages/NimClientCodegen.java | 6 ++--- .../codegen/languages/OCamlClientCodegen.java | 2 +- .../codegen/languages/ObjcClientCodegen.java | 4 +-- .../codegen/languages/PerlClientCodegen.java | 6 ++--- .../languages/PhpSilexServerCodegen.java | 2 +- .../languages/PhpSlim4ServerCodegen.java | 6 ++--- .../languages/PowerShellClientCodegen.java | 12 ++++----- .../languages/ProtobufSchemaCodegen.java | 19 ++++++-------- .../codegen/languages/RClientCodegen.java | 12 ++++----- .../codegen/languages/RubyClientCodegen.java | 8 +++--- .../ScalaPlayFrameworkServerCodegen.java | 4 +-- .../languages/Swift5ClientCodegen.java | 18 ++++++------- .../languages/Swift6ClientCodegen.java | 18 ++++++------- .../languages/SwiftCombineClientCodegen.java | 14 +++++------ .../languages/TypeScriptClientCodegen.java | 9 +++---- .../codegen/languages/WsdlSchemaCodegen.java | 2 +- .../codegen/languages/XojoClientCodegen.java | 16 ++++++------ .../codegen/utils/PatternCacheTest.java | 2 +- 48 files changed, 190 insertions(+), 185 deletions(-) diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java index f2823ce366d8..284022d16e3e 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/GenerateBatch.java @@ -148,6 +148,8 @@ public void execute() { ExecutorService executor = Executors.newFixedThreadPool(numThreads); + long startTime = System.currentTimeMillis(); + // Execute each configurator on a separate pooled thread. configurators.forEach(configurator -> { GenerationRunner runner = new GenerationRunner(configurator, rootDir, Boolean.TRUE.equals(failFast), Boolean.TRUE.equals(clean)); @@ -163,12 +165,18 @@ public void execute() { executor.awaitTermination(awaitFor, TimeUnit.MINUTES); + long elapsedMs = System.currentTimeMillis() - startTime; + long minutes = TimeUnit.MILLISECONDS.toMinutes(elapsedMs); + long seconds = TimeUnit.MILLISECONDS.toSeconds(elapsedMs) % 60; + long millis = elapsedMs % 1000; + String elapsed = String.format(Locale.ROOT, "%dm %ds %dms", minutes, seconds, millis); + int failCount = failures.intValue(); if (failCount > 0) { - System.err.println(String.format(Locale.ROOT, "[FAIL] Completed with %d failures, %d successes", failCount, successes.intValue())); + System.err.println(String.format(Locale.ROOT, "[FAIL] Completed with %d failures, %d successes in %s", failCount, successes.intValue(), elapsed)); System.exit(1); } else { - System.out.println(String.format(Locale.ROOT, "[SUCCESS] Batch generation finished %d generators successfully.", successes.intValue())); + System.out.println(String.format(Locale.ROOT, "[SUCCESS] Batch generation finished %d generators successfully in %s.", successes.intValue(), elapsed)); } } catch (InterruptedException e) { e.printStackTrace(); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 5681c6c023c5..6ffdb1d1573c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -250,11 +250,18 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected final static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application/vnd.(.*)+json(;.*)?"); private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); /** Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. */ - private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); + protected static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); /** Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. */ protected static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); /** Matches a string that starts with a digit; used in {@link #toEnumVarName}. */ protected static final Pattern LEADING_DIGIT = Pattern.compile("\\d.*"); + protected static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); + + protected static final Pattern WHITESPACE = Pattern.compile("\\s+"); + + protected static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); + + protected static final Pattern UNESCAPED_SLASH = Pattern.compile("(? Model200Response (after camelize) @@ -1804,7 +1803,7 @@ public String toEnumVarName(String name, String datatype) { enumName = adjustNamingStyle(enumName) + this.enumValueSuffix; - if (LEADING_DIGIT.matcher(enumName).find()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).find()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index 0258571fd09c..c58b4cf98f65 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -459,10 +459,10 @@ public void processOpts() { convertPropertyToStringAndWriteBack(BOOLEAN_GETTER_PREFIX, this::setBooleanGetterPrefix); convertPropertyToBooleanAndWriteBack(IGNORE_ANYOF_IN_ENUM, this::setIgnoreAnyOfInEnum); convertPropertyToTypeAndWriteBack(ADDITIONAL_MODEL_TYPE_ANNOTATIONS, - annotations -> Arrays.asList(annotations.trim().split("\\s*(;|\\r?\\n)\\s*")), + annotations -> Arrays.asList(SPLIT_ON_SEMICOLON_OR_NEWLINE_REGEX.split(annotations.trim())), this::setAdditionalModelTypeAnnotations); convertPropertyToTypeAndWriteBack(ADDITIONAL_ONE_OF_TYPE_ANNOTATIONS, - annotations -> Arrays.asList(annotations.trim().split("\\s*(;|\\r?\\n)\\s*")), + annotations -> Arrays.asList(SPLIT_ON_SEMICOLON_OR_NEWLINE_REGEX.split(annotations.trim())), this::setAdditionalOneOfTypeAnnotations); convertPropertyToTypeAndWriteBack(ADDITIONAL_ENUM_TYPE_ANNOTATIONS, annotations -> Arrays.asList(annotations.split(";")), diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index fc5f24d8ff4f..050dfec39a8f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -49,12 +49,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractPythonCodegen.class); - private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); private static final Pattern REGEX_VALUE_EXTRACTOR = Pattern.compile("^/\\^?(.+?)\\$?/.?$"); - private static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); - private static final Pattern WHITESPACE = Pattern.compile("\\s+"); - private static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); - private static final Pattern UNESCAPED_SLASH = Pattern.compile("(? regexModifiers; @@ -226,7 +227,7 @@ public String toVarName(String name) { name = name.replace("$", ""); // if it's all upper case, convert to lower case - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { name = name.toLowerCase(Locale.ROOT); } @@ -235,10 +236,10 @@ public String toVarName(String name) { name = underscore(name); // remove leading underscore - name = name.replaceAll("^_*", ""); + name = LEADING_UNDERSCORES.matcher(name).replaceAll(""); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -280,7 +281,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } @@ -704,7 +705,7 @@ public String toModelName(String name) { // remove dollar sign sanitizedName = sanitizedName.replace("$", ""); // remove whitespace - sanitizedName = sanitizedName.replaceAll("\\s+", ""); + sanitizedName = WHITESPACE.matcher(sanitizedName).replaceAll(""); String nameWithPrefixSuffix = sanitizedName; if (!StringUtils.isEmpty(modelNamePrefix)) { @@ -730,7 +731,7 @@ public String toModelName(String name) { } // model name starts with number - if (camelizedName.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(camelizedName).matches()) { String modelName = "Model" + camelizedName; // e.g. return => ModelReturn (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", camelizedName, modelName); schemaKeyToModelNameCache.put(origName, modelName); @@ -1630,7 +1631,7 @@ public String toEnumVariableName(String name, String datatype) { name = name.replaceFirst("^_", ""); name = name.replaceFirst("_$", ""); - if (name.matches("\\d.*")) { + if (LEADING_DIGIT.matcher(name).matches()) { name = "ENUM_" + name.toUpperCase(Locale.ROOT); } @@ -1931,9 +1932,9 @@ public String addRegularExpressionDelimiter(String pattern) { return pattern; } - if (!pattern.matches("^/.*")) { + if (!STARTS_WITH_SLASH.matcher(pattern).matches()) { // Perform a negative lookbehind on each `/` to ensure that it is escaped. - return "/" + pattern.replaceAll("(? Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -601,7 +601,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn(operationId + " (starting with a number) cannot be used as method sname. Renamed to " + camelize("call_" + operationId), true); operationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index 9f09d9c5cb14..56225f5f58cf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -601,7 +601,7 @@ public String toVarName(String name) { } private String toSafeIdentifier(String name) { - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } return name; @@ -650,7 +650,7 @@ protected String toTypescriptTypeName(final String name, String safePrefix) { } // model name starts with number - if (sanName.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(sanName).matches()) { String modelName = safePrefix + sanName; // e.g. 200Response => Model200Response LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", sanName, modelName); @@ -979,7 +979,7 @@ public String toEnumVarName(String name, String datatype) { varName = getNameUsingEnumPropertyNaming(varName); - if (varName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(varName).matches()) { // starts with number return "_" + varName; } else { return varName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java index b94c939f8e1a..7fc035f0127a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java @@ -263,7 +263,7 @@ public String toVarName(String name) { name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -272,7 +272,7 @@ public String toVarName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -308,7 +308,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { String modelName = "Model" + name; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java index 2fa2b92d03fd..41639a960147 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java @@ -787,7 +787,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java index 406c32d0f965..f52974777562 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java @@ -605,14 +605,14 @@ public String toVarName(String name) { // sanitize name name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // if it's all upper case, convert to lower case - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { name = name.toLowerCase(Locale.ROOT); } name = underscore(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -627,7 +627,7 @@ public String toParamName(String name) { } // should be the same as variable name - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } name = name.replaceAll("-", "_"); @@ -659,7 +659,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name)); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -722,7 +722,7 @@ public String toEnumValue(String value, String datatype) { if ("Integer".equals(datatype) || "Float".equals(datatype)) { return value; } else { - if (value.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(value).matches()) { // starts with number return escapeReservedWord(escapeText(value)); } else { return escapeText(value); @@ -750,7 +750,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -763,7 +763,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -793,7 +793,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { String newOperationId = camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER); LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, newOperationId); return newOperationId; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java index e087bfda2a25..73a33a60c9bc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java @@ -1325,9 +1325,9 @@ public String toEnumVarName(String value, String datatype) { // string String var = value.replaceAll(" ", "_"); var = camelize(var); - var = var.replaceAll("\\W+", ""); + var = NON_WORD_PLUS.matcher(var).replaceAll(""); - if (var.matches("\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { return "_" + var; } else { return var; @@ -1350,14 +1350,14 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } name = getNameUsingModelPropertyNaming(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java index 99ba87ce923e..a821d612fe48 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java @@ -849,9 +849,9 @@ public String toEnumVarName(String value, String datatype) { // string String var = value.replaceAll(" ", "_"); var = camelize(var); - var = var.replaceAll("\\W+", ""); + var = NON_WORD_PLUS.matcher(var).replaceAll(""); - if (var.matches("\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { return "_" + var; } else { return var; @@ -874,14 +874,14 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } name = getNameUsingModelPropertyNaming(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQtAbstractCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQtAbstractCodegen.java index 82f87d6b8008..758e66fe19fe 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQtAbstractCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQtAbstractCodegen.java @@ -304,7 +304,7 @@ public String toVarName(String name) { varName = sanitizeName(name); // if it's all upper case, convert to lower case - if (varName.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(varName).matches()) { varName = varName.toLowerCase(Locale.ROOT); } @@ -313,7 +313,7 @@ public String toVarName(String name) { varName = org.openapitools.codegen.utils.StringUtils.underscore(varName); // for reserved word or word starting with number, append _ - if (isReservedWord(varName) || varName.matches("^\\d.*")) { + if (isReservedWord(varName) || STARTS_WITH_DIGIT.matcher(varName).matches()) { varName = escapeReservedWord(varName); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppUE4ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppUE4ClientCodegen.java index 13ab898fa39a..736461fc5797 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppUE4ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppUE4ClientCodegen.java @@ -516,7 +516,7 @@ public String toVarName(String name) { name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // if it's all upper case, convert to lower case - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { name = name.toLowerCase(Locale.ROOT); } @@ -529,7 +529,7 @@ public String toVarName(String name) { } // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java index 29bb9e09ab95..b9b62b71e42b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java @@ -407,7 +407,7 @@ public String toModelName(final String name) { } // model name starts with number - if (modelName.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(modelName).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", modelName, camelize("model_" + modelName)); // e.g. 200Response => Model200Response (after camelize) modelName = "model_" + modelName; @@ -505,7 +505,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -518,7 +518,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -548,7 +548,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } @@ -871,7 +871,7 @@ public String toVarName(final String name) { // sanitize name varName = sanitizeName(name); // if it's all upper case, convert to lower case - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { varName = varName.toLowerCase(Locale.ROOT); } @@ -880,7 +880,7 @@ public String toVarName(final String name) { varName = underscore(varName); // for reserved word or word starting with number, append _ - if (isReservedWord(varName) || varName.matches("^\\d.*")) { + if (isReservedWord(varName) || STARTS_WITH_DIGIT.matcher(varName).matches()) { varName = escapeReservedWord(varName); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java index af66fcf53e39..53a42cafafa1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java @@ -219,8 +219,8 @@ public String toEnumVarName(String value, String datatype) { } // string - String var = value.replaceAll("\\W+", "_").toLowerCase(Locale.ROOT); - if (var.matches("\\d.*")) { + String var = NON_WORD_PLUS.matcher(value).replaceAll("_").toLowerCase(Locale.ROOT); + if (LEADING_DIGIT.matcher(var).matches()) { return "val_" + var; } else if (var.startsWith("_")) { return "val" + var; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java index 6be26abf569c..923fc9246ec5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java @@ -539,7 +539,7 @@ public String toModelFilename(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, "model_" + name); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -564,7 +564,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java index ed17189a1fd3..87fff578cdaf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java @@ -198,7 +198,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn(operationId + " (starting with a number) cannot be used as method sname. Renamed to " + camelize("call_" + operationId), true); operationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); } @@ -490,7 +490,7 @@ public String getTypeDeclaration(Schema p) { private static class RemoveWhitespaceLambda implements Mustache.Lambda { @Override public void execute(final Template.Fragment fragment, final Writer writer) throws IOException { - writer.write(fragment.execute().replaceAll("\\s+", " ").trim()); + writer.write(WHITESPACE.matcher(fragment.execute()).replaceAll(" ").trim()); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java index 7bab192f4d50..25f2286db401 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java @@ -159,7 +159,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replace("[]", ""); // ENUM starts with a number - if (enumName.matches("\\d.*")) { + if (LEADING_DIGIT.matcher(enumName).matches()) { return StringUtils.capitalize("_" + enumName) + "Enum"; } else { return StringUtils.capitalize(enumName) + "Enum"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java index 14693a3b067c..fd42e7d605f7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java @@ -88,7 +88,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC // protected String MODEL_EXTENSIONS = "modelExtensions"; private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); - private static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); static final String MEDIA_TYPE = "mediaType"; static final String MIME_NO_CONTENT = "MimeNoContent"; @@ -1201,7 +1200,7 @@ public String toVarName(String prefix, String name) { if (hasPrefix) { return prefix + name; } else { - if (name.matches("^\\d.*")) + if (STARTS_WITH_DIGIT.matcher(name).matches()) name = escapeReservedWord(name); if (isReservedWord(name)) name = escapeReservedWord(name); @@ -1266,7 +1265,7 @@ public String escapeIdentifier(String prefix, String name) { if (isReservedWord(name)) { name = prefix + name; } - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = prefix + name; // e.g. 200Response => Model200Response (after camelize) } if (languageSpecificPrimitives.contains(name)) { @@ -1462,10 +1461,10 @@ public String escapeText(String input) { // outer unescape to retain the original multi-byte characters // finally escalate characters avoiding code injection return escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( + CONTROL_WHITESPACE.matcher(StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") + .replace("\\/", "/"))) + .replaceAll(" ") .replace("\\", "\\\\") .replace("\"", "\\\"")); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java index 5d28f7b0b554..581d5afaa090 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java @@ -401,8 +401,7 @@ public void processOpts() { if (isUserTitle) { String titleName = (String) additionalProperties.get(TITLE); - mainClassName = (camelize(titleName.trim(), CamelizeOption.UPPERCASE_FIRST_CHAR) + "Application").replaceAll("\\s+", ""); - ; + mainClassName = WHITESPACE.matcher(camelize(titleName.trim(), CamelizeOption.UPPERCASE_FIRST_CHAR) + "Application").replaceAll(""); } else { mainClassName = "OpenApiGeneratorApplication"; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java index 03fe2bf5ba88..ceec5e787124 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java @@ -482,7 +482,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -491,7 +491,7 @@ public String toVarName(String name) { name = getNameUsingModelPropertyNaming(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -533,7 +533,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { String modelName = "Model" + name; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -820,7 +820,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { String newOperationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, newOperationId); return newOperationId; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java index 83c7550546f8..f7e887097a5e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java @@ -499,7 +499,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -508,7 +508,7 @@ public String toVarName(String name) { name = getNameUsingModelPropertyNaming(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -560,7 +560,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { String modelName = "Model" + name; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -847,7 +847,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { String newOperationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, newOperationId); return newOperationId; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java index 3629b1ed4d19..02b9ef65bca6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java @@ -189,7 +189,7 @@ public String toVarName(String name) { name = name.replaceAll("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) return name; // camelize the variable name @@ -197,7 +197,7 @@ public String toVarName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) name = escapeReservedWord(name); return name; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/K6ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/K6ClientCodegen.java index 0206116364d7..61577427735e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/K6ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/K6ClientCodegen.java @@ -778,7 +778,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -787,7 +787,7 @@ public String toVarName(String name) { name = getNameUsingModelPropertyNaming(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java index 92c38a91f562..d53a5f815c2d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java @@ -252,7 +252,7 @@ public String toVarName(String name) { name = sanitizeName(name.replaceAll("-", "_")); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) return name; // convert variable name to snake case @@ -264,7 +264,7 @@ public String toVarName(String name) { name = escapeReservedWord(name); // for reserved word or word starting with number, append _ - if (name.matches("^\\d.*")) + if (STARTS_WITH_DIGIT.matcher(name).matches()) name = "Var" + name; return name; @@ -309,7 +309,7 @@ public String toModelFilename(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, "model_" + name); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -561,7 +561,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + if (isReservedWord(enumName) || LEADING_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -575,7 +575,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java index 35f3ff81c2d6..fc801fa32348 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java @@ -646,7 +646,7 @@ public String getTypeDeclaration(Schema p) { return typeMapping.get(schemaType); } - if (schemaType.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(schemaType).matches()) { // starts with number return "`" + schemaType + "`"; } else { return schemaType; @@ -663,7 +663,7 @@ public String toVarName(String name) { } // numbers are not allowed at the beginning - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "`" + name + "`"; } @@ -706,7 +706,7 @@ protected boolean needToImport(String type) { public String toEnumName(CodegenProperty property) { String name = StringUtils.camelize(property.name); - if (name.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(name).matches()) { // starts with number return "`" + name + "`"; } else { return name; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java index c6c8ef3ae866..2f721db22b74 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java @@ -651,7 +651,7 @@ public String toVarName(String name) { name = escapeReservedWord(name); // for reserved word or word starting with number, append _ - if (name.matches("^\\d.*")) + if (STARTS_WITH_DIGIT.matcher(name).matches()) name = "var_" + name; return name; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ObjcClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ObjcClientCodegen.java index 9c388464071f..0a1aa52aca13 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ObjcClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ObjcClientCodegen.java @@ -445,7 +445,7 @@ public String toModelName(String type) { // model name starts with number /* no need for the fix below as objc model starts with prefix (e.g. SWG) - if (type.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(type).matches()) { LOGGER.warn(type + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + type)); type = "model_" + type; // e.g. 200Response => Model200Response (after camelize) } @@ -576,7 +576,7 @@ public String toVarName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number, prepend `_` - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java index 95cd04247ccc..e9aed9a52b94 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java @@ -330,7 +330,7 @@ public String toVarName(String name) { // parameter name starting with number won't compile // need to escape it by appending _ at the beginning - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } return name; @@ -375,7 +375,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name)); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -457,7 +457,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSilexServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSilexServerCodegen.java index d0ae87c514d6..0c6bf0c97821 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSilexServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSilexServerCodegen.java @@ -217,7 +217,7 @@ public String toVarName(String name) { // parameter name starting with number won't compile // need to escape it by appending _ at the beginning - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java index a1822fe72e23..f4be0fff4122 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java @@ -334,10 +334,10 @@ public String encodePath(String input) { // outer unescape to retain the original multi-byte characters // finally escalate characters avoiding code injection input = super.escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( + CONTROL_WHITESPACE.matcher(StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") + .replace("\\/", "/"))) + .replaceAll(" ") .replace("\\", "\\\\")); // .replace("\"", "\\\"")); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java index d0c603d20a07..cf12851ee635 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java @@ -798,10 +798,10 @@ public String escapeText(String input) { // outer unescape to retain the original multi-byte characters // finally escalate characters avoiding code injection return escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( + CONTROL_WHITESPACE.matcher(StringEscapeUtils.unescapeJava( StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") + .replace("\\/", "/"))) + .replaceAll(" ") .replace("\"", "\"\"")); } @@ -909,7 +909,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { 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) @@ -992,7 +992,7 @@ public String toParamName(String name) { name = camelize(sanitizeName(name)); // for param name reserved word or word starting with number, append _ - if (paramNameReservedWords.contains(name) || name.matches("^\\d.*")) { + if (paramNameReservedWords.contains(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (reserved word or special variable name) cannot be used in naming. Renamed to {}", name, escapeReservedWord(name)); name = escapeReservedWord(name); @@ -1127,7 +1127,7 @@ public String toVarName(String name) { name = camelize(name); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (reserved word or special variable name) cannot be used in naming. Renamed to {}", name, escapeReservedWord(name)); name = escapeReservedWord(name); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java index 9a5d5e2063a9..2b412266dd48 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java @@ -16,6 +16,7 @@ package org.openapitools.codegen.languages; +import com.google.common.base.CaseFormat; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.MapSchema; @@ -42,17 +43,13 @@ import java.io.File; import java.util.*; import java.util.Map.Entry; -import java.util.regex.Pattern; import java.util.stream.Collectors; -import com.google.common.base.CaseFormat; - -import static org.openapitools.codegen.utils.StringUtils.*; +import static org.openapitools.codegen.utils.StringUtils.camelize; +import static org.openapitools.codegen.utils.StringUtils.underscore; public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConfig { - private static final Pattern MULTILINE_STRING = Pattern.compile("\r\n|\r|\n"); - private static final String IMPORT = "import"; private static final String IMPORTS = "imports"; @@ -1613,7 +1610,7 @@ public String toVarName(String name) { name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // if it's all upper case, convert to lower case - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { name = name.toLowerCase(Locale.ROOT); } @@ -1622,10 +1619,10 @@ public String toVarName(String name) { name = underscore(name); // remove leading underscore - name = name.replaceAll("^_*", ""); + name = LEADING_UNDERSCORES.matcher(name).replaceAll(""); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -1650,7 +1647,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name)); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -1811,7 +1808,7 @@ private int generateFieldNumberFromString(String name) throws ProtoBufIndexCompu // Max value from developers.google.com/protocol-buffers/docs/proto3#assigning_field_numbers int fieldNumber = Math.abs(name.hashCode() % 536870911); if (19000 <= fieldNumber && fieldNumber <= 19999) { - LOGGER.error("Generated field number is in reserved range (19000, 19999) for %s, %d", name, fieldNumber); + LOGGER.error("Generated field number is in reserved range (19000, 19999) for {}, {}", name, fieldNumber); throw new ProtoBufIndexComputationException("Generated field number is in reserved range (19000, 19999)."); } return fieldNumber; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java index 188436a75eb5..3bca14120fa3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java @@ -403,7 +403,7 @@ public String toParamName(String name) { name = sanitizeName(name.replaceAll("-", "_")); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) return name; // convert variable name to snake case @@ -415,7 +415,7 @@ public String toParamName(String name) { name = "var_" + name; // for reserved word or word starting with number, append _ - if (name.matches("^\\d.*")) + if (STARTS_WITH_DIGIT.matcher(name).matches()) name = "var_" + name; return name; @@ -489,7 +489,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name)); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -726,7 +726,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + if (isReservedWord(enumName) || LEADING_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -740,7 +740,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; @@ -859,7 +859,7 @@ public String toDefaultValue(Schema p) { } } else if (ModelUtils.isStringSchema(p)) { if (p.getDefault() != null) { - if (Pattern.compile("\r\n|\r|\n").matcher((String.valueOf(p.getDefault()))).find()) + if (MULTILINE_STRING.matcher((String.valueOf(p.getDefault()))).find()) return "'''" + p.getDefault().toString() + "'''"; else return "\"" + ((String.valueOf(p.getDefault()))).replaceAll("\"", "\\\"") + "\""; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java index bd8595749bb2..0271e002a8f8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java @@ -442,7 +442,7 @@ public String toModelName(final String name) { } // model name starts with number - if (modelName.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(modelName).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", modelName, camelize("model_" + modelName)); modelName = "model_" + modelName; // e.g. 200Response => Model200Response (after camelize) @@ -527,7 +527,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -544,7 +544,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -574,7 +574,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java index 3fb4c823e9e5..f6cef14c9e43 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java @@ -375,8 +375,8 @@ public String toEnumVarName(String value, String datatype) { return "EMPTY"; } - String var = camelize(value.replaceAll("\\W+", "_")); - if (var.matches("\\d.*")) { + String var = camelize(NON_WORD_PLUS.matcher(value).replaceAll("_")); + if (LEADING_DIGIT.matcher(var).matches()) { return "_" + var; } else { return var; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java index 57ee37e1221b..fa9d98968522 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java @@ -768,7 +768,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { // e.g. 200Response => Model200Response (after camelize) String modelName = "Model" + name; LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, @@ -872,7 +872,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER)); operationId = camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER); } @@ -891,7 +891,7 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -905,7 +905,7 @@ public String toVarName(String name) { } // for words starting with number, append _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -926,7 +926,7 @@ public String toParamName(String name) { name = name.replaceAll("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -940,7 +940,7 @@ public String toParamName(String name) { } // for words starting with number, append _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -1021,7 +1021,7 @@ public String toEnumVarName(String name, String datatype) { } // Prefix with underscore if name starts with number - if (name.matches("\\d.*")) { + if (LEADING_DIGIT.matcher(name).matches()) { return "_" + replaceSpecialCharacters(camelize(name, LOWERCASE_FIRST_LETTER)); } @@ -1071,7 +1071,7 @@ private String replaceSpecialCharacters(String name) { } // Fallback, replace unknowns with underscore. - name = name.replaceAll("\\W+", "_"); + name = NON_WORD_PLUS.matcher(name).replaceAll("_"); return name; } @@ -1120,7 +1120,7 @@ public String toEnumName(CodegenProperty property) { // TODO: toModelName already does something for names starting with number, // so this code is probably never called - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java index caec04adb227..c4bc7ca134e6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java @@ -811,7 +811,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { // e.g. 200Response => Model200Response (after camelize) String modelName = "Model" + name; LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, @@ -915,7 +915,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER)); operationId = camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER); } @@ -934,7 +934,7 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -948,7 +948,7 @@ public String toVarName(String name) { } // for words starting with number, append _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -969,7 +969,7 @@ public String toParamName(String name) { name = name.replaceAll("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -983,7 +983,7 @@ public String toParamName(String name) { } // for words starting with number, append _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -1064,7 +1064,7 @@ public String toEnumVarName(String name, String datatype) { } // Prefix with underscore if name starts with number - if (name.matches("\\d.*")) { + if (LEADING_DIGIT.matcher(name).matches()) { return "_" + replaceSpecialCharacters(camelize(name, LOWERCASE_FIRST_LETTER)); } @@ -1125,7 +1125,7 @@ private String replaceSpecialCharacters(String name) { } // Fallback, replace unknowns with underscore. - name = name.replaceAll("\\W+", "_"); + name = NON_WORD_PLUS.matcher(name).replaceAll("_"); return name; } @@ -1174,7 +1174,7 @@ public String toEnumName(CodegenProperty property) { // TODO: toModelName already does something for names starting with number, // so this code is probably never called - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java index d321fd0d856d..5c8f20aea042 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java @@ -369,7 +369,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { // e.g. 200Response => Model200Response (after camelize) String modelName = "Model" + name; LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, @@ -465,7 +465,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER)); operationId = camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER); } @@ -480,7 +480,7 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -494,7 +494,7 @@ public String toVarName(String name) { } // for words starting with number, append _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -510,7 +510,7 @@ public String toParamName(String name) { name = name.replaceAll("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -524,7 +524,7 @@ public String toParamName(String name) { } // for words starting with number, append _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -646,7 +646,7 @@ public String toEnumName(CodegenProperty property) { // TODO: toModelName already does something for names starting with number, // so this code is probably never called - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index 085461f446ea..6b780748a399 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -262,7 +262,7 @@ public String toParamName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -279,7 +279,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -329,7 +329,7 @@ public String toEnumVarName(String name, String datatype) { enumName = getNameWithEnumPropertyNaming(enumName); - if (enumName.matches("\\d.*")) { // starts with number + if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } @@ -614,8 +614,7 @@ protected Object getObjectExample(Schema sc) { * @return quoted string */ private String ensureQuotes(String in) { - Pattern pattern = Pattern.compile("\r\n|\r|\n"); - Matcher matcher = pattern.matcher(in); + Matcher matcher = MULTILINE_STRING.matcher(in); if (matcher.find()) { // if a string has a new line in it add backticks to make it a typescript multiline string return "`" + in + "`"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java index ae4400a5acdb..14f584c5da59 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java @@ -114,7 +114,7 @@ private String escapeTitle(String title) { public String processOpenapiSpecDescription(String description) { if (description != null) { - return description.replaceAll("\\s+", " "); + return WHITESPACE.matcher(description).replaceAll(" "); } else { return "No description provided"; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java index 2a057ce0ef40..6b87d0f430cc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java @@ -432,7 +432,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (operationId.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize(sanitizeName("call_" + operationId), LOWERCASE_FIRST_LETTER)); operationId = camelize(sanitizeName("Call_" + operationId)); } @@ -510,7 +510,7 @@ private String replaceSpecialCharacters(String name) { } // Fallback, replace unknowns with underscore. - name = name.replaceAll("\\W+", "_"); + name = NON_WORD_PLUS.matcher(name).replaceAll("_"); return name; } @@ -547,12 +547,12 @@ public String toVarName(String name) { name = sanitizeName(name); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } return name; @@ -567,7 +567,7 @@ public String toParamName(String name) { name = name.replaceAll("-", "_"); // if it's all upper case, do nothing - if (name.matches("^[A-Z_]*$")) { + if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { return name; } @@ -581,7 +581,7 @@ public String toParamName(String name) { } // for words starting with number, prepend _ - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "_" + name; } @@ -602,7 +602,7 @@ public String toModelFilename(String name) { } // for words starting with number, prepend A - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "A" + name; } @@ -637,7 +637,7 @@ public String toModelName(String name) { } // model name starts with number - if (name.matches("^\\d.*")) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { // e.g. 200Response => Model200Response (after camelize) String modelName = "Model" + name; LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java index 92fbe460855b..b46b82732b57 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/PatternCacheTest.java @@ -61,7 +61,7 @@ public void testDifferentInstanceForDifferentFlags() { @Test public void testDifferentInstanceForDifferentRegex() { Pattern a = PatternCache.get("\\d+"); - Pattern b = PatternCache.get("\\w+"); + Pattern b = PatternCache.get("\\W+"); Assert.assertNotSame(a, b, "PatternCache must store separate entries for different regex strings"); } From d461793f49f4fc4a5382446e55350299e12d894c Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 23 Mar 2026 22:06:49 +0100 Subject: [PATCH 15/22] DRY regex patterns --- .../openapitools/codegen/DefaultCodegen.java | 12 ++++---- .../codegen/languages/AbstractAdaCodegen.java | 6 ++-- .../languages/AbstractApexCodegen.java | 28 +++++++++---------- .../codegen/languages/AbstractGoCodegen.java | 11 ++++---- .../languages/AbstractKotlinCodegen.java | 7 ++--- .../AbstractPythonPydanticV1Codegen.java | 2 +- .../AbstractTypeScriptClientCodegen.java | 2 +- .../languages/CLibcurlClientCodegen.java | 6 ++-- .../languages/CrystalClientCodegen.java | 4 +-- .../languages/EiffelClientCodegen.java | 2 +- .../GraphQLNodeJSExpressServerCodegen.java | 2 +- .../codegen/languages/LuaClientCodegen.java | 4 +-- .../codegen/languages/NimClientCodegen.java | 4 +-- .../codegen/languages/RClientCodegen.java | 4 +-- .../codegen/languages/RubyClientCodegen.java | 4 +-- .../ScalaPlayFrameworkServerCodegen.java | 2 +- .../languages/Swift5ClientCodegen.java | 4 +-- .../languages/Swift6ClientCodegen.java | 4 +-- .../languages/SwiftCombineClientCodegen.java | 2 +- .../languages/TypeScriptClientCodegen.java | 2 +- 20 files changed, 53 insertions(+), 59 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 6ffdb1d1573c..5ab6bd8e02ba 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -253,8 +253,6 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); /** Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. */ protected static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); - /** Matches a string that starts with a digit; used in {@link #toEnumVarName}. */ - protected static final Pattern LEADING_DIGIT = Pattern.compile("\\d.*"); protected static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); protected static final Pattern WHITESPACE = Pattern.compile("\\s+"); @@ -265,6 +263,8 @@ apiTemplateFiles are for API outputs only (controllers/handlers). /** Matches a string that starts with a digit (anchored); used across language generators. */ protected static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); + + protected static final Pattern LEADING_SINGLE_UNDERSCORE = Pattern.compile("^_"); /** Matches a string consisting entirely of uppercase letters and underscores. */ protected static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); /** Matches tab, newline, or carriage-return; used in {@link #escapeText}. */ @@ -974,12 +974,12 @@ public String toEnumValue(String value, String datatype) { * @return the sanitized variable name for enum */ public String toEnumVarName(String value, String datatype) { - if (value.length() == 0) { + if (value.isEmpty()) { return "EMPTY"; } String var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); - if (LEADING_DIGIT.matcher(var).lookingAt()) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { var = "_" + var; } @@ -998,8 +998,8 @@ public boolean specVersionGreaterThanOrEqualTo310(OpenAPI openAPI) { } else { originalSpecVersion = openAPI.getOpenapi(); } - Integer specMajorVersion = Integer.parseInt(originalSpecVersion.substring(0, 1)); - Integer specMinorVersion = Integer.parseInt(originalSpecVersion.substring(2, 3)); + int specMajorVersion = Integer.parseInt(originalSpecVersion.substring(0, 1)); + int specMinorVersion = Integer.parseInt(originalSpecVersion.substring(2, 3)); return specMajorVersion == 3 && specMinorVersion >= 1; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java index ac0e4c8a7a21..0b196dd03377 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractAdaCodegen.java @@ -46,8 +46,6 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractAdaCodegen.class); - private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); - public static final String HTTP_SUPPORT_OPTION = "httpSupport"; public static final String OPENAPI_PACKAGE_NAME_OPTION = "openApiName"; @@ -401,7 +399,7 @@ public String toModelName(final String name) { } // model name starts with number - if (LEADING_DIGIT.matcher(result).find()) { + if (STARTS_WITH_DIGIT.matcher(result).matches()) { String modelName = "Model_" + result; // e.g. 200Response => Model_200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -451,7 +449,7 @@ else if ("Integer".equals(datatype) || "Long".equals(datatype) || // string else { var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); - if (LEADING_DIGIT.matcher(var).find()) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { var = "_" + var; } else { var = sanitizeName(var); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java index 500e0df36ad3..ad0f2675e59b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractApexCodegen.java @@ -40,17 +40,17 @@ public abstract class AbstractApexCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(AbstractApexCodegen.class); - private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); - private static final Pattern DATE_FORMAT = Pattern.compile("^\\d{4}(-\\d{2}){2}"); - private static final Pattern DATETIME_FORMAT = Pattern.compile("^\\d{4}([-T:]\\d{2}){5}.+"); - private static final Pattern NON_NUMERIC = Pattern.compile("[^-0-9.]"); + private static final Pattern DATE_FORMAT = Pattern.compile("^\\d{4}(-\\d{2}){2}"); + private static final Pattern DATETIME_FORMAT = Pattern.compile("^\\d{4}([-T:]\\d{2}){5}.+"); + private static final Pattern NON_NUMERIC = Pattern.compile("[^-0-9.]"); private static final Pattern UNSAFE_PACKAGE_CHARS = Pattern.compile("[^a-zA-Z0-9_.]"); - private static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$"); - private static final Pattern UNDERSCORE_CLASS = Pattern.compile("^_*class$"); - private static final Pattern DATE_SEP_ZERO = Pattern.compile("-0?"); - private static final Pattern DATETIME_SEP_ZERO = Pattern.compile("[-T:]0?"); + private static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$"); + private static final Pattern UNDERSCORE_CLASS = Pattern.compile("^_*class$"); + private static final Pattern DATE_SEP_ZERO = Pattern.compile("-0?"); + private static final Pattern DATETIME_SEP_ZERO = Pattern.compile("[-T:]0?"); - @Setter protected Boolean serializableModel = false; + @Setter + protected Boolean serializableModel = false; public AbstractApexCodegen() { super(); @@ -90,9 +90,7 @@ public String sanitizeName(String name) { if (name.contains("__")) { // Preventing namespacing name = name.replace("__", "_"); } - if (LEADING_DIGIT.matcher(name).find()) { // Prevent named credentials with leading number - name = STARTS_WITH_DIGIT.matcher(name).replaceAll(""); - } + name = STARTS_WITH_DIGIT.matcher(name).replaceAll(""); // Prevent named credentials with leading number return name; } @@ -126,7 +124,7 @@ public String toVarName(String name) { name = camelize(name, LOWERCASE_FIRST_LETTER); // for reserved word or word starting with number, append _ - if (isReservedWord(name) || LEADING_DIGIT.matcher(name).find()) { + if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } @@ -180,7 +178,7 @@ public String toModelName(final String name) { } // model name starts with number - if (LEADING_DIGIT.matcher(camelizedName).find()) { + if (STARTS_WITH_DIGIT.matcher(camelizedName).matches()) { final String modelName = "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -543,7 +541,7 @@ public String toEnumVarName(String value, String datatype) { // string String var = NON_WORD_PLUS.matcher(value).replaceAll("_").toUpperCase(Locale.ROOT); - if (LEADING_DIGIT.matcher(var).find()) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { return "_" + var; } else { return var; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java index 5a15fecf6029..196c706d1101 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java @@ -40,7 +40,6 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege private final Logger LOGGER = LoggerFactory.getLogger(AbstractGoCodegen.class); private static final String NUMERIC_ENUM_PREFIX = "_"; - private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); private static final Pattern LEADING_TRAILING_SLASH = Pattern.compile("^/|/$"); @Setter @@ -231,7 +230,7 @@ public String toVarName(String name) { } // for reserved word or word starting with number, append _ - if (LEADING_DIGIT.matcher(name).find()) + if (STARTS_WITH_DIGIT.matcher(name).matches()) name = "Var" + name; if ("AdditionalProperties".equals(name)) { @@ -336,7 +335,7 @@ public String toModel(String name, boolean doUnderscore) { } // model name starts with number - if (LEADING_DIGIT.matcher(name).find()) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, "model_" + name); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -502,7 +501,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (LEADING_DIGIT.matcher(sanitizedOperationId).find()) { + if (STARTS_WITH_DIGIT.matcher(sanitizedOperationId).matches()) { LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, camelize("call_" + sanitizedOperationId)); sanitizedOperationId = "call_" + sanitizedOperationId; } @@ -953,7 +952,7 @@ public String toEnumVarName(String name, String datatype) { if (isReservedWord(enumName)) { // reserved word return escapeReservedWord(enumName); - } else if (LEADING_DIGIT.matcher(enumName).find()) { // starts with a number + } else if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with a number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -971,7 +970,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (LEADING_DIGIT.matcher(enumName).find()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index 8669e7b9ea9f..63233f7bb5e7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -51,7 +51,6 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements CodegenConfig { private static final Pattern NON_WORD_UNICODE = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); - private static final Pattern LEADING_DIGIT = Pattern.compile("^\\d"); private static final Pattern ALL_UPPER_ALPHANUMERIC_US = Pattern.compile("^[A-Z0-9_]*$"); private static final Pattern ALL_UNDERSCORES = Pattern.compile("^_*$"); private static final Pattern LEADING_DIGIT_OR_DOLLAR = Pattern.compile("(^\\d.*)|(.*\\$.*)"); @@ -750,7 +749,7 @@ public String toModelName(final String name) { } // model name starts with number - if (LEADING_DIGIT.matcher(modifiedName).find()) { + if (STARTS_WITH_DIGIT.matcher(modifiedName).matches()) { final String modelName = "Model" + modifiedName; // e.g. 200Response => Model200Response (after camelize) LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, modelName); @@ -783,7 +782,7 @@ public String toOperationId(String operationId) { } // operationId starts with a number - if (LEADING_DIGIT.matcher(operationId).find()) { + if (STARTS_WITH_DIGIT.matcher(operationId).matches()) { LOGGER.warn(operationId + " (starting with a number) cannot be used as method name. Renamed to " + camelize("call_" + operationId), LOWERCASE_FIRST_LETTER); operationId = camelize("call_" + operationId, LOWERCASE_FIRST_LETTER); } @@ -811,7 +810,7 @@ private String sanitizeKotlinSpecificNames(final String name) { // Fallback, replace unknowns with underscore. word = NON_WORD_UNICODE.matcher(word).replaceAll("_"); - if (LEADING_DIGIT.matcher(word).find()) { + if (STARTS_WITH_DIGIT.matcher(word).matches()) { word = "_" + word; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java index 8ec65936b2c7..dee34c47fa48 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java @@ -1631,7 +1631,7 @@ public String toEnumVariableName(String name, String datatype) { name = name.replaceFirst("^_", ""); name = name.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(name).matches()) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "ENUM_" + name.toUpperCase(Locale.ROOT); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index 56225f5f58cf..208a169cc21a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -979,7 +979,7 @@ public String toEnumVarName(String name, String datatype) { varName = getNameUsingEnumPropertyNaming(varName); - if (LEADING_DIGIT.matcher(varName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(varName).matches()) { // starts with number return "_" + varName; } else { return varName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java index f52974777562..75ce84b4db9e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java @@ -722,7 +722,7 @@ public String toEnumValue(String value, String datatype) { if ("Integer".equals(datatype) || "Float".equals(datatype)) { return value; } else { - if (LEADING_DIGIT.matcher(value).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(value).matches()) { // starts with number return escapeReservedWord(escapeText(value)); } else { return escapeText(value); @@ -750,7 +750,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -763,7 +763,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return escapeReservedWord(enumName); } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java index b9b62b71e42b..4e780063a4b8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java @@ -505,7 +505,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -518,7 +518,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java index 53a42cafafa1..be14d7b4242f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java @@ -220,7 +220,7 @@ public String toEnumVarName(String value, String datatype) { // string String var = NON_WORD_PLUS.matcher(value).replaceAll("_").toLowerCase(Locale.ROOT); - if (LEADING_DIGIT.matcher(var).matches()) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { return "val_" + var; } else if (var.startsWith("_")) { return "val" + var; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java index 25f2286db401..2f62187e385f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GraphQLNodeJSExpressServerCodegen.java @@ -159,7 +159,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replace("[]", ""); // ENUM starts with a number - if (LEADING_DIGIT.matcher(enumName).matches()) { + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { return StringUtils.capitalize("_" + enumName) + "Enum"; } else { return StringUtils.capitalize(enumName) + "Enum"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java index d53a5f815c2d..83d4f12ddc2e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java @@ -561,7 +561,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || LEADING_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number + if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -575,7 +575,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java index fc801fa32348..a46a8fe49eba 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java @@ -646,7 +646,7 @@ public String getTypeDeclaration(Schema p) { return typeMapping.get(schemaType); } - if (LEADING_DIGIT.matcher(schemaType).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(schemaType).matches()) { // starts with number return "`" + schemaType + "`"; } else { return schemaType; @@ -706,7 +706,7 @@ protected boolean needToImport(String type) { public String toEnumName(CodegenProperty property) { String name = StringUtils.camelize(property.name); - if (LEADING_DIGIT.matcher(name).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(name).matches()) { // starts with number return "`" + name + "`"; } else { return name; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java index 3bca14120fa3..df3b9dd0ef5f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java @@ -726,7 +726,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (isReservedWord(enumName) || LEADING_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number + if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); } else { return enumName; @@ -740,7 +740,7 @@ public String toEnumName(CodegenProperty property) { // remove [] for array or map of enum enumName = enumName.replace("[]", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java index 0271e002a8f8..fc55561b0062 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java @@ -527,7 +527,7 @@ public String toEnumVarName(String name, String datatype) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; @@ -544,7 +544,7 @@ public String toEnumName(CodegenProperty property) { enumName = enumName.replaceFirst("^_", ""); enumName = enumName.replaceFirst("_$", ""); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java index f6cef14c9e43..c68c8df4d079 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java @@ -376,7 +376,7 @@ public String toEnumVarName(String value, String datatype) { } String var = camelize(NON_WORD_PLUS.matcher(value).replaceAll("_")); - if (LEADING_DIGIT.matcher(var).matches()) { + if (STARTS_WITH_DIGIT.matcher(var).matches()) { return "_" + var; } else { return var; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java index fa9d98968522..49c8d07891da 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java @@ -1021,7 +1021,7 @@ public String toEnumVarName(String name, String datatype) { } // Prefix with underscore if name starts with number - if (LEADING_DIGIT.matcher(name).matches()) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { return "_" + replaceSpecialCharacters(camelize(name, LOWERCASE_FIRST_LETTER)); } @@ -1120,7 +1120,7 @@ public String toEnumName(CodegenProperty property) { // TODO: toModelName already does something for names starting with number, // so this code is probably never called - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java index c4bc7ca134e6..2a5c914b551f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java @@ -1064,7 +1064,7 @@ public String toEnumVarName(String name, String datatype) { } // Prefix with underscore if name starts with number - if (LEADING_DIGIT.matcher(name).matches()) { + if (STARTS_WITH_DIGIT.matcher(name).matches()) { return "_" + replaceSpecialCharacters(camelize(name, LOWERCASE_FIRST_LETTER)); } @@ -1174,7 +1174,7 @@ public String toEnumName(CodegenProperty property) { // TODO: toModelName already does something for names starting with number, // so this code is probably never called - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java index 5c8f20aea042..9de48f9204fe 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java @@ -646,7 +646,7 @@ public String toEnumName(CodegenProperty property) { // TODO: toModelName already does something for names starting with number, // so this code is probably never called - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } else { return enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index 6b780748a399..0ad0a2eb65a9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -329,7 +329,7 @@ public String toEnumVarName(String name, String datatype) { enumName = getNameWithEnumPropertyNaming(enumName); - if (LEADING_DIGIT.matcher(enumName).matches()) { // starts with number + if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return "_" + enumName; } From 55934d7777dda70cb0f8938c16db73ecf517e5b6 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 23 Mar 2026 23:07:34 +0100 Subject: [PATCH 16/22] DRY regex patterns --- .../org/openapitools/codegen/DefaultCodegen.java | 13 ++++++++++++- .../codegen/languages/AbstractCSharpCodegen.java | 4 ++-- .../codegen/languages/AbstractDartCodegen.java | 3 +-- .../codegen/languages/AbstractFSharpCodegen.java | 4 ++-- .../codegen/languages/AbstractGoCodegen.java | 4 ++-- .../codegen/languages/AbstractGraphQLCodegen.java | 4 ++-- .../codegen/languages/AbstractJavaCodegen.java | 4 +--- .../codegen/languages/AbstractKotlinCodegen.java | 3 +-- .../codegen/languages/AbstractPhpCodegen.java | 4 ++-- .../codegen/languages/AbstractPythonCodegen.java | 4 ++-- .../languages/AbstractPythonPydanticV1Codegen.java | 4 ++-- .../languages/AbstractTypeScriptClientCodegen.java | 6 +++--- .../codegen/languages/CLibcurlClientCodegen.java | 8 ++++---- .../codegen/languages/CrystalClientCodegen.java | 8 ++++---- .../codegen/languages/EiffelClientCodegen.java | 6 +++--- .../codegen/languages/LuaClientCodegen.java | 4 ++-- .../codegen/languages/NimClientCodegen.java | 2 +- .../codegen/languages/RClientCodegen.java | 5 ++--- .../codegen/languages/RubyClientCodegen.java | 8 ++++---- .../codegen/languages/RustServerCodegen.java | 2 +- .../languages/RustServerCodegenDeprecated.java | 2 +- .../codegen/languages/TypeScriptClientCodegen.java | 5 ++--- 22 files changed, 56 insertions(+), 51 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 5ab6bd8e02ba..d895feaf27a5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -257,6 +257,8 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected static final Pattern WHITESPACE = Pattern.compile("\\s+"); + protected static final Pattern DOUBLE_QUOTE = Pattern.compile("\""); + protected static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); protected static final Pattern UNESCAPED_SLASH = Pattern.compile("(? DEFAULT_SUPPORTED_CONTENT_TYPES = Arrays.asList( @@ -417,7 +416,7 @@ public String toVarName(String name) { if (name.equals("_")) { return "underscore"; } - name = LEADING_UNDERSCORE.matcher(name).replaceAll(""); + name = FIRST_LEADING_UNDERSCORE.matcher(name).replaceAll(""); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java index 65f17da72b56..edfd064108a3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractFSharpCodegen.java @@ -962,8 +962,8 @@ public String toEnumVarName(String name, String datatype) { String enumName = sanitizeName(name); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); enumName = camelize(enumName) + "Enum"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java index 196c706d1101..bbbdaae36c8b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java @@ -947,8 +947,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (isReservedWord(enumName)) { // reserved word return escapeReservedWord(enumName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java index 35d799005072..261554a1b278 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGraphQLCodegen.java @@ -385,8 +385,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index c58b4cf98f65..fd99443c64be 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -86,7 +86,6 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code private static final Pattern LOMBOK_ANNOTATION = Pattern.compile("@lombok.(\\w+\\.)*(?\\w+)(\\(.*?\\))?"); private static final Pattern JAVA_UTIL_IMPORT = Pattern.compile("java\\.util\\.(List|ArrayList|Map|HashMap)"); private static final Pattern STARTS_WITH_UNDERSCORE_CLASS = Pattern.compile("^_*class$"); - private static final Pattern ALL_UPPER_CASE_DIGITS_UNDERSCORE = Pattern.compile("^[A-Z0-9_]*$"); private static final Pattern ANNOTATION_IN_TYPE = Pattern.compile("(?:(?i)@[a-z0-9]*+([(].*[)]|\\s*))*+"); private static final Pattern NON_ALPHANUMERIC = Pattern.compile("\\P{Alnum}"); private static final Pattern INVALID_PACKAGE_CHARS = Pattern.compile("[^a-zA-Z0-9_.]"); @@ -941,7 +940,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (ALL_UPPER_CASE_DIGITS_UNDERSCORE.matcher(name).matches()) { + if (ALL_UPPER_UNDERSCORE_DIGITS.matcher(name).matches()) { return name; } @@ -2091,7 +2090,6 @@ public ModelsMap postProcessModels(ModelsMap objs) { // parse lombok additional model type annotations Map lombokOptions = new HashMap<>(); - String regexp = "@lombok.(\\w+\\.)*(?\\w+)(\\(.*?\\))?"; for (String annotation : additionalModelTypeAnnotations) { Matcher matcher = LOMBOK_ANNOTATION.matcher(annotation); if (matcher.find()) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index 63233f7bb5e7..9f881c867a68 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -51,7 +51,6 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements CodegenConfig { private static final Pattern NON_WORD_UNICODE = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); - private static final Pattern ALL_UPPER_ALPHANUMERIC_US = Pattern.compile("^[A-Z0-9_]*$"); private static final Pattern ALL_UNDERSCORES = Pattern.compile("^_*$"); private static final Pattern LEADING_DIGIT_OR_DOLLAR = Pattern.compile("(^\\d.*)|(.*\\$.*)"); private static final Pattern UNDERSCORE_CLASS = Pattern.compile("^_*class$"); @@ -1032,7 +1031,7 @@ protected String toVariableName(String name) { } // if it's all upper case, do nothing - if (ALL_UPPER_ALPHANUMERIC_US.matcher(name).matches()) { + if (ALL_UPPER_UNDERSCORE_DIGITS.matcher(name).matches()) { return name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java index 71dfb363ac40..41095056de8e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java @@ -781,8 +781,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index 050dfec39a8f..9debb538d98c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -1110,8 +1110,8 @@ public String toEnumVariableName(String name, String datatype) { } name = name.replace(" ", "_"); - name = name.replaceFirst("^_", ""); - name = name.replaceFirst("_$", ""); + name = FIRST_LEADING_UNDERSCORE.matcher(name).replaceFirst(""); + name = LAST_TRAILING_UNDERSCORE.matcher(name).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "ENUM_" + name.toUpperCase(Locale.ROOT); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java index dee34c47fa48..ad526fecee2d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java @@ -1628,8 +1628,8 @@ public String toEnumVariableName(String name, String datatype) { } name = name.replace(" ", "_"); - name = name.replaceFirst("^_", ""); - name = name.replaceFirst("_$", ""); + name = FIRST_LEADING_UNDERSCORE.matcher(name).replaceFirst(""); + name = LAST_TRAILING_UNDERSCORE.matcher(name).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(name).matches()) { name = "ENUM_" + name.toUpperCase(Locale.ROOT); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index 208a169cc21a..f9567693bf9f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -974,8 +974,8 @@ public String toEnumVarName(String name, String datatype) { varName = varName.replaceAll("_+", "_"); } varName = sanitizeName(varName); - varName = varName.replaceFirst("^_", ""); - varName = varName.replaceFirst("_$", ""); + varName = FIRST_LEADING_UNDERSCORE.matcher(varName).replaceFirst(""); + varName = LAST_TRAILING_UNDERSCORE.matcher(varName).replaceFirst(""); varName = getNameUsingEnumPropertyNaming(varName); @@ -1207,7 +1207,7 @@ public String escapeText(String input) { } // replace ' with \' - return super.escapeText(input).replace("\'", "\\\'"); + return super.escapeText(input).replace("'", "\\'"); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java index 75ce84b4db9e..0e8ef58b871d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java @@ -747,8 +747,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(camelize(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return escapeReservedWord(enumName); @@ -760,8 +760,8 @@ public String toEnumVarName(String name, String datatype) { @Override public String toEnumName(CodegenProperty property) { String enumName = camelize(toModelName(property.name)).toUpperCase(Locale.ROOT); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return escapeReservedWord(enumName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java index 4e780063a4b8..7306b2feb33e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java @@ -502,8 +502,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; @@ -515,8 +515,8 @@ public String toEnumVarName(String name, String datatype) { @Override public String toEnumName(CodegenProperty property) { String enumName = underscore(toModelName(property.name)).toUpperCase(Locale.ROOT); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java index be14d7b4242f..b42dc64651f9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/EiffelClientCodegen.java @@ -212,9 +212,9 @@ public String toEnumVarName(String value, String datatype) { if ("INTEGER_32".equals(datatype) || "INTEGER_64".equals(datatype) || "REAL_32".equals(datatype) || "REAL_64".equals(datatype)) { String varName = "NUMBER_" + value; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java index 83d4f12ddc2e..8b7effcd9657 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java @@ -558,8 +558,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java index a46a8fe49eba..f5f44dab93c9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java @@ -668,7 +668,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (name.matches("^[A-Z0-9_]*$")) { + if (ALL_UPPER_UNDERSCORE_DIGITS.matcher(name).matches()) { return name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java index df3b9dd0ef5f..92d5bdf07617 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java @@ -38,7 +38,6 @@ import java.io.IOException; import java.io.Writer; import java.util.*; -import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -723,8 +722,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (isReservedWord(enumName) || STARTS_WITH_DIGIT.matcher(enumName).matches()) { // reserved word or starts with number return escapeReservedWord(enumName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java index fc55561b0062..b523423540f9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java @@ -524,8 +524,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; @@ -541,8 +541,8 @@ public String toEnumName(CodegenProperty property) { } String enumName = underscore(toModelName(property.name)).toUpperCase(Locale.ROOT); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); if (STARTS_WITH_DIGIT.matcher(enumName).matches()) { // starts with number return NUMERIC_ENUM_PREFIX + enumName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java index 13f410c2d3d8..095cb333b94c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java @@ -475,7 +475,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation // // Construct a Rust constant (uppercase) token name, and ensure it's // unique using a numeric tie-breaker if required. - String basePathId = sanitizeName(op.path.replace("/", "_").replace("{", "").replace("}", "").replaceAll("^_", "")).toUpperCase(Locale.ROOT); + String basePathId = sanitizeName(FIRST_LEADING_UNDERSCORE.matcher(op.path.replace("/", "_").replace("{", "").replace("}", "")).replaceAll("")).toUpperCase(Locale.ROOT); String pathId = basePathId; int pathIdTiebreaker = 2; boolean found = false; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java index 96f28a9b14c6..44e197a9ca2c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegenDeprecated.java @@ -462,7 +462,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation // // Construct a Rust constant (uppercase) token name, and ensure it's // unique using a numeric tie-breaker if required. - String basePathId = sanitizeName(op.path.replace("/", "_").replace("{", "").replace("}", "").replaceAll("^_", "")).toUpperCase(Locale.ROOT); + String basePathId = sanitizeName(FIRST_LEADING_UNDERSCORE.matcher(op.path.replace("/", "_").replace("{", "").replace("}", "")).replaceAll("")).toUpperCase(Locale.ROOT); String pathId = basePathId; int pathIdTiebreaker = 2; boolean found = false; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index 0ad0a2eb65a9..fd14808da1f5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -49,7 +49,6 @@ import java.time.format.DateTimeFormatter; import java.util.*; import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.OnceLogger.once; @@ -324,8 +323,8 @@ public String toEnumVarName(String name, String datatype) { // string String enumName = sanitizeName(name); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); + enumName = FIRST_LEADING_UNDERSCORE.matcher(enumName).replaceFirst(""); + enumName = LAST_TRAILING_UNDERSCORE.matcher(enumName).replaceFirst(""); enumName = getNameWithEnumPropertyNaming(enumName); From 848b5fce4d383cfffd861811479973c573274b41 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 24 Mar 2026 00:20:31 +0100 Subject: [PATCH 17/22] DRY regex patterns --- .../openapitools/codegen/DefaultCodegen.java | 11 +++++--- .../languages/AbstractPythonCodegen.java | 2 +- .../AbstractPythonConnexionServerCodegen.java | 5 +++- .../AbstractTypeScriptClientCodegen.java | 12 ++++----- .../languages/CppOatppClientCodegen.java | 2 +- .../languages/CppOatppServerCodegen.java | 2 +- .../languages/CppPistacheServerCodegen.java | 2 +- .../languages/ErlangClientCodegen.java | 26 +++++++++++-------- .../ErlangServerDeprecatedCodegen.java | 2 +- .../codegen/languages/NimClientCodegen.java | 10 +++---- .../languages/Swift5ClientCodegen.java | 2 +- 11 files changed, 43 insertions(+), 33 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index d895feaf27a5..540e83c9ee5a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -254,10 +254,13 @@ apiTemplateFiles are for API outputs only (controllers/handlers). /** Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. */ protected static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); protected static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); + protected static final Pattern MULTI_UNDERSCORES = Pattern.compile("_+"); + protected static final Pattern MULTI_TRAILING_UNDERSCORES = Pattern.compile("_+$"); + protected static final Pattern FIRST_LEADING_UNDERSCORE = Pattern.compile("^_"); + protected static final Pattern LAST_TRAILING_UNDERSCORE = Pattern.compile("_$"); - protected static final Pattern WHITESPACE = Pattern.compile("\\s+"); - protected static final Pattern DOUBLE_QUOTE = Pattern.compile("\""); + protected static final Pattern WHITESPACE = Pattern.compile("\\s+"); protected static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); @@ -266,9 +269,7 @@ apiTemplateFiles are for API outputs only (controllers/handlers). /** Matches a string that starts with a digit (anchored); used across language generators. */ protected static final Pattern STARTS_WITH_DIGIT = Pattern.compile("^\\d.*"); - protected static final Pattern FIRST_LEADING_UNDERSCORE = Pattern.compile("^_"); - protected static final Pattern LAST_TRAILING_UNDERSCORE = Pattern.compile("_$"); /** Matches a string consisting entirely of uppercase letters and underscores. */ protected static final Pattern ALL_UPPER_UNDERSCORE = Pattern.compile("^[A-Z_]*$"); @@ -276,6 +277,8 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected static final Pattern PLUS = Pattern.compile("\\+"); protected static final Pattern DOT = Pattern.compile("\\."); + protected static final Pattern PATH_PARAMETER = Pattern.compile("\\{(.*?)}"); + /** Matches a string consisting entirely of uppercase letters and underscores and digits. */ protected static final Pattern ALL_UPPER_UNDERSCORE_DIGITS = Pattern.compile("^[A-Z0-9_]*$"); /** Matches tab, newline, or carriage-return; used in {@link #escapeText}. */ diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index 9debb538d98c..09a6cdf64e87 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -1401,7 +1401,7 @@ public String toEnumVarName(String name, String datatype) { if ("int".equals(datatype) || "float".equals(datatype)) { return name; } else { - return "\'" + name + "\'"; + return "'" + name + "'"; } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java index a8eec2f60d0a..7ced77d8ffb0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java @@ -46,6 +46,7 @@ import java.io.File; import java.io.IOException; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.CodegenConstants.X_MODIFIERS; import static org.openapitools.codegen.CodegenConstants.X_REGEX; @@ -71,6 +72,8 @@ public void serialize(Boolean value, JsonGenerator gen, SerializerProvider seria public static final String MOVE_TESTS_UNDER_PYTHON_SRC_ROOT = "testsUsePythonSrcRoot"; static final String MEDIA_TYPE = "mediaType"; + Pattern TRAILING_SLASHES_PATTERN = Pattern.compile("[/\\\\]+$"); + // An object mapper that is used to convert an example string to // a "python-compliant" example string (boolean as True/False). final ObjectMapper MAPPER = new ObjectMapper(); @@ -251,7 +254,7 @@ public void setPythonSrcRoot(String val) { if (val == null) { pySrcRoot = ""; } else { - pySrcRoot = val.replaceAll("[/\\\\]+$", ""); + pySrcRoot = TRAILING_SLASHES_PATTERN.matcher(val).replaceAll(""); } if (pySrcRoot.isEmpty() || ".".equals(pySrcRoot)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index f9567693bf9f..f94c1bcdbf01 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -961,17 +961,17 @@ public String toEnumVarName(String name, String datatype) { if ("number".equals(datatype)) { varName = "NUMBER_" + varName; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } // string if (isEnumPropertyNamingReplaceSpecialChar()) { - varName = varName.replaceAll("-", "_minus_"); - varName = varName.replaceAll("\\+", "_plus_"); - varName = varName.replaceAll("_+", "_"); + varName = MINUS.matcher(varName).replaceAll("_minus_"); + varName = PLUS.matcher(varName).replaceAll("_plus_"); + varName = MULTI_UNDERSCORES.matcher(varName).replaceAll("_"); } varName = sanitizeName(varName); varName = FIRST_LEADING_UNDERSCORE.matcher(varName).replaceFirst(""); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java index eec28ac05de1..90f3292078f5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java @@ -219,7 +219,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation } } - String pathForOatpp = path.replaceAll("\\{(.*?)}", "{$1}"); + String pathForOatpp = PATH_PARAMETER.matcher(path).replaceAll("{$1}"); op.vendorExtensions.put("x-codegen-oatpp-path", pathForOatpp); return op; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java index d4d1a33fb7d1..e6c4d6291590 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java @@ -222,7 +222,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation } } - String pathForOatpp = path.replaceAll("\\{(.*?)}", "{$1}"); + String pathForOatpp = PATH_PARAMETER.matcher(path).replaceAll("{$1}"); op.vendorExtensions.put("x-codegen-oatpp-path", pathForOatpp); return op; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java index b740e50db0a9..3f1b88d1fea3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java @@ -309,7 +309,7 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation } } - String pathForPistache = path.replaceAll("\\{(.*?)}", ":$1"); + String pathForPistache = PATH_PARAMETER.matcher(path).replaceAll(":$1"); op.vendorExtensions.put("x-codegen-pistache-path", pathForPistache); return op; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java index 018c8d2777f4..643d34541221 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java @@ -46,8 +46,10 @@ public class ErlangClientCodegen extends DefaultCodegen implements CodegenConfig private static final Pattern PATH_TEMPLATE_PATTERN = Pattern.compile("\\{([^\\}]+)\\}"); - @Setter protected String packageName = "openapi"; - @Setter protected String packageVersion = "1.0.0"; + @Setter + protected String packageName = "openapi"; + @Setter + protected String packageVersion = "1.0.0"; protected String sourceFolder = "src"; @Override @@ -258,7 +260,7 @@ public String toVarName(String name) { } // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(MINUS.matcher(name).replaceAll("_")); // for reserved word or word starting with number, append _ if (isReservedWord(name)) name = escapeReservedWord(name); @@ -291,24 +293,24 @@ public String toArrayModelParamName(String name) { @Override public String toModelName(String name) { - return this.packageName + "_" + underscore(name.replaceAll("-", "_").replaceAll("\\.", "_")); + return this.packageName + "_" + underscore(DOT.matcher(MINUS.matcher(name).replaceAll("_")).replaceAll("_")); } @Override public String toApiName(String name) { - return this.packageName + "_" + underscore(name.replaceAll("-", "_").replaceAll("\\.", "_")); + return this.packageName + "_" + underscore(DOT.matcher(MINUS.matcher(name).replaceAll("_")).replaceAll("_")); } @Override public String toModelFilename(String name) { - return this.packageName + "_" + underscore(name.replaceAll("\\.", "_")); + return this.packageName + "_" + underscore(DOT.matcher(name).replaceAll("_")); } @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - name = name.replaceAll("-", "_").replaceAll("\\.", "_"); + name = DOT.matcher(MINUS.matcher(name).replaceAll("_")).replaceAll("_"); // e.g. PetApi.erl => pet_api.erl return this.packageName + "_" + underscore(name) + "_api"; @@ -318,11 +320,11 @@ public String toApiFilename(String name) { public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. if if (isReservedWord(operationId)) { - LOGGER.warn("{} (reserved word) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId)).replaceAll("\\.", "_")); + LOGGER.warn("{} (reserved word) cannot be used as method name. Renamed to {}", operationId, DOT.matcher(underscore(sanitizeName("call_" + operationId))).replaceAll("_")); operationId = "call_" + operationId; } - return underscore(operationId.replaceAll("\\.", "_")); + return underscore(DOT.matcher(operationId).replaceAll("_")); } @Override @@ -403,9 +405,11 @@ public String escapeUnsafeCharacters(String input) { } class ExtendedCodegenOperation extends CodegenOperation { - @Getter @Setter + @Getter + @Setter private List pathTemplateNames = new ArrayList(); - @Getter @Setter + @Getter + @Setter private String replacedPathName; String arityRequired; String arityOptional; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java index c0e0e60a32c4..ec7f7df76de8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java @@ -282,7 +282,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List operationList = operations.getOperation(); for (CodegenOperation op : operationList) { if (op.path != null) { - op.path = op.path.replaceAll("\\{(.*?)\\}", ":$1"); + op.path = PATH_PARAMETER.matcher(op.path).replaceAll(":$1"); } } return objs; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java index f5f44dab93c9..6104bdd0ddcf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java @@ -505,8 +505,8 @@ private void processComposedSchemaVariants(CodegenModel mdl, List 0 && Character.isUpperCase(sanitizedBase.charAt(0))) { + String sanitizedBase = MULTI_TRAILING_UNDERSCORES.matcher(newVariant.baseName).replaceAll(""); // Remove trailing underscores + if (!sanitizedBase.isEmpty() && Character.isUpperCase(sanitizedBase.charAt(0))) { newVariant.baseName = toModelName(sanitizedBase); } else { newVariant.baseName = sanitizeNimIdentifier(sanitizedBase); @@ -517,7 +517,7 @@ private void processComposedSchemaVariants(CodegenModel mdl, List 0 && Character.isUpperCase(newVariant.dataType.charAt(0))) { + if (!newVariant.dataType.isEmpty() && Character.isUpperCase(newVariant.dataType.charAt(0))) { // This is likely a model type, use toModelName to properly format it newVariant.dataType = toModelName(newVariant.dataType); } else { @@ -730,9 +730,9 @@ private String sanitizeNimIdentifier(String name) { return name; } // Remove trailing underscores (Nim identifiers cannot end with underscore) - name = name.replaceAll("_+$", ""); + name = MULTI_TRAILING_UNDERSCORES.matcher(name).replaceAll(""); // Collapse multiple consecutive underscores to single underscore - name = name.replaceAll("_+", "_"); + name = MULTI_UNDERSCORES.matcher(name).replaceAll("_"); return name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java index 49c8d07891da..9da0a6eb7a7e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java @@ -923,7 +923,7 @@ public String toParamName(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { From db462a6275048598fb384e93ad88edb2bab2c7d6 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Tue, 24 Mar 2026 00:32:12 +0100 Subject: [PATCH 18/22] DRY regex patterns --- .../languages/AbstractPythonPydanticV1Codegen.java | 2 +- .../org/openapitools/codegen/languages/AdaCodegen.java | 2 +- .../openapitools/codegen/languages/AdaServerCodegen.java | 2 +- .../codegen/languages/CLibcurlClientCodegen.java | 6 +++--- .../codegen/languages/CSharpClientCodegen.java | 6 +++--- .../codegen/languages/CSharpReducedClientCodegen.java | 6 +++--- .../codegen/languages/ErlangProperCodegen.java | 8 ++++---- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java index ad526fecee2d..4c1931c5fcfe 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java @@ -776,7 +776,7 @@ public String toApiVarName(String name) { } protected static String dropDots(String str) { - return str.replaceAll("\\.", "_"); + return DOT.matcher(str).replaceAll("_"); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java index 45c87f8b1c3b..d5d91237d1d1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java @@ -103,7 +103,7 @@ public void processOpts() { } else { // default: set project based on package name // e.g. petstore.api (package name) => petstore_api (project name) - projectName = packageName.replaceAll("\\.", "_"); + projectName = DOT.matcher(packageName).replaceAll("_"); } String configBaseName = modelPackage.toLowerCase(Locale.ROOT); supportingFiles.add(new SupportingFile("gnat-project.mustache", "", toFilename(projectName) + ".gpr")); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java index afc3690d21e0..987e97d6e9cd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java @@ -108,7 +108,7 @@ public void processOpts() { } else { // default: set project based on package name // e.g. petstore.api (package name) => petstore_api (project name) - projectName = packageName.replaceAll("\\.", "_"); + projectName = DOT.matcher(packageName).replaceAll("_"); } String configBaseName = modelPackage.toLowerCase(Locale.ROOT); supportingFiles.add(new SupportingFile("gnat-project.mustache", "", toFilename(projectName) + ".gpr")); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java index 0e8ef58b871d..7aa5c22a2326 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java @@ -739,9 +739,9 @@ public String toEnumVarName(String name, String datatype) { // number if ("Integer".equals(datatype) || "Float".equals(datatype)) { String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java index 73a33a60c9bc..520b0459fac7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java @@ -1316,9 +1316,9 @@ public String toEnumVarName(String value, String datatype) { datatype.startsWith("long") || datatype.startsWith("ulong") || datatype.startsWith("double") || datatype.startsWith("float")) { String varName = "NUMBER_" + value; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java index a821d612fe48..0c6253671501 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java @@ -840,9 +840,9 @@ public String toEnumVarName(String value, String datatype) { if (datatype.startsWith("int") || datatype.startsWith("long") || datatype.startsWith("double") || datatype.startsWith("float")) { String varName = "NUMBER_" + value; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java index c9ce46417995..1d94b155c7f0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java @@ -340,7 +340,7 @@ public String toArrayModelParamName(String name) { @Override public String toModelName(String name) { - return this.packageName + "_" + underscore(name.replaceAll("-", "_").replaceAll("\\.", "_")); + return this.packageName + "_" + underscore(DOT.matcher(MINUS.matcher(name).replaceAll("_")).replaceAll("_")); } @Override @@ -350,7 +350,7 @@ public String toApiName(String name) { @Override public String toModelFilename(String name) { - return this.packageName + "_" + underscore(name.replaceAll("\\.", "_")); + return this.packageName + "_" + underscore(DOT.matcher(name).replaceAll("_")); } @Override @@ -362,11 +362,11 @@ public String toApiFilename(String name) { public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. return if (isReservedWord(operationId)) { - LOGGER.warn("{} (reserved word) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId)).replaceAll("\\.", "_")); + LOGGER.warn("{} (reserved word) cannot be used as method name. Renamed to {}", operationId, DOT.matcher(underscore(sanitizeName("call_" + operationId))).replaceAll("_")); operationId = "call_" + operationId; } - return underscore(operationId.replaceAll("\\.", "_")); + return underscore(DOT.matcher(operationId).replaceAll("_")); } @Override From 6c4c02ecd8352d932ffb60eadd4b6d57aa1f49df Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Wed, 25 Mar 2026 00:41:37 +0100 Subject: [PATCH 19/22] DRY regex patterns --- .../openapitools/codegen/DefaultCodegen.java | 40 ++++++++++--- .../codegen/DefaultGenerator.java | 2 +- .../codegen/languages/AdaCodegen.java | 6 +- .../codegen/languages/AdaServerCodegen.java | 6 +- .../languages/AndroidClientCodegen.java | 2 +- .../AsciidocDocumentationCodegen.java | 2 +- .../codegen/languages/AvroSchemaCodegen.java | 2 +- .../languages/CLibcurlClientCodegen.java | 6 +- .../languages/CSharpClientCodegen.java | 2 +- .../languages/CSharpReducedClientCodegen.java | 2 +- .../languages/CppHttplibServerCodegen.java | 2 +- .../languages/CppOatppClientCodegen.java | 8 +-- .../languages/CppOatppServerCodegen.java | 8 +-- .../languages/CppPistacheServerCodegen.java | 12 ++-- .../languages/CppRestSdkClientCodegen.java | 12 ++-- .../languages/CppRestbedServerCodegen.java | 8 +-- .../CppRestbedServerDeprecatedCodegen.java | 8 +-- .../languages/CppTinyClientCodegen.java | 4 +- .../languages/CppTizenClientCodegen.java | 4 +- .../languages/CrystalClientCodegen.java | 8 +-- .../languages/ElixirClientCodegen.java | 6 +- .../codegen/languages/ElmClientCodegen.java | 2 +- .../languages/ErlangProperCodegen.java | 2 +- .../languages/ErlangServerCodegen.java | 2 +- .../ErlangServerDeprecatedCodegen.java | 2 +- .../languages/GdscriptClientCodegen.java | 6 +- .../languages/HaskellHttpClientCodegen.java | 11 ++-- .../languages/HaskellServantCodegen.java | 1 - .../languages/HaskellYesodServerCodegen.java | 2 - .../languages/JMeterClientCodegen.java | 2 +- .../languages/JavaDubboServerCodegen.java | 2 +- .../JavaMicronautAbstractCodegen.java | 9 +-- .../languages/JavaPlayFrameworkCodegen.java | 5 +- .../languages/JavaVertXServerCodegen.java | 2 +- ...JavascriptClosureAngularClientCodegen.java | 2 +- .../JetbrainsHttpClientClientCodegen.java | 4 +- .../codegen/languages/LuaClientCodegen.java | 7 ++- .../codegen/languages/NimClientCodegen.java | 8 +-- .../codegen/languages/OCamlClientCodegen.java | 4 +- .../codegen/languages/PerlClientCodegen.java | 2 +- .../languages/PhpFlightServerCodegen.java | 2 +- .../languages/PowerShellClientCodegen.java | 2 +- .../languages/ProtobufSchemaCodegen.java | 2 +- .../languages/PythonClientCodegen.java | 2 +- .../PythonPydanticV1ClientCodegen.java | 2 +- .../codegen/languages/RClientCodegen.java | 6 +- .../languages/RubyOnRailsServerCodegen.java | 2 +- .../languages/RubySinatraServerCodegen.java | 4 +- .../languages/ScalaGatlingCodegen.java | 2 +- .../languages/ScalaLagomServerCodegen.java | 4 +- .../ScalaPlayFrameworkServerCodegen.java | 5 +- .../languages/Swift5ClientCodegen.java | 4 +- .../languages/Swift6ClientCodegen.java | 6 +- .../languages/SwiftCombineClientCodegen.java | 8 +-- .../TypeScriptAxiosClientCodegen.java | 4 +- .../languages/TypeScriptClientCodegen.java | 2 +- .../TypeScriptFetchClientCodegen.java | 4 +- .../TypeScriptNestjsServerCodegen.java | 2 +- .../codegen/languages/WsdlSchemaCodegen.java | 5 +- .../codegen/languages/XojoClientCodegen.java | 8 +-- .../templating/mustache/KebabCaseLambda.java | 24 ++++---- .../openapitools/codegen/utils/SemVer.java | 6 +- .../codegen/utils/StringUtils.java | 58 ++++++++++--------- 63 files changed, 207 insertions(+), 182 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 540e83c9ee5a..ec738b350527 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -249,15 +249,24 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected final static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application/json(;.*)?"); protected final static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application/vnd.(.*)+json(;.*)?"); private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); - /** Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. */ + /** + * Matches a trailing run of digits at the end of a name, used by {@link #generateNextName}. + */ protected static final Pattern TRAILING_DIGITS = Pattern.compile("\\d+\\z"); - /** Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. */ + /** + * Matches one or more non-word characters; used in {@link #toEnumVarName} and {@link #sanitizeName}. + */ protected static final Pattern NON_WORD_PLUS = Pattern.compile("\\W+"); protected static final Pattern LEADING_UNDERSCORES = Pattern.compile("^_*"); protected static final Pattern MULTI_UNDERSCORES = Pattern.compile("_+"); protected static final Pattern MULTI_TRAILING_UNDERSCORES = Pattern.compile("_+$"); + protected static final Pattern MULTI_LEADING_UNDERSCORES = Pattern.compile("^_+"); protected static final Pattern FIRST_LEADING_UNDERSCORE = Pattern.compile("^_"); protected static final Pattern LAST_TRAILING_UNDERSCORE = Pattern.compile("_$"); + protected static final Pattern NEWLINE = Pattern.compile("\n"); + protected static final Pattern TRAILING_NEWLINE = Pattern.compile("\n$"); + protected static final Pattern LEFT_CURLY_BRACE = Pattern.compile("\\{"); + protected static final Pattern RIGHT_CURLY_BRACE = Pattern.compile("}"); protected static final Pattern WHITESPACE = Pattern.compile("\\s+"); @@ -265,27 +274,42 @@ apiTemplateFiles are for API outputs only (controllers/handlers). protected static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); protected static final Pattern UNESCAPED_SLASH = Pattern.compile("(? templateData, String te private final Set seenFilesLower = new HashSet<>(); private File processTemplateToFile(Map templateData, String templateName, String outputFilename, boolean shouldGenerate, String skippedByOption, String intendedOutputDir) throws IOException { - String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar); + String adjustedOutputFilename = outputFilename.replace("//", "/").replace('/', File.separatorChar); File target = new File(adjustedOutputFilename); if (ignoreProcessor.allowsFile(target)) { if (shouldGenerate) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java index d5d91237d1d1..4ca6694e8355 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java @@ -125,7 +125,7 @@ public void processOpts() { additionalProperties.put("httpClientGprName", httpClientPackageName.toLowerCase(Locale.ROOT)); additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName); - String[] names = this.modelPackage.split("\\."); + String[] names = DOT.split(this.modelPackage); String pkgName = names[0]; additionalProperties.put("packageLevel1", pkgName); supportingFiles.add(new SupportingFile("package-spec-level1.mustache", "src", @@ -146,8 +146,8 @@ public void processOpts() { @Override public void execute(Template.Fragment fragment, Writer writer) throws IOException { String content = fragment.execute(); - content = content.trim().replaceAll("\n$", ""); - writer.write(content.replaceAll("\n", "\n -- ")); + content = TRAILING_NEWLINE.matcher(content.trim()).replaceAll(""); + writer.write(NEWLINE.matcher(content).replaceAll("\n -- ")); } }); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java index 987e97d6e9cd..300ebc4254b3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java @@ -131,7 +131,7 @@ public void processOpts() { additionalProperties.put("httpClientGprName", httpClientPackageName.toLowerCase(Locale.ROOT)); additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName); - String names[] = this.modelPackage.split("\\."); + String names[] = DOT.split(this.modelPackage); String pkgName = names[0]; additionalProperties.put("packageLevel1", pkgName); supportingFiles.add(new SupportingFile("package-spec-level1.mustache", "src", @@ -152,8 +152,8 @@ public void processOpts() { @Override public void execute(Template.Fragment fragment, Writer writer) throws IOException { String content = fragment.execute(); - content = content.trim().replaceAll("\n$", ""); - writer.write(content.replaceAll("\n", "\n -- ")); + content = TRAILING_NEWLINE.matcher(content.trim()).replaceAll(""); + writer.write(NEWLINE.matcher(content).replaceAll("\n -- ")); } }); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java index 7fc035f0127a..f6a54a0f5071 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AndroidClientCodegen.java @@ -260,7 +260,7 @@ public String toVarName(String name) { name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AsciidocDocumentationCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AsciidocDocumentationCodegen.java index 42f56a8bcce2..14299508433f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AsciidocDocumentationCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AsciidocDocumentationCodegen.java @@ -98,7 +98,7 @@ public void execute(final Template.Fragment frag, final Writer out) throws IOExc } private String escapeCurlyBrackets(String relativeFileName) { - return relativeFileName.replaceAll("\\{", "\\\\{").replaceAll("\\}", "\\\\}"); + return RIGHT_CURLY_BRACE.matcher(LEFT_CURLY_BRACE.matcher(relativeFileName).replaceAll("\\\\{")).replaceAll("\\\\}"); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java index 572ec6f64777..5b6dab70737b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AvroSchemaCodegen.java @@ -252,7 +252,7 @@ protected List> buildEnumVars(List values, String da */ private String sanitizeEnumValue(String value) { // Replace any non-alphanumeric characters with an underscore - String sanitizedValue = value.replaceAll("[^A-Za-z0-9_]", "_"); + String sanitizedValue = NON_WORD_CHAR.matcher(value).replaceAll("_"); // If the enum starts with a number, prefix it with an underscore sanitizedValue = sanitizedValue.replaceAll("^([0-9])", "_$1"); return sanitizedValue; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java index 7aa5c22a2326..b161f5f69d1e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CLibcurlClientCodegen.java @@ -630,7 +630,7 @@ public String toParamName(String name) { if (isReservedWord(name) || STARTS_WITH_DIGIT.matcher(name).matches()) { name = escapeReservedWord(name); } - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); return name; } @@ -683,7 +683,7 @@ public String toModelDocFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. PhoneNumberApi.rb => phone_number_api.rb return camelize(name) + "API"; @@ -715,7 +715,7 @@ public String toApiName(String name) { @Override public String toEnumValue(String value, String datatype) { - value = value.replaceAll("-", "_"); + value = MINUS.matcher(value).replaceAll("_"); if (isReservedWord(value)) { value = escapeReservedWord(value); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java index 520b0459fac7..a37e0117d717 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java @@ -1323,7 +1323,7 @@ public String toEnumVarName(String value, String datatype) { } // string - String var = value.replaceAll(" ", "_"); + String var = value.replace(" ", "_"); var = camelize(var); var = NON_WORD_PLUS.matcher(var).replaceAll(""); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java index 0c6253671501..72663fbce843 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java @@ -847,7 +847,7 @@ public String toEnumVarName(String value, String datatype) { } // string - String var = value.replaceAll(" ", "_"); + String var = value.replace(" ", "_"); var = camelize(var); var = NON_WORD_PLUS.matcher(var).replaceAll(""); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java index 1056c8b30eb7..c6786ab82233 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java @@ -1828,7 +1828,7 @@ private Map stripPathFromClassName(String path) { path = path.replace("_error", "").replace("_Error", "").replace("error_", "").replace("Error_", ""); } // Remove leading underscores - String clean = path.replaceAll("^_+", ""); + String clean = MULTI_LEADING_UNDERSCORES.matcher(path).replaceAll(""); // If the path is now empty or whitespace only, return default if (clean.isEmpty() || clean.trim().isEmpty()) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java index 90f3292078f5..20f8b9da773e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppClientCodegen.java @@ -155,10 +155,10 @@ public void processOpts() { reservedWordPrefix = (String) additionalProperties.get(RESERVED_WORD_PREFIX_OPTION); } - additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); - additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); - additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); - additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); + additionalProperties.put("modelNamespaceDeclarations", DOT.split(modelPackage)); + additionalProperties.put("modelNamespace", DOT.matcher(modelPackage).replaceAll("::")); + additionalProperties.put("apiNamespaceDeclarations", DOT.split(apiPackage)); + additionalProperties.put("apiNamespace", DOT.matcher(apiPackage).replaceAll("::")); additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix); if (additionalProperties.containsKey(OPTIONAL_EXTERNAL_LIB)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java index e6c4d6291590..48921ab8ccc3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppOatppServerCodegen.java @@ -158,10 +158,10 @@ public void processOpts() { reservedWordPrefix = (String) additionalProperties.get(RESERVED_WORD_PREFIX_OPTION); } - additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); - additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); - additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); - additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); + additionalProperties.put("modelNamespaceDeclarations", DOT.split(modelPackage)); + additionalProperties.put("modelNamespace", DOT.matcher(modelPackage).replaceAll("::")); + additionalProperties.put("apiNamespaceDeclarations", DOT.split(apiPackage)); + additionalProperties.put("apiNamespace", DOT.matcher(apiPackage).replaceAll("::")); additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix); if (additionalProperties.containsKey(OPTIONAL_EXTERNAL_LIB)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java index 3f1b88d1fea3..1adf24d01906 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java @@ -213,12 +213,12 @@ public void processOpts() { reservedWordPrefix = (String) additionalProperties.get(RESERVED_WORD_PREFIX_OPTION); } - additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); - additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); - additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); - additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); - additionalProperties.put("helpersNamespaceDeclarations", helpersPackage.split("\\.")); - additionalProperties.put("helpersNamespace", helpersPackage.replaceAll("\\.", "::")); + additionalProperties.put("modelNamespaceDeclarations", DOT.split(modelPackage)); + additionalProperties.put("modelNamespace", DOT.matcher(modelPackage).replaceAll("::")); + additionalProperties.put("apiNamespaceDeclarations", DOT.split(apiPackage)); + additionalProperties.put("apiNamespace", DOT.matcher(apiPackage).replaceAll("::")); + additionalProperties.put("helpersNamespaceDeclarations", DOT.split(helpersPackage)); + additionalProperties.put("helpersNamespace", DOT.matcher(helpersPackage).replaceAll("::")); additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix); if (additionalProperties.containsKey(OPTIONAL_EXTERNAL_LIB)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java index 5a8e523cf513..f3c449e36f5b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java @@ -207,12 +207,12 @@ public void processOpts() { } additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); - additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); - additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); - additionalProperties.put("modelHeaderGuardPrefix", modelPackage.replaceAll("\\.", "_").toUpperCase(Locale.ROOT)); - additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); - additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); - additionalProperties.put("apiHeaderGuardPrefix", apiPackage.replaceAll("\\.", "_").toUpperCase(Locale.ROOT)); + additionalProperties.put("modelNamespaceDeclarations", DOT.split(modelPackage)); + additionalProperties.put("modelNamespace", DOT.matcher(modelPackage).replaceAll("::")); + additionalProperties.put("modelHeaderGuardPrefix", DOT.matcher(modelPackage).replaceAll("_").toUpperCase(Locale.ROOT)); + additionalProperties.put("apiNamespaceDeclarations", DOT.split(apiPackage)); + additionalProperties.put("apiNamespace", DOT.matcher(apiPackage).replaceAll("::")); + additionalProperties.put("apiHeaderGuardPrefix", DOT.matcher(apiPackage).replaceAll("_").toUpperCase(Locale.ROOT)); additionalProperties.put("declspec", declspec); additionalProperties.put("defaultInclude", defaultInclude); additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerCodegen.java index bf2054e472c7..a7d3994c7304 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerCodegen.java @@ -231,10 +231,10 @@ public void processOpts() { reservedWordPrefix = additionalProperties.get(RESERVED_WORD_PREFIX_OPTION).toString(); } - additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); - additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); - additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); - additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); + additionalProperties.put("modelNamespaceDeclarations", DOT.split(modelPackage)); + additionalProperties.put("modelNamespace", DOT.matcher(modelPackage).replaceAll("::")); + additionalProperties.put("apiNamespaceDeclarations", DOT.split(apiPackage)); + additionalProperties.put("apiNamespace", DOT.matcher(apiPackage).replaceAll("::")); additionalProperties.put("declspec", declspec); additionalProperties.put("defaultInclude", defaultInclude); additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerDeprecatedCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerDeprecatedCodegen.java index 603e81e00308..c43fae3ac508 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerDeprecatedCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestbedServerDeprecatedCodegen.java @@ -218,10 +218,10 @@ public void processOpts() { reservedWordPrefix = additionalProperties.get(RESERVED_WORD_PREFIX_OPTION).toString(); } - additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); - additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); - additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); - additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); + additionalProperties.put("modelNamespaceDeclarations", DOT.split(modelPackage)); + additionalProperties.put("modelNamespace", DOT.matcher(modelPackage).replaceAll("::")); + additionalProperties.put("apiNamespaceDeclarations", DOT.split(apiPackage)); + additionalProperties.put("apiNamespace", DOT.matcher(apiPackage).replaceAll("::")); additionalProperties.put("declspec", declspec); additionalProperties.put("defaultInclude", defaultInclude); additionalProperties.put(RESERVED_WORD_PREFIX_OPTION, reservedWordPrefix); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTinyClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTinyClientCodegen.java index 7c55e0fbb24b..8f84f08b493c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTinyClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTinyClientCodegen.java @@ -337,8 +337,8 @@ public String toApiImport(String name) { @Override public String toVarName(String name) { - String paramName = name.replaceAll("[^a-zA-Z0-9_]", ""); - if (name.length() > 0) { + String paramName = NON_WORD_CHAR.matcher(name).replaceAll(""); + if (!name.isEmpty()) { paramName = Character.toLowerCase(paramName.charAt(0)) + paramName.substring(1); } if (isReservedWord(paramName)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTizenClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTizenClientCodegen.java index 9341df65be7d..36c36a87f92b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTizenClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppTizenClientCodegen.java @@ -288,8 +288,8 @@ public String toApiFilename(String name) { @Override public String toVarName(String name) { - String paramName = name.replaceAll("[^a-zA-Z0-9_]", ""); - if (name.length() > 0) { + String paramName = NON_WORD_CHAR.matcher(name).replaceAll(""); + if (!name.isEmpty()) { // additionalProperties name is "" so name.length() == 0 paramName = Character.toLowerCase(paramName.charAt(0)) + paramName.substring(1); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java index 7306b2feb33e..ecc1faeb3c53 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java @@ -450,7 +450,7 @@ public String toApiFilename(final String name) { filename = filename + "_" + apiNameSuffix; } - filename = filename.replaceAll("-", "_"); + filename = MINUS.matcher(filename).replaceAll("_"); // e.g. PhoneNumberApi.cr => phone_number_api.cr return underscore(filename); @@ -494,9 +494,9 @@ public String toEnumVarName(String name, String datatype) { // number if ("Integer".equals(datatype) || "Float".equals(datatype)) { String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return NUMERIC_ENUM_PREFIX + varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java index 923fc9246ec5..e6245618f081 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java @@ -453,13 +453,13 @@ public String escapeReservedWord(String name) { String escapedName = name + "_var"; // Trim leading underscores in the event the name is already underscored - escapedName = escapedName.replaceAll("^_+", ""); + escapedName = MULTI_LEADING_UNDERSCORES.matcher(escapedName).replaceAll(""); return escapedName; } private String sourceFolder() { ArrayList underscoredWords = new ArrayList<>(); - for (String word : moduleName.split("\\.")) { + for (String word : DOT.split(moduleName)) { underscoredWords.add(underscore(word)); } return ("lib/" + join("/", underscoredWords)).replace('/', File.separatorChar); @@ -496,7 +496,7 @@ public String toApiName(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // e.g. PetApi.go => pet_api.go return underscore(name); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java index 87fff578cdaf..dd4af3368573 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElmClientCodegen.java @@ -228,7 +228,7 @@ public String toEnumName(CodegenProperty property) { @Override public String toVarName(String name) { // Replace space with _ (underscore) so camelize works as expected - final String varName = camelize(name.replaceAll(" ", "_").replaceAll("[^a-zA-Z0-9_]", ""), + final String varName = camelize(NON_WORD_CHAR.matcher(name.replace(" ", "_")).replaceAll(""), LOWERCASE_FIRST_LETTER); return isReservedWord(varName) ? escapeReservedWord(name) : varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java index 1d94b155c7f0..05179e0c20e5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangProperCodegen.java @@ -311,7 +311,7 @@ public String modelFileFolder() { @Override public String toVarName(String name) { // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(MINUS.matcher(name).replaceAll("_")); // for reserved word or word starting with number, append _ if (isReservedWord(name)) name = escapeReservedWord(name); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java index 4581e6e4509f..541bc1124540 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java @@ -293,7 +293,7 @@ protected String toHandlerName(String name) { } protected String toModuleName(String name) { - return this.packageName + "_" + underscore(name.replaceAll("-", "_")); + return this.packageName + "_" + underscore(MINUS.matcher(name).replaceAll("_")); } protected String toSourceFilePath(String name, String extension) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java index ec7f7df76de8..2d5ff7aa36dc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerDeprecatedCodegen.java @@ -299,7 +299,7 @@ protected String toHandlerName(String name) { } protected String toModuleName(String name) { - return this.packageName + "_" + underscore(name.replaceAll("-", "_")); + return this.packageName + "_" + underscore(MINUS.matcher(name).replaceAll("_")); } protected String toSourceFilePath(String name, String extension) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GdscriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GdscriptClientCodegen.java index e2e7c5dc2004..e95a4e6d2642 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GdscriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GdscriptClientCodegen.java @@ -343,9 +343,9 @@ public String escapeReservedWord(String name) { } public String escapeStringLiteral(String input) { - return input - .replace("\"", "\\\"") // escape double quotes - .replaceAll("[\\\\]+$", "") // remove trailing backslash(es) + return TRAILING_BACKSLASHES.matcher(input + .replace("\"", "\\\"")) // escape double quotes + .replaceAll("") // remove trailing backslash(es) // issue: "foo\" will perhaps still wreak havoc ; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java index fd42e7d605f7..09ad7254263b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java @@ -32,6 +32,7 @@ import org.openapitools.codegen.model.OperationMap; import org.openapitools.codegen.model.OperationsMap; import org.openapitools.codegen.utils.ModelUtils; +import org.openapitools.codegen.utils.PatternCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,8 +88,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC // protected String MODEL_IMPORTS = "modelImports"; // protected String MODEL_EXTENSIONS = "modelExtensions"; - private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); - static final String MEDIA_TYPE = "mediaType"; static final String MIME_NO_CONTENT = "MimeNoContent"; static final String MIME_ANY = "MimeAny"; @@ -1059,14 +1058,14 @@ private void processPathExpr(CodegenOperation op) { if (op.getHasPathParams()) { for (CodegenParameter param : op.allParams) { if (param.isPathParam) { - xPath = xPath.replaceAll("\\{" + param.baseName + "\\}", "\",toPath " + param.paramName + ",\""); + xPath = PatternCache.get("\\{" + param.baseName + "}").matcher(xPath).replaceAll("\",toPath " + param.paramName + ",\""); } } xPath = xPath.replaceAll(",\"\",", ","); xPath = xPath.replaceAll("\"\",", ","); xPath = xPath.replaceAll(",\"\"", ","); xPath = xPath.replaceAll("^\\[,", "["); - xPath = xPath.replaceAll(",\\]$", "]"); + xPath = xPath.replaceAll(",]$", "]"); } op.vendorExtensions.put(VENDOR_EXTENSION_X_PATH, xPath); } @@ -1191,7 +1190,7 @@ public String toVarName(String name) { public String toVarName(String prefix, String name) { boolean hasPrefix = !StringUtils.isBlank(prefix); - name = underscore(sanitizeName(name.replaceAll("-", "_"))); + name = underscore(sanitizeName(MINUS.matcher(name).replaceAll("_"))); if (name.equals("_")) { name = "underscore"; } @@ -1430,7 +1429,7 @@ public String toEnumVarName(String value, String datatype) { String varName = "Num" + value; varName = varName.replaceAll("-", "Minus_"); varName = varName.replaceAll("\\+", "Plus_"); - varName = varName.replaceAll("\\.", "_Dot_"); + varName = DOT.matcher(varName).replaceAll("_Dot_"); return "'" + StringUtils.capitalize(sanitizeName(varName)); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java index 06703ff40856..8b651ba97529 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java @@ -42,7 +42,6 @@ public class HaskellServantCodegen extends DefaultCodegen implements CodegenConf // source folder where to write the files protected String sourceFolder = "src"; protected String apiVersion = "0.0.1"; - private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); public static final String PROP_SERVE_STATIC = "serveStatic"; public static final String PROP_SERVE_STATIC_DESC = "serve will serve files from the directory 'static'."; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellYesodServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellYesodServerCodegen.java index 38e1ba948657..5809707c5299 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellYesodServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellYesodServerCodegen.java @@ -45,8 +45,6 @@ public class HaskellYesodServerCodegen extends DefaultCodegen implements Codegen public static final String PROJECT_NAME = "projectName"; public static final String API_MODULE_NAME = "apiModuleName"; - private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); - private final Logger LOGGER = LoggerFactory.getLogger(HaskellYesodServerCodegen.class); @Getter @Setter diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JMeterClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JMeterClientCodegen.java index d005f54111b6..55c62a97823d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JMeterClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JMeterClientCodegen.java @@ -154,7 +154,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { PathItem path = openAPIGetPathsEntry.getValue(); if (path.readOperations() != null) { for (Operation operation : path.readOperations()) { - String pathWithDollars = pathname.replaceAll("\\{", "\\$\\{"); + String pathWithDollars = LEFT_CURLY_BRACE.matcher(pathname).replaceAll("\\${"); operation.addExtension("x-path", pathWithDollars); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java index 581d5afaa090..8be3b7da5f9d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaDubboServerCodegen.java @@ -454,7 +454,7 @@ private boolean isDubbo33OrHigher(String version) { try { String cleanVersion = version.split("-")[0]; - String[] parts = cleanVersion.split("\\."); + String[] parts = DOT.split(cleanVersion); if (parts.length >= 2) { int major = Integer.parseInt(parts[0]); int minor = Integer.parseInt(parts[1]); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java index 0ff8db956a42..069ed20ca45a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java @@ -403,17 +403,17 @@ public void processOpts() { @Override public String apiTestFileFolder() { if (testTool.equals(OPT_TEST_SPOCK)) { - return getOutputDir() + "/src/test/groovy/" + apiPackage().replaceAll("\\.", "/"); + return getOutputDir() + "/src/test/groovy/" + DOT.matcher(apiPackage()).replaceAll("/"); } - return getOutputDir() + "/src/test/java/" + apiPackage().replaceAll("\\.", "/"); + return getOutputDir() + "/src/test/java/" + DOT.matcher(apiPackage()).replaceAll("/"); } @Override public String modelTestFileFolder() { if (testTool.equals(OPT_TEST_SPOCK)) { - return getOutputDir() + "/src/test/groovy/" + modelPackage().replaceAll("\\.", "/"); + return getOutputDir() + "/src/test/groovy/" + DOT.matcher(modelPackage()).replaceAll("/"); } - return getOutputDir() + "/src/test/java/" + modelPackage().replaceAll("\\.", "/"); + return getOutputDir() + "/src/test/java/" + DOT.matcher(modelPackage()).replaceAll("/"); } @Override @@ -694,6 +694,7 @@ public String escapeTextGroovy(String text) { if (text == null) { return null; } + //TODO: this seems like a bug, since it is basically no-op currently. Probably .replace instead of .replaceAll is wanted here? return escapeText(text).replaceAll("'", "\\'"); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java index cc841af76b62..86b227ca17f6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaPlayFrameworkCodegen.java @@ -33,7 +33,6 @@ import java.io.File; import java.util.*; import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -41,8 +40,6 @@ public class JavaPlayFrameworkCodegen extends AbstractJavaCodegen implements BeanValidationFeatures { private final Logger LOGGER = LoggerFactory.getLogger(JavaPlayFrameworkCodegen.class); - private static final Pattern PATH_VARIABLE = Pattern.compile("\\{([^}]+)}"); - public static final String TITLE = "title"; public static final String CONFIG_PACKAGE = "configPackage"; public static final String BASE_PACKAGE = "basePackage"; @@ -248,7 +245,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List en String operationId = entry.getValue().getOperationId(); return (operationId != null) ? operationId : entry.getKey().name() - + pathname.replaceAll("-", "_").replaceAll("/", "_").replaceAll("[{}]", ""); + + MINUS.matcher(pathname).replaceAll("_").replaceAll("/", "_").replaceAll("[{}]", ""); } protected String extractPortFromHost(String host) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java index 02b9ef65bca6..43563ffdc421 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClosureAngularClientCodegen.java @@ -186,7 +186,7 @@ public String toVarName(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JetbrainsHttpClientClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JetbrainsHttpClientClientCodegen.java index 5f6711b6d39a..9caedfb94c69 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JetbrainsHttpClientClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JetbrainsHttpClientClientCodegen.java @@ -147,9 +147,7 @@ public static class DoubleMustacheLambda implements Mustache.Lambda { @Override public void execute(Template.Fragment fragment, Writer writer) throws IOException { String text = fragment.execute(); - writer.write(text - .replaceAll("\\{", "{{") - .replaceAll("}", "}}") + writer.write(RIGHT_CURLY_BRACE.matcher(LEFT_CURLY_BRACE.matcher(text).replaceAll("{{")).replaceAll("}}") ); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java index 8b7effcd9657..486b8a6a11bc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java @@ -249,7 +249,8 @@ public String toVarName(String name) { } // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(MINUS.matcher(name).replaceAll("_")); + name = sanitizeName(MINUS.matcher(name).replaceAll("_")); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) @@ -321,7 +322,7 @@ public String toModelFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. PetApi.lua => pet_api.lua return underscore(name) + "_api"; @@ -547,7 +548,7 @@ public String toEnumVarName(String name, String datatype) { String varName = name; varName = varName.replaceAll("-", "MINUS_"); varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java index 6104bdd0ddcf..072db3881810 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java @@ -416,7 +416,7 @@ public String escapeUnsafeCharacters(String input) { @Override public String toModelImport(String name) { name = normalizeSchemaName(name); - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); if (importMapping.containsKey(name)) { return sanitizeNimIdentifier("model_" + StringUtils.underscore(importMapping.get(name))); @@ -427,7 +427,7 @@ public String toModelImport(String name) { @Override public String toApiImport(String name) { - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); if (importMapping.containsKey(name)) { return sanitizeNimIdentifier("api_" + StringUtils.underscore(importMapping.get(name))); } else { @@ -571,13 +571,13 @@ public String toModelName(String name) { @Override public String toModelFilename(String name) { name = normalizeSchemaName(name); - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); return sanitizeNimIdentifier("model_" + StringUtils.underscore(name)); } @Override public String toApiFilename(String name) { - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); return sanitizeNimIdentifier("api_" + StringUtils.underscore(name)); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java index 2f721db22b74..f7e6a6f3d845 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java @@ -641,7 +641,7 @@ public String modelFileFolder() { @Override public String toVarName(String name) { // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(MINUS.matcher(name).replaceAll("_")); // snake_case, e.g. PetId => pet_id name = underscore(name); @@ -701,7 +701,7 @@ public String toModelFilename(String name) { @Override public String toApiFilename(final String name) { // replace - with _ e.g. created-at => created_at - final String _name = name.replaceAll("-", "_"); + final String _name = MINUS.matcher(name).replaceAll("_"); // e.g. PetApi.ml => pet_api.ml return underscore(_name) + "_api"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java index e9aed9a52b94..f1c3226f868b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PerlClientCodegen.java @@ -426,7 +426,7 @@ public String toApiDocFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. phone_number_api.rb => PhoneNumberApi.rb return camelize(name) + "Api"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpFlightServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpFlightServerCodegen.java index bd35553b9834..b14617bd54be 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpFlightServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpFlightServerCodegen.java @@ -207,7 +207,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // e.g. PhoneNumber => phone_number return underscore(name) + "_service"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java index 924263f217b3..8ed782c63cdf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java @@ -310,7 +310,7 @@ public void processOpts() { } // If the package name consists of dots(openapi.client), then we need to create the directory structure like openapi/client with __init__ files. - String[] packageNameSplits = packageName.split("\\."); + String[] packageNameSplits = DOT.split(packageName); String currentPackagePath = ""; for (int i = 0; i < packageNameSplits.length - 1; i++) { if (i > 0) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonPydanticV1ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonPydanticV1ClientCodegen.java index 0834b51cd4cf..bb3178be50ba 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonPydanticV1ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonPydanticV1ClientCodegen.java @@ -314,7 +314,7 @@ public void processOpts() { } // If the package name consists of dots(openapi.client), then we need to create the directory structure like openapi/client with __init__ files. - String[] packageNameSplits = packageName.split("\\."); + String[] packageNameSplits = DOT.split(packageName); String currentPackagePath = ""; for (int i = 0; i < packageNameSplits.length - 1; i++) { if (i > 0) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java index 92d5bdf07617..3c77fa895feb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java @@ -399,7 +399,7 @@ public String toParamName(String name) { } // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); + name = sanitizeName(MINUS.matcher(name).replaceAll("_")); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) @@ -501,7 +501,7 @@ public String toModelName(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. PetApi.r => pet_api.r return underscore(name + "_api"); @@ -711,7 +711,7 @@ public String toEnumVarName(String name, String datatype) { String varName = name; varName = varName.replaceAll("-", "MINUS_"); varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyOnRailsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyOnRailsServerCodegen.java index b3f484f375cd..e4db03a7668f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyOnRailsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyOnRailsServerCodegen.java @@ -265,7 +265,7 @@ public String toModelFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. DefaultController => defaults_controller.rb return underscore(name) + "_controller"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubySinatraServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubySinatraServerCodegen.java index 1e51a349e0db..c32e2e70db57 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubySinatraServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubySinatraServerCodegen.java @@ -157,7 +157,7 @@ public String toModelFilename(String name) { @Override public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = MINUS.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // e.g. PhoneNumberApi.rb => phone_number_api.rb return underscore(name) + "_api"; @@ -165,7 +165,7 @@ public String toApiFilename(String name) { @Override public String toApiName(String name) { - if (name.length() == 0) { + if (name.isEmpty()) { return "DefaultApi"; } // e.g. phone_number_api => PhoneNumberApi diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaGatlingCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaGatlingCodegen.java index 3a9eb9b1c1c7..2dfef0551e46 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaGatlingCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaGatlingCodegen.java @@ -268,7 +268,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { if (!operation.getExtensions().keySet().contains("x-gatling-path")) { if (pathname.contains("{")) { - String gatlingPath = pathname.replaceAll("\\{", "\\$\\{"); + String gatlingPath = LEFT_CURLY_BRACE.matcher(pathname).replaceAll("\\${"); operation.addExtension("x-gatling-path", gatlingPath); } else { operation.addExtension("x-gatling-path", pathname); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java index 57efc5671b20..dbb5a30d004b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java @@ -190,7 +190,7 @@ public ModelsMap postProcessModelsEnum(ModelsMap objs) { for (final ListIterator i = enumValues.listIterator(); i.hasNext(); ) { final String element = String.valueOf(i.next()); - i.set(element.replaceAll("^\"|\"$", "")); + i.set(ENCLOSING_QUOTES.matcher(element).replaceAll("")); } } } @@ -222,7 +222,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List ops = operations.getOperation(); for (CodegenOperation operation : ops) { - Matcher match = PATH_VARIABLE.matcher(operation.path); + Matcher match = PATH_PARAMETER.matcher(operation.path); while (match.find()) { String completeMatch = match.group(); String replacement = ":" + camelize(match.group(1), LOWERCASE_FIRST_LETTER); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java index 9da0a6eb7a7e..f5299e1a429f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java @@ -1051,8 +1051,8 @@ public String toEnumVarName(String name, String datatype) { } char[] separators = {'-', '_', ' ', ':', '(', ')'}; - return camelize(replaceSpecialCharacters(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators) - .replaceAll("[-_ :\\(\\)]", "")), + return camelize(replaceSpecialCharacters(DASH_UNDERSCORE_SPACE_COLON_PARENTHESES.matcher(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators)) + .replaceAll("")), LOWERCASE_FIRST_LETTER); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java index 2a5c914b551f..47477c334774 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java @@ -966,7 +966,7 @@ public String toParamName(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { @@ -1094,8 +1094,8 @@ public String toEnumVarName(String name, String datatype) { } char[] separators = {'-', '_', ' ', ':', '(', ')'}; - return camelize(replaceSpecialCharacters(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators) - .replaceAll("[-_ :\\(\\)]", "")), + return camelize(replaceSpecialCharacters(DASH_UNDERSCORE_SPACE_COLON_PARENTHESES.matcher(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators)) + .replaceAll("")), LOWERCASE_FIRST_LETTER); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java index 9de48f9204fe..f716dc4c99db 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java @@ -507,7 +507,7 @@ public String toParamName(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { @@ -612,7 +612,7 @@ public String toEnumVarName(String name, String datatype) { String varName = "number" + camelize(name); varName = varName.replaceAll("-", "minus"); varName = varName.replaceAll("\\+", "plus"); - varName = varName.replaceAll("\\.", "dot"); + varName = DOT.matcher(varName).replaceAll("dot"); return varName; } @@ -623,8 +623,8 @@ public String toEnumVarName(String name, String datatype) { } char[] separators = {'-', '_', ' ', ':', '(', ')'}; - return camelize(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators) - .replaceAll("[-_ :\\(\\)]", ""), + return camelize(DASH_UNDERSCORE_SPACE_COLON_PARENTHESES.matcher(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators)) + .replaceAll(""), LOWERCASE_FIRST_LETTER); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java index 6898d3d7ccfd..0d7bd9d161f8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java @@ -127,8 +127,8 @@ private static String getRelativeToRoot(String path) { @Override public void processOpts() { super.processOpts(); - tsModelPackage = modelPackage.replaceAll("\\.", "/"); - String tsApiPackage = apiPackage.replaceAll("\\.", "/"); + tsModelPackage = DOT.matcher(modelPackage).replaceAll("/"); + String tsApiPackage = DOT.matcher(apiPackage).replaceAll("/"); String modelRelativeToRoot = getRelativeToRoot(tsModelPackage); String apiRelativeToRoot = getRelativeToRoot(tsApiPackage); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index fd14808da1f5..972a276ae15e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -317,7 +317,7 @@ public String toEnumVarName(String name, String datatype) { varName = varName.replaceAll("-", "MINUS_"); varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); + varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java index e66f40d55342..3375dbf2dd6f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java @@ -215,11 +215,11 @@ public void setSagasAndRecords(Boolean sagasAndRecords) { } public String getPassthroughSuffix() { - return detectPassthroughModelsWithSuffixAndField != null ? detectPassthroughModelsWithSuffixAndField.split("\\.")[0] : null; + return detectPassthroughModelsWithSuffixAndField != null ? DOT.split(detectPassthroughModelsWithSuffixAndField)[0] : null; } public String getPassthroughField() { - return detectPassthroughModelsWithSuffixAndField != null ? detectPassthroughModelsWithSuffixAndField.split("\\.")[1] : null; + return detectPassthroughModelsWithSuffixAndField != null ? DOT.split(detectPassthroughModelsWithSuffixAndField)[1] : null; } public boolean getInferUniqueIdFromNameSuffix() { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index 8e8a9a2fdc1d..d8f23dea7899 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -322,7 +322,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L List operationList = objectMap.getOperation(); for (CodegenOperation operation : operationList) { - operation.path = operation.path.replaceAll("\\{([^}]+)}", ":$1"); + operation.path = PATH_PARAMETER.matcher(operation.path).replaceAll(":$1"); operation.httpMethod = camelize(operation.httpMethod.toLowerCase(Locale.ROOT)); List params = operation.allParams; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java index 14f584c5da59..18f360b3a9ac 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/WsdlSchemaCodegen.java @@ -25,6 +25,7 @@ import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.PatternCache; import java.io.File; import java.text.Normalizer; @@ -107,8 +108,8 @@ public void processOpts() { private String escapeTitle(String title) { // strip umlauts etc. - final String normalizedTitle = Normalizer.normalize(title, Normalizer.Form.NFD) - .replaceAll("[^\\p{ASCII}]", ""); + final String normalizedTitle = PatternCache.get("[^\\p{ASCII}]").matcher(Normalizer.normalize(title, Normalizer.Form.NFD)) + .replaceAll(""); return super.escapeUnsafeCharacters(normalizedTitle); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java index 6b87d0f430cc..294183c12eb9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java @@ -495,8 +495,8 @@ public String toEnumVarName(String name, String datatype) { } char[] separators = {'-', '_', ' ', ':', '(', ')'}; - return camelize(replaceSpecialCharacters(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators) - .replaceAll("[-_ :\\(\\)]", ""))); + return camelize(replaceSpecialCharacters(DASH_UNDERSCORE_SPACE_COLON_PARENTHESES.matcher(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators)) + .replaceAll(""))); } private String replaceSpecialCharacters(String name) { @@ -564,7 +564,7 @@ public String toParamName(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); // if it's all upper case, do nothing if (ALL_UPPER_UNDERSCORE.matcher(name).matches()) { @@ -595,7 +595,7 @@ public String toModelFilename(String name) { name = sanitizeName(name); // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); + name = MINUS.matcher(name).replaceAll("_"); if (isReservedWord(name)) { name = escapeReservedWord(name); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/KebabCaseLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/KebabCaseLambda.java index af384969debc..00bd67a95cd6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/KebabCaseLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/KebabCaseLambda.java @@ -36,21 +36,25 @@ * {{#kebabcase}}{{summary}}{{/kebabcase}} * */ +import java.util.regex.Pattern; + public class KebabCaseLambda implements Mustache.Lambda { + + private static final Pattern PACKAGE_SEPARATOR = Pattern.compile("\\."); + private static final Pattern SPECIAL = Pattern.compile("[^A-Za-z0-9_]"); + private static final Pattern FIRST_PATTERN = Pattern.compile("([A-Z]+)([A-Z][a-z])"); + private static final Pattern SECOND_PATTERN = Pattern.compile("([a-z\\d])([A-Z])"); + + private static final String SPACE_REPLACEMENT = "$1 $2"; + @Override public void execute(Template.Fragment fragment, Writer writer) throws IOException { String text = fragment.execute(); - String SPECIAL = "[^A-Za-z0-9_]"; - String firstPattern = "([A-Z]+)([A-Z][a-z])"; - String secondPattern = "([a-z\\d])([A-Z])"; - String replacementPattern = "$1 $2"; - // Replace package separator with slash. - text = text.replaceAll("\\.", "/"); - text = text.replaceAll(SPECIAL, ""); - // Replace capital letter with _ plus lowercase letter. - text = text.replaceAll(firstPattern, replacementPattern); - text = text.replaceAll(secondPattern, replacementPattern); + text = PACKAGE_SEPARATOR.matcher(text).replaceAll("/"); + text = SPECIAL.matcher(text).replaceAll(""); + text = FIRST_PATTERN.matcher(text).replaceAll(SPACE_REPLACEMENT); + text = SECOND_PATTERN.matcher(text).replaceAll(SPACE_REPLACEMENT); text = text.replace('_', '-'); text = text.replace(' ', '-'); text = text.toLowerCase(Locale.ROOT); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/SemVer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/SemVer.java index 0dc350677794..0ecddd4b1fa9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/SemVer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/SemVer.java @@ -17,14 +17,18 @@ package org.openapitools.codegen.utils; +import java.util.regex.Pattern; + public class SemVer implements Comparable { + private static final Pattern DOT = Pattern.compile("\\."); + public final int major; public final int minor; public final int revision; public SemVer(String versionString) { - String[] tokens = versionString.split("\\."); + String[] tokens = DOT.split(versionString); major = Integer.parseInt(tokens[0]); minor = tokens.length < 2 ? 0 : Integer.parseInt(tokens[1]); revision = tokens.length < 3 ? 0 : Integer.parseInt(tokens[2]); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/StringUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/StringUtils.java index 18517bed319e..b043a77fc3bb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/StringUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/StringUtils.java @@ -56,10 +56,10 @@ public class StringUtils { .build(); } - private static Pattern capitalLetterPattern = Pattern.compile("([A-Z]+)([A-Z][a-z][a-z]+)"); - private static Pattern lowercasePattern = Pattern.compile("([a-z\\d])([A-Z])"); - private static Pattern pkgSeparatorPattern = Pattern.compile("\\."); - private static Pattern dollarPattern = Pattern.compile("\\$"); + private static final Pattern CAPITAL_LETTER_PATTERN = Pattern.compile("([A-Z]+)([A-Z][a-z][a-z]+)"); + private static final Pattern LOWERCASE_PATTERN = Pattern.compile("([a-z\\d])([A-Z])"); + private static final Pattern PKG_SEPARATOR_PATTERN = Pattern.compile("\\."); + private static final Pattern DOLLAR_PATTERN = Pattern.compile("\\$"); /** * Underscore the given word. @@ -74,12 +74,12 @@ public static String underscore(final String word) { String result; String replacementPattern = "$1_$2"; // Replace package separator with slash. - result = pkgSeparatorPattern.matcher(wordToUnderscore).replaceAll("/"); + result = PKG_SEPARATOR_PATTERN.matcher(wordToUnderscore).replaceAll("/"); // Replace $ with two underscores for inner classes. - result = dollarPattern.matcher(result).replaceAll("__"); + result = DOLLAR_PATTERN.matcher(result).replaceAll("__"); // Replace capital letter with _ plus lowercase letter. - result = capitalLetterPattern.matcher(result).replaceAll(replacementPattern); - result = lowercasePattern.matcher(result).replaceAll(replacementPattern); + result = CAPITAL_LETTER_PATTERN.matcher(result).replaceAll(replacementPattern); + result = LOWERCASE_PATTERN.matcher(result).replaceAll(replacementPattern); result = result.replace('-', '_'); // replace space with underscore result = result.replace(' ', '_'); @@ -88,6 +88,7 @@ public static String underscore(final String word) { }); } + private static final Pattern UNDERSCORE_OR_SPACE = Pattern.compile("[_ ]+"); /** * Dashize the given word. * @@ -95,7 +96,7 @@ public static String underscore(final String word) { * @return The dashized version of the word, e.g. "my-name" */ public static String dashize(String word) { - return underscore(word).replaceAll("[_ ]+", "-"); + return UNDERSCORE_OR_SPACE.matcher(underscore(word)).replaceAll("-"); } /** @@ -110,12 +111,13 @@ public static String camelize(String word) { return camelize(word, UPPERCASE_FIRST_CHAR); } - private static Pattern camelizeSlashPattern = Pattern.compile("\\/(.?)"); - private static Pattern camelizeUppercasePattern = Pattern.compile("(\\.?)(\\w)([^\\.]*)$"); - private static Pattern camelizeUnderscorePattern = Pattern.compile("(_)(.)"); - private static Pattern camelizeHyphenPattern = Pattern.compile("(-)(.)"); - private static Pattern camelizeDollarPattern = Pattern.compile("\\$"); - private static Pattern camelizeSimpleUnderscorePattern = Pattern.compile("_"); + private static final Pattern CAMELIZE_SLASH_PATTERN = Pattern.compile("/(.?)"); + private static final Pattern CAMELIZE_UPPERCASE_PATTERN = Pattern.compile("(\\.?)(\\w)([^.]*)$"); + private static final Pattern CAMELIZE_UNDERSCORE_PATTERN = Pattern.compile("(_)(.)"); + private static final Pattern CAMELIZE_HYPHEN_PATTERN = Pattern.compile("(-)(.)"); + private static final Pattern CAMELIZE_DOLLAR_PATTERN = Pattern.compile("\\$"); + private static final Pattern CAMELIZE_SIMPLE_UNDERSCORE_PATTERN = Pattern.compile("_"); + private static final Pattern DOT = Pattern.compile("\\."); /** * Camelize name (parameter, property, method, etc) @@ -131,14 +133,14 @@ public static String camelize(final String inputWord, CamelizeOption camelizeOpt String word = pair.getKey(); CamelizeOption option = pair.getValue(); // Replace all slashes with dots (package separator) - Matcher m = camelizeSlashPattern.matcher(word); + Matcher m = CAMELIZE_SLASH_PATTERN.matcher(word); while (m.find()) { word = m.replaceFirst("." + m.group(1).replace("\\", "\\\\")/*.toUpperCase()*/); - m = camelizeSlashPattern.matcher(word); + m = CAMELIZE_SLASH_PATTERN.matcher(word); } // case out dots - String[] parts = word.split("\\."); + String[] parts = DOT.split(word); StringBuilder f = new StringBuilder(); for (String z : parts) { if (z.length() > 0) { @@ -147,38 +149,38 @@ public static String camelize(final String inputWord, CamelizeOption camelizeOpt } word = f.toString(); - m = camelizeSlashPattern.matcher(word); + m = CAMELIZE_SLASH_PATTERN.matcher(word); while (m.find()) { word = m.replaceFirst(Character.toUpperCase(m.group(1).charAt(0)) + m.group(1).substring(1)/*.toUpperCase()*/); - m = camelizeSlashPattern.matcher(word); + m = CAMELIZE_SLASH_PATTERN.matcher(word); } // Uppercase the class name. - m = camelizeUppercasePattern.matcher(word); + m = CAMELIZE_UPPERCASE_PATTERN.matcher(word); if (m.find()) { String rep = m.group(1) + m.group(2).toUpperCase(Locale.ROOT) + m.group(3); - rep = camelizeDollarPattern.matcher(rep).replaceAll("\\\\\\$"); + rep = CAMELIZE_DOLLAR_PATTERN.matcher(rep).replaceAll("\\\\\\$"); word = m.replaceAll(rep); } // Remove all underscores (underscore_case to camelCase) - m = camelizeUnderscorePattern.matcher(word); + m = CAMELIZE_UNDERSCORE_PATTERN.matcher(word); while (m.find()) { String original = m.group(2); String upperCase = original.toUpperCase(Locale.ROOT); if (original.equals(upperCase)) { - word = camelizeSimpleUnderscorePattern.matcher(word).replaceFirst(""); + word = CAMELIZE_SIMPLE_UNDERSCORE_PATTERN.matcher(word).replaceFirst(""); } else { word = m.replaceFirst(upperCase); } - m = camelizeUnderscorePattern.matcher(word); + m = CAMELIZE_UNDERSCORE_PATTERN.matcher(word); } // Remove all hyphens (hyphen-case to camelCase) - m = camelizeHyphenPattern.matcher(word); + m = CAMELIZE_HYPHEN_PATTERN.matcher(word); while (m.find()) { word = m.replaceFirst(m.group(2).toUpperCase(Locale.ROOT)); - m = camelizeHyphenPattern.matcher(word); + m = CAMELIZE_HYPHEN_PATTERN.matcher(word); } switch (option) { @@ -191,7 +193,7 @@ public static String camelize(final String inputWord, CamelizeOption camelizeOpt } // remove all underscore - word = camelizeSimpleUnderscorePattern.matcher(word).replaceAll(""); + word = CAMELIZE_SIMPLE_UNDERSCORE_PATTERN.matcher(word).replaceAll(""); return word; }); } From 28e70269c15e04d8dc918938af61a264b795e88b Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Wed, 25 Mar 2026 22:53:02 +0100 Subject: [PATCH 20/22] refactor: introduce shared regex patterns for improved code readability --- .../openapitools/codegen/DefaultCodegen.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 0fbab8cf5345..18011bf1d0f7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -312,6 +312,43 @@ apiTemplateFiles are for API outputs only (controllers/handlers). */ private static final Pattern CALLBACK_EXPRESSION_PARAM = Pattern.compile("\\{\\$.*}"); + // --- Shared patterns reused across multiple language generators --- + + /** Splits on {@code | } (the union-type pipe separator), e.g. {@code TypeA | TypeB}. */ + public static final Pattern SPLIT_ON_PIPE = Pattern.compile(" \\| "); + /** Splits on {@code |} used as a single-char separator in rule strings. */ + protected static final Pattern SPLIT_ON_PIPE_CHAR = Pattern.compile("[|]"); + /** Splits on line-endings, both Windows ({@code \r\n}) and Unix ({@code \n}). */ + public static final Pattern SPLIT_ON_NEWLINE = Pattern.compile("\\r?\\n"); + /** Matches the camelCase boundary */ + public static final Pattern CAMEL_CASE_BOUNDARY = Pattern.compile("([a-z0-9])([A-Z])"); + /** Matches one or more trailing whitespace characters in an identifier. */ + protected static final Pattern TRAILING_WHITESPACE = Pattern.compile("\\s+$"); + /** Matches any string that ends with at least one whitespace character. */ + protected static final Pattern ENDS_WITH_WHITESPACE = Pattern.compile(".*\\s$"); + /** Matches strings consisting entirely of ASCII digits. */ + protected static final Pattern DIGITS_ONLY = Pattern.compile("^\\d+$"); + /** Matches one or more characters that are not ASCII alphanumeric (no underscore). */ + protected static final Pattern NON_ALPHANUMERIC = Pattern.compile("[^a-zA-Z0-9]+"); + /** Matches exactly one character that is not ASCII alphanumeric (no underscore). */ + protected static final Pattern NON_ALPHANUMERIC_CHAR = Pattern.compile("[^a-zA-Z0-9]"); + /** Matches {@code .}, {@code /}, or {@code \} — for package-name-to-path conversion. */ + protected static final Pattern PACKAGE_SEPARATOR = Pattern.compile("[./\\\\]"); + /** Matches a CamelCase-initial type name like {@code FooBar} (used in Swift/Kotlin). */ + protected static final Pattern CAMEL_INITIAL = Pattern.compile("[A-Z][a-z0-9]+[a-zA-Z0-9]*"); + /** Matches a 2xx HTTP success status code (e.g. {@code 200}, {@code 201}). */ + protected static final Pattern HTTP_2XX_CODE = Pattern.compile("2[0-9][0-9]"); + /** Matches a path segment that is entirely a parameter placeholder, e.g. {@code {id}}. */ + public static final Pattern IS_PATH_PARAM = Pattern.compile("^\\{.*}$"); + /** Matches one or more consecutive hyphens. */ + protected static final Pattern HYPHENS = Pattern.compile("-+"); + /** Matches two or more consecutive hyphens. */ + protected static final Pattern MULTI_HYPHEN = Pattern.compile("-{2,}"); + /** Matches one or more leading hyphens (at start of string). */ + protected static final Pattern LEADING_HYPHENS = Pattern.compile("^-+"); + /** Matches one or more trailing hyphens (at end of string). */ + protected static final Pattern TRAILING_HYPHENS = Pattern.compile("-+$"); + /** * True if the code generator supports multiple class inheritance. * This is used to model the parent hierarchy based on the 'allOf' composed schemas. @@ -4413,7 +4450,7 @@ void updateDefaultToEmptyContainer(CodegenProperty cp, Schema p) { * @param input a set of rules separated by `|` */ void parseDefaultToEmptyContainer(String input) { - String[] inputs = input.split("[|]"); + String[] inputs = SPLIT_ON_PIPE_CHAR.split(input); String containerType; for (String rule : inputs) { if (StringUtils.isEmpty(rule)) { @@ -5915,7 +5952,7 @@ protected String getOrGenerateOperationId(Operation operation, String path, Stri // remove prefix in operationId if (removeOperationIdPrefix) { // The prefix is everything before the removeOperationIdPrefixCount occurrence of removeOperationIdPrefixDelimiter - String[] components = operationId.split("[" + removeOperationIdPrefixDelimiter + "]"); + String[] components = PatternCache.get("[" + removeOperationIdPrefixDelimiter + "]").split(operationId); if (components.length > 1) { // If removeOperationIdPrefixCount is -1 or bigger that the number of occurrences, uses the last one int component_number = removeOperationIdPrefixCount == -1 ? components.length - 1 : removeOperationIdPrefixCount; @@ -7755,7 +7792,7 @@ protected void addBodyModelSchema(CodegenParameter codegenParameter, String name if (codegenProperty != null && codegenProperty.getComplexType() != null && codegenProperty.getComplexType().contains(" | ")) { // TODO move this splitting logic to the generator that needs it only - List parts = Arrays.asList(codegenProperty.getComplexType().split(" \\| ")); + List parts = Arrays.asList(SPLIT_ON_PIPE.split(codegenProperty.getComplexType())); imports.addAll(parts); String codegenModelName = codegenProperty.getComplexType(); From 15fc1f5218975f83eea9a809edf55df0b66d9f10 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Wed, 25 Mar 2026 22:53:46 +0100 Subject: [PATCH 21/22] refactor: introduce shared regex patterns for improved code readability --- .../languages/AbstractJavaCodegen.java | 6 +-- .../languages/AbstractRustCodegen.java | 6 ++- .../languages/AbstractScalaCodegen.java | 12 +++-- .../AbstractTypeScriptClientCodegen.java | 9 +++- .../codegen/languages/BashClientCodegen.java | 51 ++++++++++--------- .../languages/ClojureClientCodegen.java | 8 ++- .../languages/CppHttplibServerCodegen.java | 35 ++++++++----- .../languages/ErlangServerCodegen.java | 2 +- .../languages/GoEchoServerCodegen.java | 2 +- .../codegen/languages/GoGinServerCodegen.java | 2 +- .../languages/HaskellHttpClientCodegen.java | 20 +++++--- .../languages/HaskellServantCodegen.java | 4 +- .../languages/JavaInflectorServerCodegen.java | 2 +- .../languages/JavaUndertowServerCodegen.java | 2 +- .../JavascriptApolloClientCodegen.java | 6 ++- .../languages/JavascriptClientCodegen.java | 6 ++- .../languages/KotlinSpringServerCodegen.java | 6 ++- .../codegen/languages/KtormSchemaCodegen.java | 10 ++-- .../codegen/languages/LuaClientCodegen.java | 4 +- .../codegen/languages/MysqlSchemaCodegen.java | 8 +-- .../codegen/languages/N4jsClientCodegen.java | 5 +- .../codegen/languages/NimClientCodegen.java | 18 ++++--- .../languages/NodeJSExpressServerCodegen.java | 11 ++-- .../codegen/languages/OCamlClientCodegen.java | 22 +++++--- .../codegen/languages/ObjcClientCodegen.java | 14 +++-- .../languages/PhpSlim4ServerCodegen.java | 45 +++++++++++----- .../languages/PostgresqlSchemaCodegen.java | 20 ++++---- .../languages/PowerShellClientCodegen.java | 13 +++-- .../codegen/languages/RClientCodegen.java | 4 +- .../codegen/languages/RubyClientCodegen.java | 4 +- .../languages/ScalaAkkaClientCodegen.java | 2 +- .../languages/ScalaCaskServerCodegen.java | 13 +++-- .../languages/ScalaFinchServerCodegen.java | 8 ++- .../languages/ScalaHttp4sServerCodegen.java | 2 +- .../languages/ScalaPekkoClientCodegen.java | 2 +- .../languages/ScalaSttp4ClientCodegen.java | 2 +- .../ScalaSttp4JsoniterClientCodegen.java | 2 +- .../languages/ScalaSttpClientCodegen.java | 2 +- .../languages/Swift5ClientCodegen.java | 2 +- .../languages/Swift6ClientCodegen.java | 2 +- .../languages/SwiftCombineClientCodegen.java | 9 ++-- .../languages/TerraformProviderCodegen.java | 9 +++- .../TypeScriptAngularClientCodegen.java | 12 ++--- .../TypeScriptAxiosClientCodegen.java | 8 +-- .../languages/TypeScriptClientCodegen.java | 9 ++-- .../TypeScriptNestjsClientCodegen.java | 8 +-- .../TypeScriptNestjsServerCodegen.java | 11 ++-- .../codegen/languages/XojoClientCodegen.java | 2 +- .../codegen/utils/ModelUtils.java | 2 +- .../codegen/utils/URLPathUtils.java | 11 ++-- 50 files changed, 297 insertions(+), 178 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index fd99443c64be..f02e54d3ba51 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -87,7 +87,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code private static final Pattern JAVA_UTIL_IMPORT = Pattern.compile("java\\.util\\.(List|ArrayList|Map|HashMap)"); private static final Pattern STARTS_WITH_UNDERSCORE_CLASS = Pattern.compile("^_*class$"); private static final Pattern ANNOTATION_IN_TYPE = Pattern.compile("(?:(?i)@[a-z0-9]*+([(].*[)]|\\s*))*+"); - private static final Pattern NON_ALPHANUMERIC = Pattern.compile("\\P{Alnum}"); + private static final Pattern NON_ALPHANUMERIC_UNICODE = Pattern.compile("\\P{Alnum}"); private static final Pattern INVALID_PACKAGE_CHARS = Pattern.compile("[^a-zA-Z0-9_.]"); public static final String DEFAULT_LIBRARY = ""; @@ -2003,8 +2003,8 @@ public String removeAnnotations(String dataType) { */ public String sanitizeDataType(String dataType) { String content = removeAnnotations(dataType); - if (content != null && NON_ALPHANUMERIC.matcher(content).find()) { - content = NON_ALPHANUMERIC.matcher(content).replaceAll(""); + if (content != null && NON_ALPHANUMERIC_UNICODE.matcher(content).find()) { + content = NON_ALPHANUMERIC_UNICODE.matcher(content).replaceAll(""); } return content; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java index d9427b111c94..5b8b4300ff82 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractRustCodegen.java @@ -14,6 +14,7 @@ import java.math.BigInteger; import java.util.*; import java.util.function.Function; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.StringUtils.*; @@ -21,6 +22,9 @@ public abstract class AbstractRustCodegen extends DefaultCodegen implements Code private final Logger LOGGER = LoggerFactory.getLogger(AbstractRustCodegen.class); + /** Matches hyphens and periods — replaced with underscores during name sanitisation. */ + private static final Pattern HYPHEN_OR_PERIOD = Pattern.compile("[.\\-]"); + protected static final String VENDOR_EXTENSION_PARAM_IDENTIFIER = "x-rust-param-identifier"; protected List charactersToAllow = Collections.singletonList("_"); @@ -184,7 +188,7 @@ public String sanitizeIdentifier(String name, CasingType casingType, String esca } // Replace hyphens and periods with underscores - name = name.replaceAll("[\\.\\-]", "_"); + name = HYPHEN_OR_PERIOD.matcher(name).replaceAll("_"); // Apply special character escapes, e.g. "@type" => "At_type" // Remove the trailing underscore if necessary diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractScalaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractScalaCodegen.java index 5b0a70415761..f93800dc53a2 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractScalaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractScalaCodegen.java @@ -37,6 +37,7 @@ import java.io.StringWriter; import java.io.Writer; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.languages.AbstractJavaCodegen.DATE_LIBRARY; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; @@ -47,6 +48,11 @@ public abstract class AbstractScalaCodegen extends DefaultCodegen { private final Logger LOGGER = LoggerFactory.getLogger(AbstractScalaCodegen.class); + /** Matches a valid Java/Scala identifier starting with a letter, {@code _} or {@code $}. */ + private static final Pattern SCALA_IDENTIFIER = Pattern.compile("[a-zA-Z_$][\\w_$]+"); + /** Matches a string consisting only of digits (possibly empty). */ + private static final Pattern DIGITS_ZERO_OR_MORE = Pattern.compile("[0-9]*"); + // https://spec.openapis.org/registry/format/date-time-local.html protected static final String DATE_TIME_LOCAL_FORMAT = "date-time-local"; @@ -288,7 +294,7 @@ public String toVarName(String name) { } // if it's all upper case, do nothing - if (!varName.matches("^[A-Z_0-9]*$")) { + if (!ALL_UPPER_UNDERSCORE_DIGITS.matcher(varName).matches()) { varName = getNameUsingModelPropertyNaming(varName); } @@ -531,13 +537,13 @@ protected String formatIdentifier(String name, boolean capitalized) { if (capitalized) { identifier = StringUtils.capitalize(identifier); } - if (identifier.matches("[a-zA-Z_$][\\w_$]+") && !isReservedWord(identifier)) { + if (SCALA_IDENTIFIER.matcher(identifier).matches() && !isReservedWord(identifier)) { return identifier; } // below code block only for scala-sttp4-jsoniter for backward copmatibility if (this instanceof ScalaSttp4JsoniterClientCodegen) { - if (identifier.matches("[0-9]*")) { + if (DIGITS_ZERO_OR_MORE.matcher(identifier).matches()) { return escapeReservedWord(identifier); } if (!capitalized || StringUtils.isNumeric(name)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java index f94c1bcdbf01..d08229ca2e2e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -79,6 +79,11 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen implements CodegenConfig { + /** Splits a composed TypeScript type string on {@code |}, {@code &}, {@code <}, or {@code >}. */ + private static final Pattern COMPOSED_TYPE_DELIMITERS = Pattern.compile("[|&<>]"); + /** Matches NPM versions that end with {@code -SNAPSHOT}. */ + private static final Pattern SNAPSHOT_VERSION = Pattern.compile("^.*-SNAPSHOT$"); + /** * Help generating code for any kind of URL parameters (path, matrix, query). *

@@ -491,7 +496,7 @@ public Map toModelImportMap(String name) { } private String[] splitComposedType(String name) { - return name.replace(" ", "").split("[|&<>]"); + return COMPOSED_TYPE_DELIMITERS.split(name.replace(" ", "")); } private boolean isUnionType(String name) { @@ -526,7 +531,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } if (additionalProperties.containsKey(SNAPSHOT) && Boolean.parseBoolean(additionalProperties.get(SNAPSHOT).toString())) { - if (npmVersion.toUpperCase(Locale.ROOT).matches("^.*-SNAPSHOT$")) { + if (SNAPSHOT_VERSION.matcher(npmVersion.toUpperCase(Locale.ROOT)).matches()) { this.setNpmVersion(npmVersion + "." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date())); } else { this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date())); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java index 41639a960147..0d0ae19e9299 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java @@ -58,6 +58,18 @@ public class BashClientCodegen extends DefaultCodegen implements CodegenConfig { protected static int emptyMethodNameCounter = 0; + // --- Precompiled patterns for markdown-to-bash conversion --- + private static final java.util.regex.Pattern MD_BOLD_STAR = java.util.regex.Pattern.compile("(?m)(^|\\s)\\*{2}([\\w ]+)\\*{2}($|\\s)"); + private static final java.util.regex.Pattern MD_BOLD_UNDERSCORE = java.util.regex.Pattern.compile("(?m)(^|\\s)_{2}([\\w ]+)_{2}($|\\s)"); + private static final java.util.regex.Pattern MD_ITALIC_STAR = java.util.regex.Pattern.compile("(?m)(^|\\s)\\*([\\w ]+)\\*($|\\s)"); + private static final java.util.regex.Pattern MD_ITALIC_UNDER = java.util.regex.Pattern.compile("(?m)(^|\\s)_([\\w ]+)_($|\\s)"); + private static final java.util.regex.Pattern MD_H1 = java.util.regex.Pattern.compile("(?m)^#\\s+(.+)$"); + private static final java.util.regex.Pattern MD_H2 = java.util.regex.Pattern.compile("(?m)^##\\s+(.+)$"); + private static final java.util.regex.Pattern MD_H3 = java.util.regex.Pattern.compile("(?m)^###\\s+(.+)$"); + private static final java.util.regex.Pattern MD_CODE_BACKTICK = java.util.regex.Pattern.compile("(?m)\\s*```.*$"); + private static final java.util.regex.Pattern MD_CODE_QUOTE = java.util.regex.Pattern.compile("(?m)\\s*'''.*$"); + private static final java.util.regex.Pattern MD_TRAILING_WS = java.util.regex.Pattern.compile("\\s+$"); + public static final String CURL_OPTIONS = "curlOptions"; public static final String PROCESS_MARKDOWN = "processMarkdown"; public static final String SCRIPT_NAME = "scriptName"; @@ -510,56 +522,45 @@ public String escapeText(String input) { * Convert markdown strong **Bold text** and __Bold text__ * to bash bold control sequences (tput bold) */ - result = result.replaceAll("(?m)(^|\\s)\\*{2}([\\w\\d ]+)\\*{2}($|\\s)", - "\\$\\(tput bold\\) $2 \\$\\(tput sgr0\\)"); + result = MD_BOLD_STAR.matcher(result).replaceAll("\\$\\(tput bold\\) $2 \\$\\(tput sgr0\\)"); + result = MD_BOLD_UNDERSCORE.matcher(result).replaceAll("\\$\\(tput bold\\) $2 \\$\\(tput sgr0\\)"); - result = result.replaceAll("(?m)(^|\\s)_{2}([\\w\\d ]+)_{2}($|\\s)", - "\\$\\(tput bold\\) $2 \\$\\(tput sgr0\\)"); /** * Convert markdown *Italics text* and _Italics text_ to bash dim * control sequences (tput dim) */ - result = result.replaceAll("(?m)(^|\\s)\\*{1}([\\w\\d ]+)\\*{1}($|\\s)", - "\\$\\(tput dim\\) $2 \\$\\(tput sgr0\\)"); - - result = result.replaceAll("(?m)(^|\\s)_{1}([\\w\\d ]+)_{1}($|\\s)", - "\\$\\(tput dim\\) $2 \\$\\(tput sgr0\\)"); + result = MD_ITALIC_STAR.matcher(result).replaceAll("\\$\\(tput dim\\) $2 \\$\\(tput sgr0\\)"); + result = MD_ITALIC_UNDER.matcher(result).replaceAll("\\$\\(tput dim\\) $2 \\$\\(tput sgr0\\)"); /** * Convert all markdown section 1 level headers with bold */ - result = result.replaceAll("(?m)^\\#\\s+(.+)$", - "\n\\$\\(tput bold\\)\\$\\(tput setaf 7\\)" - + "$1\\$\\(tput sgr0\\)"); + result = MD_H1.matcher(result).replaceAll("\n\\$\\(tput bold\\)\\$\\(tput setaf 7\\)" + + "$1\\$\\(tput sgr0\\)"); /** * Convert all markdown section 2 level headers with bold */ - result = result.replaceAll("(?m)^\\#\\#\\s+(.+)$", - "\n\\$\\(tput bold\\)\\$\\(tput setaf 7\\)" - + "$1\\$\\(tput sgr0\\)"); + result = MD_H2.matcher(result).replaceAll("\n\\$\\(tput bold\\)\\$\\(tput setaf 7\\)" + + "$1\\$\\(tput sgr0\\)"); /** * Convert all markdown section 3 level headers with bold */ - result = result.replaceAll("(?m)^\\#\\#\\#\\s+(.+)$", - "\n\\$\\(tput bold\\)\\$\\(tput setaf 7\\)" - + "$1\\$\\(tput sgr0\\)"); + result = MD_H3.matcher(result).replaceAll("\n\\$\\(tput bold\\)\\$\\(tput setaf 7\\)" + + "$1\\$\\(tput sgr0\\)"); /** * Convert all markdown code blocks into --- delimited sections */ - result = result.replaceAll("(?m)\\s*```.*$", - "\n---"); - - result = result.replaceAll("(?m)\\s*\\'\\'\\'.*$", - "\n---"); + result = MD_CODE_BACKTICK.matcher(result).replaceAll("\n---"); + result = MD_CODE_QUOTE.matcher(result).replaceAll("\n---"); /** * Remove any trailing new line at the end of the string */ - result = result.replaceAll("\\s+$", ""); + result = MD_TRAILING_WS.matcher(result).replaceAll(""); } return result; @@ -583,7 +584,7 @@ public String escapeUnsafeCharacters(String input) { /** * Replace backticks with normal single quotes. */ - String result = input.replaceAll("`", "'"); + String result = input.replace("`", "'"); return result; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java index 08c866a64ef7..4b9de965cfda 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java @@ -37,6 +37,7 @@ import java.io.File; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.StringUtils.dashize; import static org.openapitools.codegen.utils.StringUtils.underscore; @@ -63,6 +64,9 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi protected String sourceFolder = "src"; + private static final Pattern NON_ALPHA_UNDERSCORE_PLUS = Pattern.compile("[^a-zA-Z_]+"); + private static final Pattern NON_ALPHANUMERIC_UNDERSCORE_HYPHEN_PLUS = Pattern.compile("[^a-zA-Z0-9_-]+"); + public ClojureClientCodegen() { super(); @@ -306,7 +310,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { @Override public String sanitizeTag(String tag) { - return tag.replaceAll("[^a-zA-Z_]+", "_"); + return NON_ALPHA_UNDERSCORE_PLUS.matcher(tag).replaceAll("_"); } @Override @@ -359,7 +363,7 @@ public String toVarName(String name) { return nameMapping.get(name); } - name = name.replaceAll("[^a-zA-Z0-9_-]+", ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = NON_ALPHANUMERIC_UNDERSCORE_HYPHEN_PLUS.matcher(name).replaceAll(""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. return name; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java index c6786ab82233..7c9a5fe0e92d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java @@ -79,6 +79,17 @@ public class CppHttplibServerCodegen extends AbstractCppCodegen { private final Logger LOGGER = LoggerFactory.getLogger(CppHttplibServerCodegen.class); + /** Matches a string that consists entirely of uppercase ASCII letters. */ + private static final java.util.regex.Pattern CPP_ALL_UPPER = java.util.regex.Pattern.compile("[A-Z]+"); + /** Matches a camelCase boundary for inserting a space separator. */ + private static final java.util.regex.Pattern CPP_CAMEL_BOUNDARY = java.util.regex.Pattern.compile("([a-z])([A-Z])"); + /** Matches the last digit-only part of a name after an underscore (e.g. {@code _2} in {@code method_2}). */ + private static final java.util.regex.Pattern CPP_TRAILING_DIGIT_PART = java.util.regex.Pattern.compile("\\d+"); + /** Matches a split point at a camelCase boundary OR any non-alphanumeric character. */ + private static final java.util.regex.Pattern CPP_CAMEL_SPLIT = java.util.regex.Pattern.compile("(?<=[a-z0-9])(?=[A-Z])|[^a-zA-Z0-9]"); + /** Matches one or more consecutive non-word-and-non-hyphen separators (spaces, underscores, etc.) in a name. */ + private static final java.util.regex.Pattern CPP_WORD_SEPARATORS = java.util.regex.Pattern.compile("[_\\-\\s]+"); + @Override public void preprocessOpenAPI(OpenAPI openAPI) { super.preprocessOpenAPI(openAPI); @@ -1315,9 +1326,9 @@ private void processModelVariable(CodegenProperty var, CodegenModel model) { } } else { // Explicit default value from schema - use qualified enum name - String defaultVal = var.defaultValue.replaceAll("\"", ""); + String defaultVal = var.defaultValue.replace("\"", ""); // Convert numeric enum values (e.g., "100" -> "_100") - if (defaultVal.matches("^[0-9]+$")) { + if (DIGITS_ONLY.matcher(defaultVal).matches()) { defaultVal = "_" + defaultVal; } // Convert to UPPERCASE to match enum definition @@ -1435,7 +1446,7 @@ public String toModelName(String name) { // Try to extract a number suffix for uniqueness if (name.contains("_")) { String[] parts = name.split("_"); - if (parts.length > 1 && parts[parts.length - 1].matches("\\d+")) { + if (parts.length > 1 && CPP_TRAILING_DIGIT_PART.matcher(parts[parts.length - 1]).matches()) { return "InlineModel" + parts[parts.length - 1]; } } @@ -1838,7 +1849,7 @@ private Map stripPathFromClassName(String path) { } // Split by "/", "_", "-", " ", camelCase boundary - String[] segments = clean.split("(?<=[a-z0-9])(?=[A-Z])|[^a-zA-Z0-9]"); + String[] segments = CPP_CAMEL_SPLIT.split(clean); List parts = new ArrayList<>(); for (String seg : segments) { if (seg != null && !seg.trim().isEmpty()) { @@ -1940,9 +1951,9 @@ public String toPascalCase(String name) { } // First, insert spaces before uppercase letters to split camelCase - String normalized = name.replaceAll("([a-z])([A-Z])", "$1 $2"); + String normalized = CPP_CAMEL_BOUNDARY.matcher(name).replaceAll("$1 $2"); // Split on delimiters (hyphens, underscores, spaces) - String[] parts = normalized.split("[_\\-\\s]+"); + String[] parts = CPP_WORD_SEPARATORS.split(normalized); StringBuilder result = new StringBuilder(); for (String part : parts) { if (part != null && !part.isEmpty()) { @@ -2020,7 +2031,7 @@ public String camelize(String name, boolean lowercaseFirstLetter) { } // If the result is the same as input and input is all uppercase, - if (result.equals(name) && name.equals(name.toUpperCase(Locale.ROOT)) && name.matches("[A-Z]+")) { + if (result.equals(name) && name.equals(name.toUpperCase(Locale.ROOT)) && CPP_ALL_UPPER.matcher(name).matches()) { // Handle all-caps words manually result = name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).toLowerCase(Locale.ROOT); if (lowercaseFirstLetter) { @@ -2041,7 +2052,7 @@ public String camelize(String name, boolean lowercaseFirstLetter) { public String toHandlerFunctionName(String httpMethod, String path, Boolean prefix) { String method = toPascalCase(httpMethod); String className = stripPathFromClassName(path).get("className"); - className = className.replaceAll("[^A-Za-z0-9]", ""); + className = NON_ALPHANUMERIC_CHAR.matcher(className).replaceAll(""); if (prefix) { return "handle" + method + "For" + toPascalCase(className); } else { @@ -2057,7 +2068,7 @@ public String toHandlerFunctionName(String httpMethod, String path, Boolean pref */ public String toHandlerFunctionRequest(String path, String method) { String className = stripPathFromClassName(path).get("className"); - className = className.replaceAll("[^A-Za-z0-9]", ""); + className = NON_ALPHANUMERIC_CHAR.matcher(className).replaceAll(""); return toPascalCase(className + toPascalCase(method) + "Request"); } @@ -2070,7 +2081,7 @@ public String toHandlerFunctionRequest(String path, String method) { */ public String toHandlerFunctionResponse(String path, String method) { String className = stripPathFromClassName(path).get("className"); - className = className.replaceAll("[^A-Za-z0-9]", ""); + className = NON_ALPHANUMERIC_CHAR.matcher(className).replaceAll(""); return toPascalCase(className + toPascalCase(method) + "Response"); } @@ -2374,7 +2385,7 @@ private void convertNumericEnumValues(CodegenProperty property) { String enumValStr = enumVal != null ? enumVal.toString() : ""; String convertedVal = enumValStr; // Convert numeric values to have underscore prefix - if (enumValStr.matches("^[0-9]+$")) { + if (DIGITS_ONLY.matcher(enumValStr).matches()) { convertedVal = "_" + enumValStr; } // Convert to UPPERCASE for C++ enum standards (clang style) @@ -2457,7 +2468,7 @@ private void setEnumVendorExtensions(Object varObj, CodegenModel model) { for (String enumVal : enumValues) { // Convert numeric values to words or keep string values as-is String convertedVal = enumVal; - if (enumVal.matches("^[0-9]+$")) { + if (DIGITS_ONLY.matcher(enumVal).matches()) { // Convert number to enum name convertedVal = "_" + enumVal; // Prefix with underscore for numeric values } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java index 541bc1124540..b4b246bbf575 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangServerCodegen.java @@ -276,7 +276,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List operationList = operations.getOperation(); for (CodegenOperation op : operationList) { if (op.path != null) { - op.path = op.path.replaceAll("\\{(.*?)\\}", ":$1"); + op.path = PATH_PARAMETER.matcher(op.path).replaceAll(":$1"); } } return objs; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoEchoServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoEchoServerCodegen.java index 621494b0c7a5..c3be9c4ddf5f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoEchoServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoEchoServerCodegen.java @@ -118,7 +118,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List operationList = operations.getOperation(); for (CodegenOperation op : operationList) { if (op.path != null) { - op.path = op.path.replaceAll("\\{(.*?)\\}", ":$1"); + op.path = PATH_PARAMETER.matcher(op.path).replaceAll(":$1"); } } return objs; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoGinServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoGinServerCodegen.java index ce8721a251ed..c76e4762fc2b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoGinServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoGinServerCodegen.java @@ -133,7 +133,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List operationList = operations.getOperation(); for (CodegenOperation op : operationList) { if (op.path != null) { - op.path = op.path.replaceAll("\\{(.*?)\\}", ":$1"); + op.path = PATH_PARAMETER.matcher(op.path).replaceAll(":$1"); } } return objs; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java index 09ad7254263b..0ed1e43aef51 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java @@ -50,6 +50,12 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(HaskellHttpClientCodegen.class); + private static final Pattern XPATH_EMPTY_SEGMENT = Pattern.compile(",\"\","); + private static final Pattern XPATH_LEADING_EMPTY = Pattern.compile("\"\","); + private static final Pattern XPATH_TRAILING_EMPTY = Pattern.compile(",\"\""); + private static final Pattern XPATH_LEADING_COMMA = Pattern.compile("^\\[,"); + private static final Pattern XPATH_TRAILING_COMMA = Pattern.compile(",]$"); + // source folder where to write the files protected String sourceFolder = "lib"; @@ -1061,11 +1067,11 @@ private void processPathExpr(CodegenOperation op) { xPath = PatternCache.get("\\{" + param.baseName + "}").matcher(xPath).replaceAll("\",toPath " + param.paramName + ",\""); } } - xPath = xPath.replaceAll(",\"\",", ","); - xPath = xPath.replaceAll("\"\",", ","); - xPath = xPath.replaceAll(",\"\"", ","); - xPath = xPath.replaceAll("^\\[,", "["); - xPath = xPath.replaceAll(",]$", "]"); + xPath = XPATH_EMPTY_SEGMENT.matcher(xPath).replaceAll(","); + xPath = XPATH_LEADING_EMPTY.matcher(xPath).replaceAll(","); + xPath = XPATH_TRAILING_EMPTY.matcher(xPath).replaceAll(","); + xPath = XPATH_LEADING_COMMA.matcher(xPath).replaceAll("["); + xPath = XPATH_TRAILING_COMMA.matcher(xPath).replaceAll("]"); } op.vendorExtensions.put(VENDOR_EXTENSION_X_PATH, xPath); } @@ -1427,8 +1433,8 @@ public String toEnumVarName(String value, String datatype) { // number if (num.contains(datatype.toLowerCase(Locale.ROOT))) { String varName = "Num" + value; - varName = varName.replaceAll("-", "Minus_"); - varName = varName.replaceAll("\\+", "Plus_"); + varName = MINUS.matcher(varName).replaceAll("Minus_"); + varName = PLUS.matcher(varName).replaceAll("Plus_"); varName = DOT.matcher(varName).replaceAll("_Dot_"); return "'" + StringUtils.capitalize(sanitizeName(varName)); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java index 8b651ba97529..383575bec51a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java @@ -597,7 +597,7 @@ public CodegenOperation fromOperation(String resourcePath, String httpMethod, Op // Because headers is a Map multiple Set-Cookie headers are currently not possible. If someone // uses the workaround with null bytes, remove them and add add each header to the list: // https://github.com/OAI/OpenAPI-Specification/issues/1237#issuecomment-906603675 - String headerName = h.baseName.replaceAll("\0", ""); + String headerName = h.baseName.replace("\0", ""); String headerType = h.dataType; headerContents.add("Header \"" + headerName + "\" " + headerType); } @@ -608,7 +608,7 @@ public CodegenOperation fromOperation(String resourcePath, String httpMethod, Op String code = "200"; for (CodegenResponse r : op.responses) { - if (r.code.matches("2[0-9][0-9]")) { + if (HTTP_2XX_CODE.matcher(r.code).matches()) { code = r.code; break; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaInflectorServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaInflectorServerCodegen.java index ae73860f8c2a..7bea84bcc4f2 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaInflectorServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaInflectorServerCodegen.java @@ -238,7 +238,7 @@ public String toApiName(String name) { if (name.length() == 0) { return "DefaultController"; } - name = name.replaceAll("[^a-zA-Z0-9]+", "_"); + name = NON_ALPHANUMERIC.matcher(name).replaceAll("_"); return camelize(name) + "Controller"; } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaUndertowServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaUndertowServerCodegen.java index 5437e15f9b59..8d5a39b3d8e8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaUndertowServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaUndertowServerCodegen.java @@ -215,7 +215,7 @@ public String toApiName(String name) { if (name.length() == 0) { return "DefaultHandler"; } - name = name.replaceAll("[^a-zA-Z0-9]+", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = NON_ALPHANUMERIC.matcher(name).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. return camelize(name) + "Handler"; } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java index ceec5e787124..118984fab16e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java @@ -48,6 +48,10 @@ public class JavascriptApolloClientCodegen extends DefaultCodegen implements Cod @SuppressWarnings("hiding") private final Logger LOGGER = LoggerFactory.getLogger(JavascriptApolloClientCodegen.class); + /** Matches JS/JsDoc primitive type names for quoting in documentation. */ + private static final java.util.regex.Pattern JS_PRIMITIVE_TYPES = + java.util.regex.Pattern.compile("\\b(Boolean|Integer|Number|String|Date|Blob)\\b"); + public static final String PROJECT_NAME = "projectName"; public static final String MODULE_NAME = "moduleName"; public static final String PROJECT_DESCRIPTION = "projectDescription"; @@ -782,7 +786,7 @@ protected String setPropertyExampleValue(CodegenProperty p) { * @return Normalized type */ private String normalizeType(String type) { - return type.replaceAll("\\b(Boolean|Integer|Number|String|Date|Blob)\\b", "'$1'"); + return JS_PRIMITIVE_TYPES.matcher(type).replaceAll("'$1'"); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java index f7e887097a5e..f44aabdb513e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptClientCodegen.java @@ -97,6 +97,10 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo @Setter protected boolean useURLSearchParams = true; + /** Matches JS/JsDoc primitive type names for quoting in documentation. */ + private static final java.util.regex.Pattern JS_PRIMITIVE_TYPES = + java.util.regex.Pattern.compile("\\b(Boolean|Integer|Number|String|Date|Blob)\\b"); + public JavascriptClientCodegen() { super(); @@ -809,7 +813,7 @@ protected String setPropertyExampleValue(CodegenProperty p) { * @return Normalized type */ private String normalizeType(String type) { - return type.replaceAll("\\b(Boolean|Integer|Number|String|Date|Blob)\\b", "'$1'"); + return JS_PRIMITIVE_TYPES.matcher(type).replaceAll("'$1'"); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java index 6730dd636649..053256cd2112 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java @@ -56,6 +56,10 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen private final Logger LOGGER = LoggerFactory.getLogger(KotlinSpringServerCodegen.class); + /** Matches {@code kotlin.collections.Mutable} prefix — replaced with its immutable counterpart. */ + private static final java.util.regex.Pattern KOTLIN_MUTABLE_COLLECTION = + java.util.regex.Pattern.compile("kotlin\\.collections\\.Mutable"); + private static final HashSet VARIABLE_RESERVED_WORDS = new HashSet<>(Arrays.asList( "ApiClient", @@ -1363,7 +1367,7 @@ public Map postProcessSupportingFileData(Map obj private String getNonMutableContainerTypeIfNeeded(String type) { if (type != null && type.contains("kotlin.collections.Mutable")) { - return type.replaceAll("kotlin\\.collections\\.Mutable", "kotlin.collections."); + return KOTLIN_MUTABLE_COLLECTION.matcher(type).replaceAll("kotlin.collections."); } return type; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KtormSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KtormSchemaCodegen.java index f2ea9f4ca507..6113f049f306 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KtormSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KtormSchemaCodegen.java @@ -44,7 +44,7 @@ public class KtormSchemaCodegen extends AbstractKotlinCodegen { private final Logger LOGGER = LoggerFactory.getLogger(KtormSchemaCodegen.class); - private static final Pattern UNSAFE_IDENTIFIER_CHARS = Pattern.compile("[^0-9a-zA-Z$_\\x0080-\\xFFFF]"); + private static final Pattern UNSAFE_IDENTIFIER_CHARS = Pattern.compile("[^0-9a-zA-Z$_\\u0080-\\uFFFF]"); public static final String VENDOR_EXTENSION_SCHEMA = "x-ktorm-schema"; public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName"; @@ -1063,13 +1063,13 @@ public String toColumnName(String name) { public String toIdentifier(String name, String prefix, String suffix) { String escapedName = escapeQuotedIdentifier(name); // Database, table, and column names cannot end with space characters. - if (escapedName.matches(".*\\s$")) { + if (ENDS_WITH_WHITESPACE.matcher(escapedName).matches()) { LOGGER.warn("Database, table, and column names cannot end with space characters. Check '{}' name", name); - escapedName = escapedName.replaceAll("\\s+$", ""); + escapedName = TRAILING_WHITESPACE.matcher(escapedName).replaceAll(""); } // Identifiers may begin with a digit but unless quoted may not consist solely of digits. - if (escapedName.matches("^\\d+$")) { + if (DIGITS_ONLY.matcher(escapedName).matches()) { LOGGER.warn("Database, table, and column names cannot consist solely of digits. Check '{}' name", name); escapedName = prefix + escapedName + suffix; } @@ -1167,7 +1167,7 @@ public String toSrcPath(String packageName) { // Trim prefix file separators from package path String packagePath = StringUtils.removeStart( // Replace period, backslash, forward slash with file separator in package name - packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), + PACKAGE_SEPARATOR.matcher(packageName).replaceAll(Matcher.quoteReplacement("/")), File.separator ); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java index 486b8a6a11bc..4232dbbfcd42 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/LuaClientCodegen.java @@ -546,8 +546,8 @@ public String toEnumVarName(String name, String datatype) { // number if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java index e80c374f70bf..ecb67b92bfc3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/MysqlSchemaCodegen.java @@ -1086,13 +1086,13 @@ public String toColumnName(String name) { public String toMysqlIdentifier(String name, String prefix, String suffix) { String escapedName = escapeMysqlQuotedIdentifier(name); // Database, table, and column names cannot end with space characters. - if (escapedName.matches(".*\\s$")) { + if (ENDS_WITH_WHITESPACE.matcher(escapedName).matches()) { LOGGER.warn("Database, table, and column names cannot end with space characters. Check '{}' name", name); - escapedName = escapedName.replaceAll("\\s+$", ""); + escapedName = TRAILING_WHITESPACE.matcher(escapedName).replaceAll(""); } // Identifiers may begin with a digit but unless quoted may not consist solely of digits. - if (escapedName.matches("^\\d+$")) { + if (DIGITS_ONLY.matcher(escapedName).matches()) { LOGGER.warn("Database, table, and column names cannot consist solely of digits. Check '{}' name", name); escapedName = prefix + escapedName + suffix; } @@ -1211,7 +1211,7 @@ public String toSrcPath(String packageName) { // Trim prefix file separators from package path String packagePath = StringUtils.removeStart( // Replace period, backslash, forward slash with file separator in package name - packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), + PACKAGE_SEPARATOR.matcher(packageName).replaceAll(Matcher.quoteReplacement("/")), File.separator ); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java index 2448f1dc7e00..dc23d5c61f47 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java @@ -35,6 +35,7 @@ import java.io.File; import java.util.*; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.openapitools.codegen.CodegenConstants.*; @@ -43,7 +44,7 @@ public class N4jsClientCodegen extends DefaultCodegen implements CodegenConfig { public static final String CHECK_REQUIRED_PARAMS_NOT_NULL = "checkRequiredParamsNotNull"; public static final String CHECK_SUPERFLUOUS_BODY_PROPS = "checkSuperfluousBodyProps"; public static final String GENERATE_DEFAULT_API_EXECUTER = "generateDefaultApiExecuter"; - + private static final Pattern COMPOSED_TYPE_DELIMITERS = Pattern.compile("[|&<>]"); final Logger LOGGER = LoggerFactory.getLogger(N4jsClientCodegen.class); final Set forbiddenChars = new HashSet<>(); @@ -300,7 +301,7 @@ protected void addImport(Set importsToBeAddedTo, String type) { } private String[] splitComposedType(String name) { - return name.replace(" ", "").split("[|&<>]"); + return COMPOSED_TYPE_DELIMITERS.split(name.replace(" ", "")); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java index 072db3881810..30bddfe35d1f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NimClientCodegen.java @@ -45,6 +45,14 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig { public static final String PROJECT_NAME = "projectName"; + /** Matches an underscore, one or more digits, and a trailing underscore; used to strip version markers. */ + private static final Pattern UNDERSCORE_DIGITS_UNDERSCORE = Pattern.compile("_(\\d+)_"); + /** Matches an underscore followed by one or more digits at the end of a string. */ + private static final Pattern UNDERSCORE_DIGITS_END = Pattern.compile("_(\\d+)$"); + /** Matches a valid Nim type name: starts with an uppercase letter and continues with CamelCase segments. */ + private static final Pattern NIM_TYPE_NAME = Pattern.compile( + "^(?:[A-Z]|[a-z]|[\\x80-\\xff])(_?(?:[A-Z]|[a-z]|[\\x80-\\xff]|[0-9]))*$"); + @Setter protected String packageName = "openapiclient"; @Setter protected String packageVersion = "1.0.0"; @@ -443,12 +451,8 @@ private String normalizeSchemaName(String name) { if (name == null) { return null; } - // Remove underscores around and before digits (HTTP status codes, version numbers, etc.) - // e.g., "GetComments_200_response" -> "GetComments200response" - // e.g., "Config_anyOf_1" -> "ConfiganyOf1" - // This ensures consistent handling whether the name comes with or without underscores - name = name.replaceAll("_(\\d+)_", "$1"); // Underscores on both sides - name = name.replaceAll("_(\\d+)$", "$1"); // Trailing underscore before digits + name = UNDERSCORE_DIGITS_UNDERSCORE.matcher(name).replaceAll("$1"); // Underscores on both sides + name = UNDERSCORE_DIGITS_END.matcher(name).replaceAll("$1"); // Trailing underscore before digits return name; } @@ -715,7 +719,7 @@ public String toEnumName(CodegenProperty property) { private boolean isValidIdentifier(String identifier) { //see https://nim-lang.org/docs/manual.html#lexical-analysis-identifiers-amp-keywords - return identifier.matches("^(?:[A-Z]|[a-z]|[\\x80-\\xff])(_?(?:[A-Z]|[a-z]|[\\x80-\\xff]|[0-9]))*$"); + return NIM_TYPE_NAME.matcher(identifier).matches(); } /** diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NodeJSExpressServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NodeJSExpressServerCodegen.java index 61931624de0b..34927e1ec01a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NodeJSExpressServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/NodeJSExpressServerCodegen.java @@ -343,12 +343,11 @@ public void preprocessOpenAPI(OpenAPI openAPI) { if (info.getTitle() != null) { // when info.title is defined, use it for projectName // used in package.json - projectName = info.getTitle() - .replaceAll("[^a-zA-Z0-9]", "-") - .replaceAll("^[-]*", "") - .replaceAll("[-]*$", "") - .replaceAll("[-]{2,}", "-") - .toLowerCase(Locale.ROOT); + projectName = NON_ALPHANUMERIC_CHAR.matcher(info.getTitle()).replaceAll("-"); + projectName = LEADING_HYPHENS.matcher(projectName).replaceAll(""); + projectName = TRAILING_HYPHENS.matcher(projectName).replaceAll(""); + projectName = MULTI_HYPHEN.matcher(projectName).replaceAll("-"); + projectName = projectName.toLowerCase(Locale.ROOT); this.additionalProperties.put("projectName", projectName); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java index f7e6a6f3d845..4e93ba6d44f9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java @@ -38,6 +38,7 @@ import java.io.File; import java.util.*; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.capitalize; @@ -51,6 +52,15 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig static final String X_MODEL_MODULE = "x-model-module"; + /** Matches a model name that starts with a digit or underscore (invalid OCaml identifier start). */ + private static final Pattern OCAML_INVALID_NAME_START = Pattern.compile("^\\d.*|^_.*"); + /** Matches a string that does NOT start with a letter or underscore. */ + private static final Pattern OCAML_INVALID_IDENTIFIER_START = Pattern.compile("^[^a-zA-Z_]"); + /** Matches a trailing {@code " list"} type suffix in OCaml return types. */ + private static final Pattern OCAML_LIST_SUFFIX = Pattern.compile(" list$"); + /** Matches a comma delimiter used to split enum value strings. */ + private static final Pattern COMMA_DELIMITER = Pattern.compile(","); + @Setter protected String packageName = "openapi"; @Setter @@ -689,7 +699,7 @@ public String toModelFilename(String name) { } // model name starts with number or _ - if (name.matches("^\\d.*|^_.*")) { + if (OCAML_INVALID_NAME_START.matcher(name).matches()) { LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, "model_" + name); name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) @@ -791,7 +801,7 @@ public String toOperationId(String operationId) { String sanitizedOperationId = sanitizeName(operationId); // method name cannot use reserved keyword, e.g. return - if (isReservedWord(sanitizedOperationId) || sanitizedOperationId.matches("^[0-9].*")) { + if (isReservedWord(sanitizedOperationId) || STARTS_WITH_DIGIT.matcher(sanitizedOperationId).matches()) { LOGGER.warn("{} (reserved word) cannot be used as method name. Renamed to {}", operationId, underscore("call_" + operationId)); sanitizedOperationId = "call_" + sanitizedOperationId; } @@ -808,7 +818,7 @@ private Map allowableValues(String valueString) { private List> buildEnumValues(String valueString) { List> result = new ArrayList<>(); - for (String v : valueString.split(",")) { + for (String v : COMMA_DELIMITER.split(valueString)) { Map m = new HashMap<>(); String value = v.isEmpty() ? "empty" : v; m.put("name", value); @@ -834,7 +844,7 @@ private String ocamlizeEnumValue(String value) { toEnumValueName(value.isEmpty() ? "empty" : value) .replace(" ", "_"); - if (!sanitizedValue.matches("^[a-zA-Z_].*")) { + if (OCAML_INVALID_IDENTIFIER_START.matcher(sanitizedValue).find()) { sanitizedValue = "_" + sanitizedValue; } return "`" + capitalize(sanitizedValue); @@ -846,7 +856,7 @@ private CodegenModel buildEnumModel(String enumName, String values) { m.setName(enumName); m.setClassname(enumName); m.setDataType(enumName); - String[] vals = values.split(","); + String[] vals = COMMA_DELIMITER.split(values); if (vals.length == 1) { m.setDefaultValue(ocamlizeEnumValue(vals[0])); } @@ -879,7 +889,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List advancedMappingTypes = new HashSet<>(); + /** Matches any character that is not alphanumeric or underscore; used in model name normalization. */ + private static final java.util.regex.Pattern NON_ALNUM_UNDERSCORE = java.util.regex.Pattern.compile("[^0-9a-zA-Z_]"); + /** Matches a single uppercase letter or underscore only (e.g. {@code "A"}, {@code "_"}). */ + private static final java.util.regex.Pattern SINGLE_UPPER_OR_UNDERSCORE = java.util.regex.Pattern.compile("^[A-Z_]$"); + public ObjcClientCodegen() { super(); @@ -462,7 +468,7 @@ public String toModelName(String type) { * @return model Name in ObjC style guide */ public String toModelNameWithoutReservedWordCheck(String type) { - type = type.replaceAll("[^0-9a-zA-Z_]", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + type = NON_ALNUM_UNDERSCORE.matcher(type).replaceAll("_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // language build-in classes if (typeMapping.keySet().contains(type) || @@ -557,18 +563,18 @@ public String toVarName(String name) { name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. // if it's all upper case, do noting - if (name.matches("^[A-Z_]$")) { + if (SINGLE_UPPER_OR_UNDERSCORE.matcher(name).matches()) { return name; } // if name starting with special word, escape with '_' for (String specialWord : specialWords) { - if (name.matches("(?i:^" + specialWord + ".*)")) + if (PatternCache.get("(?i:^" + specialWord + ".*)").matcher(name).matches()) name = escapeSpecialWord(name); } if (name.startsWith(classPrefix)) { - name = name.replaceFirst(classPrefix, ""); //remove the class prefix, e.g. SWGPet => Pet + name = PatternCache.get(java.util.regex.Pattern.quote(classPrefix)).matcher(name).replaceFirst(""); //remove the class prefix, e.g. SWGPet => Pet } // camelize (lower first character) the variable name diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java index f4be0fff4122..33725abf475e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlim4ServerCodegen.java @@ -29,6 +29,7 @@ import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.OperationMap; import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.PatternCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,6 +61,17 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen { protected String interfacesDirName = "Interfaces"; protected String interfacesPackage = ""; + /** Precompiled patterns for URL-encoding fixups in {@link #encodePath}. */ + private static final java.util.regex.Pattern URL_ENC_PLUS = java.util.regex.Pattern.compile("\\+"); + private static final java.util.regex.Pattern URL_ENC_SLASH = java.util.regex.Pattern.compile("%2F", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_OPEN_CB = java.util.regex.Pattern.compile("%7B", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_CLOSE_CB = java.util.regex.Pattern.compile("%7D", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_OPEN_SB = java.util.regex.Pattern.compile("%5B", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_CLOSE_SB = java.util.regex.Pattern.compile("%5D", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_COLON = java.util.regex.Pattern.compile("%3A", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_PLUS_SIGN = java.util.regex.Pattern.compile("%2B", java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern URL_ENC_BACKSLASH_D = java.util.regex.Pattern.compile("%5C%5Cd", java.util.regex.Pattern.CASE_INSENSITIVE); + public PhpSlim4ServerCodegen() { super(); @@ -316,8 +328,8 @@ public String toApiTestFilename(String name) { */ private void addUserClassnameToOperations(OperationMap operations) { String classname = operations.getClassname(); - classname = classname.replaceAll("^" + abstractNamePrefix, ""); - classname = classname.replaceAll(abstractNameSuffix + "$", ""); + classname = PatternCache.get("^" + abstractNamePrefix).matcher(classname).replaceAll(""); + classname = PatternCache.get(abstractNameSuffix + "$").matcher(classname).replaceAll(""); operations.put(USER_CLASSNAME_KEY, classname); } @@ -345,16 +357,25 @@ public String encodePath(String input) { // Trim the string to avoid leading and trailing spaces. input = input.trim(); - return URLEncoder.encode(input, StandardCharsets.UTF_8) - .replaceAll("\\+", "%20") - .replaceAll("\\%2F", "/") - .replaceAll("\\%7B", "{") // keep { part of complex placeholders - .replaceAll("\\%7D", "}") // } part - .replaceAll("\\%5B", "[") // [ part - .replaceAll("\\%5D", "]") // ] part - .replaceAll("\\%3A", ":") // : part - .replaceAll("\\%2B", "+") // + part - .replaceAll("\\%5C\\%5Cd", "\\\\d"); // \d part + return URL_ENC_BACKSLASH_D.matcher( + URL_ENC_PLUS_SIGN.matcher( + URL_ENC_COLON.matcher( + URL_ENC_CLOSE_SB.matcher( + URL_ENC_OPEN_SB.matcher( + URL_ENC_CLOSE_CB.matcher( + URL_ENC_OPEN_CB.matcher( + URL_ENC_SLASH.matcher( + URL_ENC_PLUS.matcher( + URLEncoder.encode(input, StandardCharsets.UTF_8)) + .replaceAll("%20")) + .replaceAll("/")) + .replaceAll("{")) + .replaceAll("}")) + .replaceAll("[")) + .replaceAll("]")) + .replaceAll(":")) + .replaceAll("+")) + .replaceAll("\\\\d"); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java index 35b6ac62ef14..b6c51c5ae4c6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -38,9 +38,8 @@ @SuppressWarnings("unchecked") public class PostgresqlSchemaCodegen extends DefaultCodegen { private final Logger LOGGER = LoggerFactory.getLogger(PostgresqlSchemaCodegen.class); - - /** Characters not allowed in an unquoted PostgreSQL identifier: outside [0-9,a-z,A-Z,$,_,U+0080..U+FFFF]. */ - private static final Pattern UNSAFE_IDENTIFIER_CHARS = Pattern.compile("[^0-9a-zA-Z$_\\u0080-\\uFFFF]"); + private static final Pattern UNSAFE_UNQUOTED_IDENTIFIER_CHARS = Pattern.compile("[^0-9a-zA-Z$_\\u0080-\\uFFFF]"); + private static final Pattern UNSAFE_QUOTED_IDENTIFIER_CHARS = Pattern.compile("[^\\u0001-\\u007F\\u0080-\\uFFFF]"); public static final String VENDOR_EXTENSION_POSTGRESQL_SCHEMA = "x-postgresql-schema"; public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName"; @@ -1320,14 +1319,14 @@ public String toColumnName(String name) { public String toPostgresqlIdentifier(String name, String prefix, String suffix) { String escapedName = escapePostgresqlQuotedIdentifier(name); // Database, table, and column names cannot end with space characters. - if (escapedName.matches(".*\\s$")) { + if (ENDS_WITH_WHITESPACE.matcher(escapedName).matches()) { LOGGER.warn("Database, table, and column names cannot end with space characters. Check '{}' name", name); - escapedName = escapedName.replaceAll("\\s+$", ""); + escapedName = TRAILING_WHITESPACE.matcher(escapedName).replaceAll(""); } // Identifiers may begin with a digit but unless quoted may not consist solely // of digits. - if (escapedName.matches("^\\d+$")) { + if (DIGITS_ONLY.matcher(escapedName).matches()) { LOGGER.warn("Database, table, and column names cannot consist solely of digits. Check '{}' name", name); escapedName = prefix + escapedName + suffix; } @@ -1349,7 +1348,7 @@ public String toPostgresqlIdentifier(String name, String prefix, String suffix) public String escapePostgresqlUnquotedIdentifier(String identifier) { // ASCII: [0-9,a-z,A-Z$_] (basic Latin letters, digits 0-9, dollar, underscore) // Extended: U+0080 .. U+FFFF - Matcher matcher = UNSAFE_IDENTIFIER_CHARS.matcher(identifier); + Matcher matcher = UNSAFE_UNQUOTED_IDENTIFIER_CHARS.matcher(identifier); if (matcher.find()) { LOGGER.warn("Identifier '{}' contains unsafe characters out of [0-9,a-z,A-Z$_] and U+0080..U+FFFF range", identifier); @@ -1373,12 +1372,11 @@ public String escapePostgresqlUnquotedIdentifier(String identifier) { */ public String escapePostgresqlQuotedIdentifier(String identifier) { // ASCII: U+0001 .. U+007F Extended: U+0080 .. U+FFFF - Pattern regexp = Pattern.compile("[^\\u0001-\\u007F\\u0080-\\uFFFF]"); - Matcher matcher = regexp.matcher(identifier); + Matcher matcher = UNSAFE_QUOTED_IDENTIFIER_CHARS.matcher(identifier); if (matcher.find()) { LOGGER.warn("Identifier '{}' contains unsafe characters out of U+0001..U+007F and U+0080..U+FFFF range", identifier); - identifier = identifier.replaceAll("[^\\u0001-\\u007F\\u0080-\\uFFFF]", ""); + identifier = matcher.reset().replaceAll(""); } // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not @@ -1460,7 +1458,7 @@ public String toSrcPath(String packageName) { // Trim prefix file separators from package path String packagePath = StringUtils.removeStart( // Replace period, backslash, forward slash with file separator in package name - packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), + PACKAGE_SEPARATOR.matcher(packageName).replaceAll(Matcher.quoteReplacement("/")), File.separator); // Trim trailing file separators from the overall path diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java index 512192589db4..8c9448f307bc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellClientCodegen.java @@ -42,6 +42,11 @@ public class PowerShellClientCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(PowerShellClientCodegen.class); + + /** Captures the last path segment of a URL (for extracting PS Gallery package ID). */ + private static final java.util.regex.Pattern GALLERY_URL_LAST_SEGMENT = java.util.regex.Pattern.compile(".*/([^/?]+).*"); + /** Matches two or more consecutive newlines — for normalising example output. */ + private static final java.util.regex.Pattern MULTI_NEWLINE = java.util.regex.Pattern.compile("[\n]{2,}"); @Setter private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}"; @@ -702,7 +707,7 @@ public void processOpts() { 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")); + additionalProperties.put("powershellGalleryId", GALLERY_URL_LAST_SEGMENT.matcher(powershellGalleryUrl).replaceFirst("$1")); } if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) { @@ -1202,7 +1207,7 @@ private String constructExampleCode(CodegenParameter codegenParameter, HashMap modelMaps, HashMap processedModelMap, boolean requiredOnly) { @@ -1541,8 +1546,8 @@ public String toEnumVarName(String name, String 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 = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); varName = DOT.matcher(varName).replaceAll("_DOT_"); return "NUMBER_" + varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java index 3c77fa895feb..dd06b7cc7fb4 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RClientCodegen.java @@ -709,8 +709,8 @@ public String toEnumVarName(String name, String datatype) { // number if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java index b523423540f9..328be26b8dd1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RubyClientCodegen.java @@ -340,7 +340,7 @@ public String getHelp() { */ @SuppressWarnings("static-method") public String generateModuleName(String gemName) { - return camelize(gemName.replaceAll("[^\\w]+", "_")); + return camelize(NON_WORD_PLUS.matcher(gemName).replaceAll("_")); } /** @@ -351,7 +351,7 @@ public String generateModuleName(String gemName) { */ @SuppressWarnings("static-method") public String generateGemName(String moduleName) { - return underscore(moduleName.replaceAll("[^\\w]+", "")); + return underscore(NON_WORD_PLUS.matcher(moduleName).replaceAll("")); } @Override diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaAkkaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaAkkaClientCodegen.java index 749ea34f98ef..f1608604cf23 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaAkkaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaAkkaClientCodegen.java @@ -305,7 +305,7 @@ public String toDefaultValue(Schema p) { private static class JavadocLambda extends CustomLambda { @Override public String formatFragment(String fragment) { - final String[] lines = fragment.split("\\r?\\n"); + final String[] lines = DefaultCodegen.SPLIT_ON_NEWLINE.split(fragment); final StringBuilder sb = new StringBuilder(); sb.append(" /**\n"); for (String line : lines) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java index 7179f7374817..0e51eee3b5ec 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java @@ -45,6 +45,11 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code // citizen of cask private static final String AdditionalPropertiesType = "Value"; + /** Matches any character that is not alphanumeric, underscore, ], or [. */ + private static final Pattern NON_IDENTIFIER_CHAR = Pattern.compile("[^a-zA-Z0-9_\\]\\[]"); + /** Matches the inner type of a collection, e.g. {@code List[Foo]} → {@code Foo}. */ + private static final Pattern INNER_COLLECTION_TYPE = Pattern.compile(".*\\[(.*)]"); + private final Logger LOGGER = LoggerFactory.getLogger(ScalaCaskServerCodegen.class); @Override @@ -323,7 +328,7 @@ public String apiInterfaceFileFolder() { static String asMethod(final String input) { // Remove all non-alphanumeric characters using regex - String alphanumeric = input.replaceAll("[^a-zA-Z0-9]", ""); + String alphanumeric = NON_ALPHANUMERIC_CHAR.matcher(input).replaceAll(""); // Ensure the method name doesn't start with a digit if (alphanumeric.isEmpty()) { @@ -940,7 +945,7 @@ private void postProcessProperty(final CodegenProperty p) { * type: string * }}} */ - if (p.datatypeWithEnum != null && p.datatypeWithEnum.matches(".*[^a-zA-Z0-9_\\]\\[].*")) { + if (p.datatypeWithEnum != null && NON_IDENTIFIER_CHAR.matcher(p.datatypeWithEnum).find()) { p.datatypeWithEnum = fixBackTicks(p.datatypeWithEnum); } @@ -1238,11 +1243,11 @@ private static String chompSuffix(String str, String suffix) { } private static boolean hasBrackets(String str) { - return str.matches("^\\{(.*)\\}$"); + return IS_PATH_PARAM.matcher(str).matches(); } static String containerType(String dataType) { - String fixedForList = dataType.replaceAll(".*\\[(.*)\\]", "$1"); + String fixedForList = INNER_COLLECTION_TYPE.matcher(dataType).replaceAll("$1"); // if it is a map, we want the value type final String[] parts = fixedForList.split(","); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaFinchServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaFinchServerCodegen.java index e6144e081f92..d51074d73f40 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaFinchServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaFinchServerCodegen.java @@ -32,6 +32,10 @@ import java.util.*; public class ScalaFinchServerCodegen extends DefaultCodegen implements CodegenConfig { + + /** Matches the non-bracket prefix of a collection type, e.g. {@code "List"} in {@code "List[String]"}. */ + private static final java.util.regex.Pattern NON_BRACKET_PREFIX = java.util.regex.Pattern.compile("^[^\\[]+"); + private final Logger LOGGER = LoggerFactory.getLogger(ScalaFinchServerCodegen.class); protected String invokerPackage = "org.openapitools.client"; protected String groupId = "org.openapitools"; @@ -383,7 +387,7 @@ private void generateScalaPath(CodegenOperation op) { for (String item : items) { - if (item.matches("^\\{(.*)}$")) { // wrap in {} + if (IS_PATH_PARAM.matcher(item).matches()) { // wrap in {} // find the datatype of the parameter final CodegenParameter cp = op.pathParams.get(pathParamIndex); @@ -429,7 +433,7 @@ private void generateInputParameters(CodegenOperation op) { p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType); } else if (p.isContainer || p.isArray) { p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p, "params", false)); - p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType.replaceAll("^[^\\[]+", "Seq")); + p.vendorExtensions.put("x-codegen-normalized-input-type", NON_BRACKET_PREFIX.matcher(p.dataType).replaceAll("Seq")); } else if (p.isQueryParam) { p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p, "param", true)); p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p)); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sServerCodegen.java index fe8098feb1ce..e92778fab32e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sServerCodegen.java @@ -787,7 +787,7 @@ private void generateScalaPath(CodegenOperation op) { for (String item : items) { - if (item.matches("^\\{(.*)}$")) { // wrap in {} + if (IS_PATH_PARAM.matcher(item).matches()) { // wrap in {} // find the datatype of the parameter final CodegenParameter cp = op.pathParams.get(pathParamIndex); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java index 1eb47d1d0f80..293680bcb3a3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java @@ -303,7 +303,7 @@ public String toDefaultValue(Schema p) { private static class JavadocLambda extends CustomLambda { @Override public String formatFragment(String fragment) { - final String[] lines = fragment.split("\\r?\\n"); + final String[] lines = DefaultCodegen.SPLIT_ON_NEWLINE.split(fragment); final StringBuilder sb = new StringBuilder(); sb.append(" /**\n"); for (String line : lines) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4ClientCodegen.java index b628cb615500..20ae0974ee0b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4ClientCodegen.java @@ -585,7 +585,7 @@ public String getInvokerPackage(Map additionalProperties) { private static class JavadocLambda extends CustomLambda { @Override public String formatFragment(String fragment) { - final String[] lines = fragment.split("\\r?\\n"); + final String[] lines = DefaultCodegen.SPLIT_ON_NEWLINE.split(fragment); final StringBuilder sb = new StringBuilder(); sb.append(" /**\n"); for (String line : lines) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4JsoniterClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4JsoniterClientCodegen.java index c47a7a390680..94fd2cb535cb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4JsoniterClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttp4JsoniterClientCodegen.java @@ -670,7 +670,7 @@ public String getInvokerPackage(Map additionalProperties) { private static class JavadocLambda extends CustomLambda { @Override public String formatFragment(String fragment) { - final String[] lines = fragment.split("\\r?\\n"); + final String[] lines = DefaultCodegen.SPLIT_ON_NEWLINE.split(fragment); final StringBuilder sb = new StringBuilder(); sb.append(" /**\n"); for (String line : lines) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java index f4aa0e66327d..ac1f0acae9e5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java @@ -585,7 +585,7 @@ public String getInvokerPackage(Map additionalProperties) { private static class JavadocLambda extends CustomLambda { @Override public String formatFragment(String fragment) { - final String[] lines = fragment.split("\\r?\\n"); + final String[] lines = DefaultCodegen.SPLIT_ON_NEWLINE.split(fragment); final StringBuilder sb = new StringBuilder(); sb.append(" /**\n"); for (String line : lines) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java index f5299e1a429f..ab15dd6a6ab6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift5ClientCodegen.java @@ -1032,7 +1032,7 @@ public String toEnumVarName(String name, String datatype) { // Camelize only when we have a structure defined below boolean camelized = false; - if (name.matches("[A-Z][a-z0-9]+[a-zA-Z0-9]*")) { + if (CAMEL_INITIAL.matcher(name).matches()) { name = camelize(name, LOWERCASE_FIRST_LETTER); camelized = true; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java index 47477c334774..7027918f56a7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java @@ -1075,7 +1075,7 @@ public String toEnumVarName(String name, String datatype) { // Camelize only when we have a structure defined below boolean camelized = false; - if (name.matches("[A-Z][a-z0-9]+[a-zA-Z0-9]*")) { + if (CAMEL_INITIAL.matcher(name).matches()) { name = camelize(name, LOWERCASE_FIRST_LETTER); camelized = true; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java index f716dc4c99db..9ef3aef332b0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SwiftCombineClientCodegen.java @@ -51,6 +51,9 @@ public class SwiftCombineClientCodegen extends DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(SwiftCombineClientCodegen.class); + /** Matches a Swift identifier starting with an uppercase letter followed by lowercase+digits. */ + // CAMEL_INITIAL pattern is inherited from DefaultCodegen + public static final String PROJECT_NAME = "projectName"; public static final String MAP_FILE_BINARY_TO_DATA = "mapFileBinaryToData"; @Setter protected String projectName = "OpenAPIClient"; @@ -595,7 +598,7 @@ public String toEnumVarName(String name, String datatype) { // Camelize only when we have a structure defined below Boolean camelized = false; - if (name.matches("[A-Z][a-z0-9]+[a-zA-Z0-9]*")) { + if (CAMEL_INITIAL.matcher(name).matches()) { name = camelize(name, LOWERCASE_FIRST_LETTER); camelized = true; } @@ -610,8 +613,8 @@ public String toEnumVarName(String name, String datatype) { if ("Int".equals(datatype) || "Int32".equals(datatype) || "Int64".equals(datatype) || "Float".equals(datatype) || "Double".equals(datatype)) { String varName = "number" + camelize(name); - varName = varName.replaceAll("-", "minus"); - varName = varName.replaceAll("\\+", "plus"); + varName = MINUS.matcher(varName).replaceAll("minus"); + varName = PLUS.matcher(varName).replaceAll("plus"); varName = DOT.matcher(varName).replaceAll("dot"); return varName; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java index 7cc6bcfbcf98..4b51a7e97056 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java @@ -24,11 +24,13 @@ import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.model.OperationMap; import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.PatternCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.*; +import java.util.regex.Pattern; import static org.openapitools.codegen.utils.StringUtils.camelize; import static org.openapitools.codegen.utils.StringUtils.underscore; @@ -37,6 +39,9 @@ public class TerraformProviderCodegen extends AbstractGoCodegen { private final Logger LOGGER = LoggerFactory.getLogger(TerraformProviderCodegen.class); + /** Case-insensitive match of a trailing "api" suffix. */ + private static final Pattern API_SUFFIX = Pattern.compile("(?i)api$"); + public static final String PROVIDER_NAME = "providerName"; public static final String PROVIDER_ADDRESS = "providerAddress"; public static final String PROVIDER_VERSION = "providerVersion"; @@ -296,7 +301,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List id) if (!idResolved) { - String strippedId = idField.replaceFirst("(?i)^" + resourceName, ""); + String strippedId = PatternCache.get("(?i)^" + java.util.regex.Pattern.quote(resourceName)).matcher(idField).replaceFirst(""); if (!strippedId.isEmpty()) { for (CodegenProperty prop : model.vars) { if (prop.baseName.equalsIgnoreCase(strippedId)) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java index 8190aa46c700..587409afcc73 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java @@ -48,9 +48,9 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCodegen { private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptAngularClientCodegen.class); - private static String CLASS_NAME_PREFIX_PATTERN = "^[a-zA-Z0-9]*$"; - private static String CLASS_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9]*$"; - private static String FILE_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9.-]*$"; + private static final java.util.regex.Pattern CLASS_NAME_PREFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9]*$"); + private static final java.util.regex.Pattern CLASS_NAME_SUFFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9]*$"); + private static final java.util.regex.Pattern FILE_NAME_SUFFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9.-]*$"); public static enum QUERY_PARAM_OBJECT_FORMAT_TYPE {dot, json, key} @@ -657,7 +657,7 @@ public String removeModelPrefixSuffix(String name) { * @param value The value that is being validated. */ private void validateFileSuffixArgument(String argument, String value) { - if (!value.matches(FILE_NAME_SUFFIX_PATTERN)) { + if (!FILE_NAME_SUFFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s file suffix only allows '.', '-' and alphanumeric characters.", argument) ); @@ -672,7 +672,7 @@ private void validateFileSuffixArgument(String argument, String value) { * @param value The value that is being validated. */ private void validateClassPrefixArgument(String argument, String value) { - if (!value.matches(CLASS_NAME_PREFIX_PATTERN)) { + if (!CLASS_NAME_PREFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s class prefix only allows alphanumeric characters.", argument) ); @@ -687,7 +687,7 @@ private void validateClassPrefixArgument(String argument, String value) { * @param value The value that is being validated. */ private void validateClassSuffixArgument(String argument, String value) { - if (!value.matches(CLASS_NAME_SUFFIX_PATTERN)) { + if (!CLASS_NAME_SUFFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s class suffix only allows alphanumeric characters.", argument) ); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java index 0d7bd9d161f8..e61571a7be12 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java @@ -270,7 +270,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { } // Deduce the model file name in kebab case - cm.classFilename = cm.classname.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT); + cm.classFilename = CAMEL_CASE_BOUNDARY.matcher(cm.classname).replaceAll("$1-$2").toLowerCase(Locale.ROOT); //processed enum names if (!withoutPrefixEnums) { @@ -299,7 +299,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { String tsImport = tsModelPackage + "/" + javaImport; m.put("tsImport", tsImport); m.put("class", javaImport); - m.put("filename", javaImport.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT)); + m.put("filename", CAMEL_CASE_BOUNDARY.matcher(javaImport).replaceAll("$1-$2").toLowerCase(Locale.ROOT)); } return objs; } @@ -329,12 +329,12 @@ public String toRegularExpression(String pattern) { @Override public String toModelFilename(String name) { - return super.toModelFilename(name).replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT); + return CAMEL_CASE_BOUNDARY.matcher(super.toModelFilename(name)).replaceAll("$1-$2").toLowerCase(Locale.ROOT); } @Override public String toApiFilename(String name) { - return super.toApiFilename(name).replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT); + return CAMEL_CASE_BOUNDARY.matcher(super.toApiFilename(name)).replaceAll("$1-$2").toLowerCase(Locale.ROOT); } private void addNpmPackageGeneration() { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index 972a276ae15e..8e8a4aba88b8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -59,6 +59,9 @@ public class TypeScriptClientCodegen extends AbstractTypeScriptClientCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptClientCodegen.class); + /** Splits a TypeScript composed-type string on {@code |}, {@code &}, {@code <}, or {@code >}. */ + private static final java.util.regex.Pattern COMPOSED_TYPE_DELIMITERS = java.util.regex.Pattern.compile("[|&<>]"); + private static final String FRAMEWORK_SWITCH = "framework"; private static final String FRAMEWORK_SWITCH_DESC = "Specify the framework which should be used in the client code."; private static final String[] FRAMEWORKS = {"fetch-api", "jquery"}; @@ -315,8 +318,8 @@ public String toEnumVarName(String name, String datatype) { if ("number".equals(datatype)) { String varName = "NUMBER_" + name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); + varName = MINUS.matcher(varName).replaceAll("MINUS_"); + varName = PLUS.matcher(varName).replaceAll("PLUS_"); varName = DOT.matcher(varName).replaceAll("_DOT_"); return varName; } @@ -1131,6 +1134,6 @@ protected void addImport(Set importsToBeAddedTo, String type) { * @return list of types */ protected String[] splitComposedTypes(String type) { - return type.replace(" ", "").split("[|&<>]"); + return COMPOSED_TYPE_DELIMITERS.split(type.replace(" ", "")); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsClientCodegen.java index cfd075645e4c..854e702f7a72 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsClientCodegen.java @@ -41,8 +41,8 @@ public class TypeScriptNestjsClientCodegen extends AbstractTypeScriptClientCodegen { private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptNestjsClientCodegen.class); - private static String CLASS_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9]*$"; - private static String FILE_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9.-]*$"; + private static final java.util.regex.Pattern CLASS_NAME_SUFFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9]*$"); + private static final java.util.regex.Pattern FILE_NAME_SUFFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9.-]*$"); public static final String NPM_REPOSITORY = "npmRepository"; public static final String WITH_INTERFACES = "withInterfaces"; @@ -496,7 +496,7 @@ public String removeModelPrefixSuffix(String name) { * @param value The value that is being validated. */ private void validateFileSuffixArgument(String argument, String value) { - if (!value.matches(FILE_NAME_SUFFIX_PATTERN)) { + if (!FILE_NAME_SUFFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s file suffix only allows '.', '-' and alphanumeric characters.", argument) ); @@ -511,7 +511,7 @@ private void validateFileSuffixArgument(String argument, String value) { * @param value The value that is being validated. */ private void validateClassSuffixArgument(String argument, String value) { - if (!value.matches(CLASS_NAME_SUFFIX_PATTERN)) { + if (!CLASS_NAME_SUFFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s class suffix only allows alphanumeric characters.", argument) ); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java index d8f23dea7899..7e9fa789d005 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNestjsServerCodegen.java @@ -42,8 +42,9 @@ public class TypeScriptNestjsServerCodegen extends AbstractTypeScriptClientCodegen { private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptNestjsServerCodegen.class); - private static String CLASS_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9]*$"; - private static String FILE_NAME_SUFFIX_PATTERN = "^[a-zA-Z0-9.-]*$"; + private static final java.util.regex.Pattern CLASS_NAME_SUFFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9]*$"); + private static final java.util.regex.Pattern FILE_NAME_SUFFIX_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9.-]*$"); + private static final java.util.regex.Pattern QUOTED_STRING = java.util.regex.Pattern.compile("([\"'].*[\"'])"); public static final String NPM_REPOSITORY = "npmRepository"; public static final String TAGGED_UNIONS = "taggedUnions"; @@ -291,7 +292,7 @@ private boolean isLanguagePrimitive(String type) { private boolean isInlineUnion(String type) { return Arrays.stream(type.split("\\|")) .map(String::trim) - .allMatch(value -> value.matches("([\"'].*[\"'])")); + .allMatch(value -> QUOTED_STRING.matcher(value).matches()); } private boolean isLanguageGenericType(String type) { @@ -561,7 +562,7 @@ public String removeModelPrefixSuffix(String name) { * @param value The value that is being validated. */ private void validateFileSuffixArgument(String argument, String value) { - if (value != null && !value.matches(FILE_NAME_SUFFIX_PATTERN)) { + if (value != null && !FILE_NAME_SUFFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s file suffix only allows '.', '-' and alphanumeric characters.", argument) ); @@ -576,7 +577,7 @@ private void validateFileSuffixArgument(String argument, String value) { * @param value The value that is being validated. */ private void validateClassSuffixArgument(String argument, String value) { - if (value != null && !value.matches(CLASS_NAME_SUFFIX_PATTERN)) { + if (value != null && !CLASS_NAME_SUFFIX_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException( String.format(Locale.ROOT, "%s class suffix only allows alphanumeric characters.", argument) ); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java index 294183c12eb9..532859bd85d6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/XojoClientCodegen.java @@ -476,7 +476,7 @@ public String toEnumVarName(String name, String datatype) { // Camelize only when we have a structure defined below boolean camelized = false; - if (name.matches("[A-Z][a-z0-9]+[a-zA-Z0-9]*")) { + if (CAMEL_INITIAL.matcher(name).matches()) { name = camelize(name); camelized = true; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index 3e355713e7f9..95a88ee7566a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -2017,7 +2017,7 @@ private static ObjectMapper getRightMapper(String data) { */ public static JsonNode readWithInfo(String location, List auths) throws Exception { String data; - location = location.replaceAll("\\\\", "/"); + location = location.replace("\\", "/"); if (location.toLowerCase(Locale.ROOT).startsWith("http")) { data = RemoteUrl.urlToString(location, auths); } else { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java index 47c98f557411..a7bb6460eba7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/URLPathUtils.java @@ -38,7 +38,12 @@ public class URLPathUtils { private static final Logger LOGGER = LoggerFactory.getLogger(URLPathUtils.class); public static final String LOCAL_HOST = "http://localhost"; - public static final Pattern VARIABLE_PATTERN = Pattern.compile("(? userDefinedVariables) { @@ -210,7 +215,7 @@ private static String sanitizeUrl(String url) { } else if (url.startsWith("/")) { url = LOCAL_HOST + url; once(LOGGER).info("'host' (OAS 2.0) or 'servers' (OAS 3.0) not defined in the spec. Default to [{}] for server URL [{}]", LOCAL_HOST, url); - } else if (!url.matches("[a-zA-Z][0-9a-zA-Z.+\\-]+://.+")) { + } else if (!URL_WITH_SCHEME.matcher(url).matches()) { // Add http scheme for urls without a scheme. // 2.0 spec is restricted to the following schemes: "http", "https", "ws", "wss" // 3.0 spec does not have an enumerated list of schemes @@ -235,7 +240,7 @@ private static URL getDefaultUrl() { public static boolean isRelativeUrl(List servers) { if (servers != null && servers.size() > 0) { final Server firstServer = servers.get(0); - return Pattern.matches("^(\\/[\\w\\d.~@-]+)+", firstServer.getUrl()); + return RELATIVE_PATH_URL.matcher(firstServer.getUrl()).matches(); } return false; } From a0172e119b4246759f0d2a5635e29029426c2b11 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Wed, 25 Mar 2026 23:45:56 +0100 Subject: [PATCH 22/22] refactor: introduce shared regex patterns for improved code readability --- .../openapitools/codegen/DefaultCodegen.java | 142 +++++++++--------- 1 file changed, 69 insertions(+), 73 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 18011bf1d0f7..8af0ae7022c7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -98,8 +98,6 @@ public class DefaultCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class); - public static final Pattern SPLIT_ON_SEMICOLON_OR_NEWLINE_REGEX = Pattern.compile("\\s*(;|\\r?\\n)\\s*"); // Splits on semicolon or new line, ignoring surrounding whitespace - public static FeatureSet DefaultFeatureSet; // A cache of sanitized words. The sanitizeName() method is invoked many times with the same @@ -245,109 +243,107 @@ apiTemplateFiles are for API outputs only (controllers/handlers). // sort operations by default protected boolean skipSortingOperations = false; + // --- MIME type patterns --- protected final static Pattern XML_MIME_PATTERN = Pattern.compile("(?i)application/(.*)[+]?xml(;.*)?"); protected final static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application/json(;.*)?"); protected final static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application/vnd.(.*)+json(;.*)?"); - private static final Pattern COMMON_PREFIX_ENUM_NAME = Pattern.compile("[a-zA-Z0-9]+\\z"); + + // --- HTTP / path patterns --- + protected static final Pattern STARTS_WITH_SLASH = Pattern.compile("^/.*"); + protected static final Pattern UNESCAPED_SLASH = Pattern.compile("(?