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  }