@ -178,6 +178,99 @@ std::string UnderscoresToPascalCase(const std::string& input) {
return UnderscoresToCamelCase ( input , true ) ;
}
// Convert a string which is expected to be SHOUTY_CASE (but may not be *precisely* shouty)
// into a PascalCase string. Precise rules implemented:
// Previous input character Current character Case
// Any Non-alphanumeric Skipped
// None - first char of input Alphanumeric Upper
// Non-letter (e.g. _ or 1) Alphanumeric Upper
// Numeric Alphanumeric Upper
// Lower letter Alphanumeric Same as current
// Upper letter Alphanumeric Lower
std : : string ShoutyToPascalCase ( const std : : string & input ) {
string result ;
// Simple way of implementing "always start with upper"
char previous = ' _ ' ;
for ( int i = 0 ; i < input . size ( ) ; i + + ) {
char current = input [ i ] ;
if ( ! ascii_isalnum ( current ) ) {
previous = current ;
continue ;
}
if ( ! ascii_isalnum ( previous ) ) {
result + = ascii_toupper ( current ) ;
} else if ( ascii_isdigit ( previous ) ) {
result + = ascii_toupper ( current ) ;
} else if ( ascii_islower ( previous ) ) {
result + = current ;
} else {
result + = ascii_tolower ( current ) ;
}
previous = current ;
}
return result ;
}
// Attempt to remove a prefix from a value, ignoring casing and skipping underscores.
// (foo, foo_bar) => bar - underscore after prefix is skipped
// (FOO, foo_bar) => bar - casing is ignored
// (foo_bar, foobarbaz) => baz - underscore in prefix is ignored
// (foobar, foo_barbaz) => baz - underscore in value is ignored
// (foo, bar) => bar - prefix isn't matched; return original value
std : : string TryRemovePrefix ( const std : : string & prefix , const std : : string & value ) {
// First normalize to a lower-case no-underscores prefix to match against
std : : string prefix_to_match = " " ;
for ( size_t i = 0 ; i < prefix . size ( ) ; i + + ) {
if ( prefix [ i ] ! = ' _ ' ) {
prefix_to_match + = ascii_tolower ( prefix [ i ] ) ;
}
}
// This keeps track of how much of value we've consumed
size_t prefix_index , value_index ;
for ( prefix_index = 0 , value_index = 0 ;
prefix_index < prefix_to_match . size ( ) & & value_index < value . size ( ) ;
value_index + + ) {
// Skip over underscores in the value
if ( value [ value_index ] = = ' _ ' ) {
continue ;
}
if ( ascii_tolower ( value [ value_index ] ) ! = prefix_to_match [ prefix_index + + ] ) {
// Failed to match the prefix - bail out early.
return value ;
}
}
// If we didn't finish looking through the prefix, we can't strip it.
if ( prefix_index < prefix_to_match . size ( ) ) {
return value ;
}
// Step over any underscores after the prefix
while ( value_index < value . size ( ) & & value [ value_index ] = = ' _ ' ) {
value_index + + ;
}
// If there's nothing left (e.g. it was a prefix with only underscores afterwards), don't strip.
if ( value_index = = value . size ( ) ) {
return value ;
}
return value . substr ( value_index ) ;
}
std : : string GetEnumValueName ( const std : : string & enum_name , const std : : string & enum_value_name ) {
std : : string stripped = TryRemovePrefix ( enum_name , enum_value_name ) ;
std : : string result = ShoutyToPascalCase ( stripped ) ;
// Just in case we have an enum name of FOO and a value of FOO_2... make sure the returned
// string is a valid identifier.
if ( ascii_isdigit ( result [ 0 ] ) ) {
result = " _ " + result ;
}
return result ;
}
std : : string ToCSharpName ( const std : : string & name , const FileDescriptor * file ) {
std : : string result = GetFileNamespace ( file ) ;
if ( result ! = " " ) {