github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/go-xorm/core/mapper.go (about) 1 package core 2 3 import ( 4 "strings" 5 "sync" 6 ) 7 8 // name translation between struct, fields names and table, column names 9 type IMapper interface { 10 Obj2Table(string) string 11 Table2Obj(string) string 12 } 13 14 type CacheMapper struct { 15 oriMapper IMapper 16 obj2tableCache map[string]string 17 obj2tableMutex sync.RWMutex 18 table2objCache map[string]string 19 table2objMutex sync.RWMutex 20 } 21 22 func NewCacheMapper(mapper IMapper) *CacheMapper { 23 return &CacheMapper{oriMapper: mapper, obj2tableCache: make(map[string]string), 24 table2objCache: make(map[string]string), 25 } 26 } 27 28 func (m *CacheMapper) Obj2Table(o string) string { 29 m.obj2tableMutex.RLock() 30 t, ok := m.obj2tableCache[o] 31 m.obj2tableMutex.RUnlock() 32 if ok { 33 return t 34 } 35 36 t = m.oriMapper.Obj2Table(o) 37 m.obj2tableMutex.Lock() 38 m.obj2tableCache[o] = t 39 m.obj2tableMutex.Unlock() 40 return t 41 } 42 43 func (m *CacheMapper) Table2Obj(t string) string { 44 m.table2objMutex.RLock() 45 o, ok := m.table2objCache[t] 46 m.table2objMutex.RUnlock() 47 if ok { 48 return o 49 } 50 51 o = m.oriMapper.Table2Obj(t) 52 m.table2objMutex.Lock() 53 m.table2objCache[t] = o 54 m.table2objMutex.Unlock() 55 return o 56 } 57 58 // SameMapper implements IMapper and provides same name between struct and 59 // database table 60 type SameMapper struct { 61 } 62 63 func (m SameMapper) Obj2Table(o string) string { 64 return o 65 } 66 67 func (m SameMapper) Table2Obj(t string) string { 68 return t 69 } 70 71 // SnakeMapper implements IMapper and provides name transaltion between 72 // struct and database table 73 type SnakeMapper struct { 74 } 75 76 func snakeCasedName(name string) string { 77 newstr := make([]rune, 0) 78 for idx, chr := range name { 79 if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { 80 if idx > 0 { 81 newstr = append(newstr, '_') 82 } 83 chr -= ('A' - 'a') 84 } 85 newstr = append(newstr, chr) 86 } 87 88 return string(newstr) 89 } 90 91 func (mapper SnakeMapper) Obj2Table(name string) string { 92 return snakeCasedName(name) 93 } 94 95 func titleCasedName(name string) string { 96 newstr := make([]rune, 0) 97 upNextChar := true 98 99 name = strings.ToLower(name) 100 101 for _, chr := range name { 102 switch { 103 case upNextChar: 104 upNextChar = false 105 if 'a' <= chr && chr <= 'z' { 106 chr -= ('a' - 'A') 107 } 108 case chr == '_': 109 upNextChar = true 110 continue 111 } 112 113 newstr = append(newstr, chr) 114 } 115 116 return string(newstr) 117 } 118 119 func (mapper SnakeMapper) Table2Obj(name string) string { 120 return titleCasedName(name) 121 } 122 123 // GonicMapper implements IMapper. It will consider initialisms when mapping names. 124 // E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid 125 type GonicMapper map[string]bool 126 127 func isASCIIUpper(r rune) bool { 128 return 'A' <= r && r <= 'Z' 129 } 130 131 func toASCIIUpper(r rune) rune { 132 if 'a' <= r && r <= 'z' { 133 r -= ('a' - 'A') 134 } 135 return r 136 } 137 138 func gonicCasedName(name string) string { 139 newstr := make([]rune, 0, len(name)+3) 140 for idx, chr := range name { 141 if isASCIIUpper(chr) && idx > 0 { 142 if !isASCIIUpper(newstr[len(newstr)-1]) { 143 newstr = append(newstr, '_') 144 } 145 } 146 147 if !isASCIIUpper(chr) && idx > 1 { 148 l := len(newstr) 149 if isASCIIUpper(newstr[l-1]) && isASCIIUpper(newstr[l-2]) { 150 newstr = append(newstr, newstr[l-1]) 151 newstr[l-1] = '_' 152 } 153 } 154 155 newstr = append(newstr, chr) 156 } 157 return strings.ToLower(string(newstr)) 158 } 159 160 func (mapper GonicMapper) Obj2Table(name string) string { 161 return gonicCasedName(name) 162 } 163 164 func (mapper GonicMapper) Table2Obj(name string) string { 165 newstr := make([]rune, 0) 166 167 name = strings.ToLower(name) 168 parts := strings.Split(name, "_") 169 170 for _, p := range parts { 171 _, isInitialism := mapper[strings.ToUpper(p)] 172 for i, r := range p { 173 if i == 0 || isInitialism { 174 r = toASCIIUpper(r) 175 } 176 newstr = append(newstr, r) 177 } 178 } 179 180 return string(newstr) 181 } 182 183 // A GonicMapper that contains a list of common initialisms taken from golang/lint 184 var LintGonicMapper = GonicMapper{ 185 "API": true, 186 "ASCII": true, 187 "CPU": true, 188 "CSS": true, 189 "DNS": true, 190 "EOF": true, 191 "GUID": true, 192 "HTML": true, 193 "HTTP": true, 194 "HTTPS": true, 195 "ID": true, 196 "IP": true, 197 "JSON": true, 198 "LHS": true, 199 "QPS": true, 200 "RAM": true, 201 "RHS": true, 202 "RPC": true, 203 "SLA": true, 204 "SMTP": true, 205 "SSH": true, 206 "TLS": true, 207 "TTL": true, 208 "UI": true, 209 "UID": true, 210 "UUID": true, 211 "URI": true, 212 "URL": true, 213 "UTF8": true, 214 "VM": true, 215 "XML": true, 216 "XSRF": true, 217 "XSS": true, 218 } 219 220 // provide prefix table name support 221 type PrefixMapper struct { 222 Mapper IMapper 223 Prefix string 224 } 225 226 func (mapper PrefixMapper) Obj2Table(name string) string { 227 return mapper.Prefix + mapper.Mapper.Obj2Table(name) 228 } 229 230 func (mapper PrefixMapper) Table2Obj(name string) string { 231 return mapper.Mapper.Table2Obj(name[len(mapper.Prefix):]) 232 } 233 234 func NewPrefixMapper(mapper IMapper, prefix string) PrefixMapper { 235 return PrefixMapper{mapper, prefix} 236 } 237 238 // provide suffix table name support 239 type SuffixMapper struct { 240 Mapper IMapper 241 Suffix string 242 } 243 244 func (mapper SuffixMapper) Obj2Table(name string) string { 245 return mapper.Mapper.Obj2Table(name) + mapper.Suffix 246 } 247 248 func (mapper SuffixMapper) Table2Obj(name string) string { 249 return mapper.Mapper.Table2Obj(name[:len(name)-len(mapper.Suffix)]) 250 } 251 252 func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper { 253 return SuffixMapper{mapper, suffix} 254 }