1package kong
2
3// NOTE: This code is from https://github.com/fatih/camelcase. MIT license.
4
5import (
6	"unicode"
7	"unicode/utf8"
8)
9
10// Split splits the camelcase word and returns a list of words. It also
11// supports digits. Both lower camel case and upper camel case are supported.
12// For more info please check: http://en.wikipedia.org/wiki/CamelCase
13//
14// Examples
15//
16//   "" =>                     [""]
17//   "lowercase" =>            ["lowercase"]
18//   "Class" =>                ["Class"]
19//   "MyClass" =>              ["My", "Class"]
20//   "MyC" =>                  ["My", "C"]
21//   "HTML" =>                 ["HTML"]
22//   "PDFLoader" =>            ["PDF", "Loader"]
23//   "AString" =>              ["A", "String"]
24//   "SimpleXMLParser" =>      ["Simple", "XML", "Parser"]
25//   "vimRPCPlugin" =>         ["vim", "RPC", "Plugin"]
26//   "GL11Version" =>          ["GL", "11", "Version"]
27//   "99Bottles" =>            ["99", "Bottles"]
28//   "May5" =>                 ["May", "5"]
29//   "BFG9000" =>              ["BFG", "9000"]
30//   "BöseÜberraschung" =>     ["Böse", "Überraschung"]
31//   "Two  spaces" =>          ["Two", "  ", "spaces"]
32//   "BadUTF8\xe2\xe2\xa1" =>  ["BadUTF8\xe2\xe2\xa1"]
33//
34// Splitting rules
35//
36//  1) If string is not valid UTF-8, return it without splitting as
37//     single item array.
38//  2) Assign all unicode characters into one of 4 sets: lower case
39//     letters, upper case letters, numbers, and all other characters.
40//  3) Iterate through characters of string, introducing splits
41//     between adjacent characters that belong to different sets.
42//  4) Iterate through array of split strings, and if a given string
43//     is upper case:
44//       if subsequent string is lower case:
45//         move last character of upper case string to beginning of
46//         lower case string
47func camelCase(src string) (entries []string) {
48	// don't split invalid utf8
49	if !utf8.ValidString(src) {
50		return []string{src}
51	}
52	entries = []string{}
53	var runes [][]rune
54	lastClass := 0
55	// split into fields based on class of unicode character
56	for _, r := range src {
57		var class int
58		switch {
59		case unicode.IsLower(r):
60			class = 1
61		case unicode.IsUpper(r):
62			class = 2
63		case unicode.IsDigit(r):
64			class = 3
65		default:
66			class = 4
67		}
68		if class == lastClass {
69			runes[len(runes)-1] = append(runes[len(runes)-1], r)
70		} else {
71			runes = append(runes, []rune{r})
72		}
73		lastClass = class
74	}
75	// handle upper case -> lower case sequences, e.g.
76	// "PDFL", "oader" -> "PDF", "Loader"
77	for i := 0; i < len(runes)-1; i++ {
78		if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
79			runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
80			runes[i] = runes[i][:len(runes[i])-1]
81		}
82	}
83	// construct []string from results
84	for _, s := range runes {
85		if len(s) > 0 {
86			entries = append(entries, string(s))
87		}
88	}
89	return entries
90}
91