/**************************************************************************/
/* naming_utils.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "naming_utils.h"
#include "core/string/ucaps.h"
#include "core/templates/hash_map.h"
HashMap<String, String> _create_hashmap_from_vector(Vector<Pair<String, String>> vector) {
HashMap<String, String> hashmap = HashMap<String, String>(vector.size());
for (const Pair<String, String> &pair : vector) {
hashmap.insert(pair.first, pair.second);
}
return hashmap;
}
// Hardcoded collection of PascalCase name conversions.
const HashMap<String, String> pascal_case_name_overrides = _create_hashmap_from_vector({
{ "BitMap", "Bitmap" },
{ "JSONRPC", "JsonRpc" },
{ "Object", "GodotObject" },
{ "OpenXRIPBinding", "OpenXRIPBinding" },
{ "SkeletonModification2DCCDIK", "SkeletonModification2DCcdik" },
{ "SkeletonModification2DFABRIK", "SkeletonModification2DFabrik" },
{ "SkeletonModification3DCCDIK", "SkeletonModification3DCcdik" },
{ "SkeletonModification3DFABRIK", "SkeletonModification3DFabrik" },
{ "System", "System_" },
{ "Thread", "GodotThread" },
});
// Hardcoded collection of PascalCase part conversions.
const HashMap<String, String> pascal_case_part_overrides = _create_hashmap_from_vector({
{ "AA", "AA" }, // Anti Aliasing
{ "AO", "AO" }, // Ambient Occlusion
{ "FILENAME", "FileName" },
{ "FADEIN", "FadeIn" },
{ "FADEOUT", "FadeOut" },
{ "FX", "FX" },
{ "GI", "GI" }, // Global Illumination
{ "GZIP", "GZip" },
{ "HBOX", "HBox" }, // Horizontal Box
{ "ID", "Id" },
{ "IO", "IO" }, // Input/Output
{ "IP", "IP" }, // Internet Protocol
{ "IV", "IV" }, // Initialization Vector
{ "MACOS", "MacOS" },
{ "NODEPATH", "NodePath" },
{ "SPIRV", "SpirV" },
{ "STDIN", "StdIn" },
{ "STDOUT", "StdOut" },
{ "USERNAME", "UserName" },
{ "UV", "UV" },
{ "UV2", "UV2" },
{ "VBOX", "VBox" }, // Vertical Box
{ "WHITESPACE", "WhiteSpace" },
{ "WM", "WM" },
{ "XR", "XR" },
{ "XRAPI", "XRApi" },
});
String _get_pascal_case_part_override(String p_part, bool p_input_is_upper = true) {
if (!p_input_is_upper) {
for (int i = 0; i < p_part.length(); i++) {
p_part[i] = _find_upper(p_part[i]);
}
}
if (pascal_case_part_overrides.has(p_part)) {
return pascal_case_part_overrides.get(p_part);
}
return String();
}
Vector<String> _split_pascal_case(const String &p_identifier) {
Vector<String> parts;
int current_part_start = 0;
bool prev_was_upper = is_ascii_upper_case(p_identifier[0]);
for (int i = 1; i < p_identifier.length(); i++) {
if (prev_was_upper) {
if (is_digit(p_identifier[i]) || is_ascii_lower_case(p_identifier[i])) {
if (!is_digit(p_identifier[i])) {
// These conditions only apply when the separator is not a digit.
if (i - current_part_start == 1) {
// Upper character was only the beginning of a word.
prev_was_upper = false;
continue;
}
if (i != p_identifier.length()) {
// If this is not the last character, the last uppercase
// character is the start of the next word.
i--;
}
}
if (i - current_part_start > 0) {
parts.append(p_identifier.substr(current_part_start, i - current_part_start));
current_part_start = i;
prev_was_upper = false;
}
}
} else {
if (is_digit(p_identifier[i]) || is_ascii_upper_case(p_identifier[i])) {
parts.append(p_identifier.substr(current_part_start, i - current_part_start));
current_part_start = i;
prev_was_upper = true;
}
}
}
// Add the rest of the identifier as the last part.
if (current_part_start != p_identifier.length()) {
parts.append(p_identifier.substr(current_part_start));
}
return parts;
}
String pascal_to_pascal_case(const String &p_identifier) {
if (p_identifier.length() == 0) {
return p_identifier;
}
if (p_identifier.length() <= 2) {
return p_identifier.to_upper();
}
if (pascal_case_name_overrides.has(p_identifier)) {
// Use hardcoded value for the identifier.
return pascal_case_name_overrides.get(p_identifier);
}
Vector<String> parts = _split_pascal_case(p_identifier);
String ret;
for (String &part : parts) {
String part_override = _get_pascal_case_part_override(part);
if (!part_override.is_empty()) {
// Use hardcoded value for part.
ret += part_override;
continue;
}
if (part.length() <= 2 && part.to_upper() == part) {
// Acronym of length 1 or 2.
for (int j = 0; j < part.length(); j++) {
part[j] = _find_upper(part[j]);
}
ret += part;
continue;
}
part[0] = _find_upper(part[0]);
for (int i = 1; i < part.length(); i++) {
if (is_digit(part[i - 1])) {
// Use uppercase after digits.
part[i] = _find_upper(part[i]);
continue;
}
part[i] = _find_lower(part[i]);
}
ret += part;
}
return ret;
}
String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper) {
String ret;
Vector<String> parts = p_identifier.split("_", true);
for (int i = 0; i < parts.size(); i++) {
String part = parts[i];
String part_override = _get_pascal_case_part_override(part, p_input_is_upper);
if (!part_override.is_empty()) {
// Use hardcoded value for part.
ret += part_override;
continue;
}
if (!part.is_empty()) {
part[0] = _find_upper(part[0]);
for (int j = 1; j < part.length(); j++) {
if (is_digit(part[j - 1])) {
// Use uppercase after digits.
part[j] = _find_upper(part[j]);
continue;
}
if (p_input_is_upper) {
part[j] = _find_lower(part[j]);
}
}
ret += part;
} else {
if (i == 0 || i == (parts.size() - 1)) {
// Preserve underscores at the beginning and end
ret += "_";
} else {
// Preserve contiguous underscores
if (parts[i - 1].length()) {
ret += "__";
} else {
ret += "_";
}
}
}
}
return ret;
}
String snake_to_camel_case(const String &p_identifier, bool p_input_is_upper) {
String ret;
Vector<String> parts = p_identifier.split("_", true);
for (int i = 0; i < parts.size(); i++) {
String part = parts[i];
String part_override = _get_pascal_case_part_override(part, p_input_is_upper);
if (!part_override.is_empty()) {
// Use hardcoded value for part.
if (i == 0) {
part_override[0] = _find_lower(part_override[0]);
}
ret += part_override;
continue;
}
if (!part.is_empty()) {
if (i == 0) {
part[0] = _find_lower(part[0]);
} else {
part[0] = _find_upper(part[0]);
}
for (int j = 1; j < part.length(); j++) {
if (is_digit(part[j - 1])) {
// Use uppercase after digits.
part[j] = _find_upper(part[j]);
continue;
}
if (p_input_is_upper) {
part[j] = _find_lower(part[j]);
}
}
ret += part;
} else {
if (i == 0 || i == (parts.size() - 1)) {
// Preserve underscores at the beginning and end
ret += "_";
} else {
// Preserve contiguous underscores
if (parts[i - 1].length()) {
ret += "__";
} else {
ret += "_";
}
}
}
}
return ret;
}