Change the camelcase algorithm to be consistent with earlier
implementations (copy the google-internal version).
The difference is that lower-case letters are always capitalized
if the preceding character is not an upper-case letter.
R=rsc
CC=dsymonds1
http://codereview.appspot.com/1599045
diff --git a/compiler/generator/generator.go b/compiler/generator/generator.go
index f6fb238..c548332 100644
--- a/compiler/generator/generator.go
+++ b/compiler/generator/generator.go
@@ -1042,30 +1042,65 @@
// And now lots of helper functions.
-// CamelCase returns the CamelCased name. Given foo_bar_Baz, the result is FooBar_Baz.
-func CamelCase(name string) string {
- elems := strings.Split(name, "_", 0)
- for i, e := range elems {
- if e == "" {
- elems[i] = "_"
+// Is c an ASCII lower-case letter?
+func isASCIILower(c byte) bool {
+ return 'a' <= c && c <= 'z'
+}
+
+// Is c an ASCII digit?
+func isASCIIDigit(c byte) bool {
+ return '0' <= c && c <= '9'
+}
+
+// CamelCase returns the CamelCased name.
+// If there is an interior underscore followed by a lower case letter,
+// drop the underscore and convert the letter to upper case.
+// There is a remote possibility of this rewrite causing a name collision,
+// but it's so remote we're prepared to pretend it's nonexistent - since the
+// C++ generator lowercases names, it's extremely unlikely to have two fields
+// with different capitalizations.
+// In short, _my_field_name_2 becomes XMyFieldName2.
+func CamelCase(s string) string {
+ if s == "" {
+ return ""
+ }
+ t := make([]byte, 0, 32)
+ oneC := make([]byte, 1)
+ i := 0
+ if s[0] == '_' {
+ // Need a capital letter; drop the '_'.
+ oneC[0] = 'X'
+ t = bytes.Add(t, oneC)
+ i++
+ }
+ // Invariant: if the next letter is lower case, it must be converted
+ // to upper case.
+ // That is, we process a word at a time, where words are marked by _ or
+ // upper case letter. Digits are treated as words.
+ for ; i < len(s); i++ {
+ c := s[i]
+ oneC[0] = c
+ if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
+ continue // Skip the underscore in s.
+ }
+ if isASCIIDigit(c) {
+ t = bytes.Add(t, oneC)
continue
}
- runes := []int(e)
- if unicode.IsLower(runes[0]) {
- runes[0] = unicode.ToUpper(runes[0])
- elems[i] = string(runes)
- } else {
- if i > 0 {
- elems[i] = "_" + e
- }
+ // Assume we have a letter now - if not, it's a bogus identifier.
+ // The next word is a sequence of characters that must start upper case.
+ if isASCIILower(c) {
+ oneC[0] ^= ' ' // Make it a capital letter.
+ }
+ t = bytes.Add(t, oneC) // Guaranteed not lower case.
+ // Accept lower case sequence that follows.
+ for i+1 < len(s) && isASCIILower(s[i+1]) {
+ i++
+ oneC[0] = s[i]
+ t = bytes.Add(t, oneC)
}
}
- s := strings.Join(elems, "")
- // Name must not begin with an underscore.
- if len(s) > 0 && s[0] == '_' {
- s = "X" + s[1:]
- }
- return s
+ return string(t)
}
// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to