mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			259 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2019 The Xorm Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package core
 | 
						|
 | 
						|
import (
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
// IMapper represents a name convertation between struct's fields name and table's column name
 | 
						|
type IMapper interface {
 | 
						|
	Obj2Table(string) string
 | 
						|
	Table2Obj(string) string
 | 
						|
}
 | 
						|
 | 
						|
type CacheMapper struct {
 | 
						|
	oriMapper      IMapper
 | 
						|
	obj2tableCache map[string]string
 | 
						|
	obj2tableMutex sync.RWMutex
 | 
						|
	table2objCache map[string]string
 | 
						|
	table2objMutex sync.RWMutex
 | 
						|
}
 | 
						|
 | 
						|
func NewCacheMapper(mapper IMapper) *CacheMapper {
 | 
						|
	return &CacheMapper{oriMapper: mapper, obj2tableCache: make(map[string]string),
 | 
						|
		table2objCache: make(map[string]string),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (m *CacheMapper) Obj2Table(o string) string {
 | 
						|
	m.obj2tableMutex.RLock()
 | 
						|
	t, ok := m.obj2tableCache[o]
 | 
						|
	m.obj2tableMutex.RUnlock()
 | 
						|
	if ok {
 | 
						|
		return t
 | 
						|
	}
 | 
						|
 | 
						|
	t = m.oriMapper.Obj2Table(o)
 | 
						|
	m.obj2tableMutex.Lock()
 | 
						|
	m.obj2tableCache[o] = t
 | 
						|
	m.obj2tableMutex.Unlock()
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
func (m *CacheMapper) Table2Obj(t string) string {
 | 
						|
	m.table2objMutex.RLock()
 | 
						|
	o, ok := m.table2objCache[t]
 | 
						|
	m.table2objMutex.RUnlock()
 | 
						|
	if ok {
 | 
						|
		return o
 | 
						|
	}
 | 
						|
 | 
						|
	o = m.oriMapper.Table2Obj(t)
 | 
						|
	m.table2objMutex.Lock()
 | 
						|
	m.table2objCache[t] = o
 | 
						|
	m.table2objMutex.Unlock()
 | 
						|
	return o
 | 
						|
}
 | 
						|
 | 
						|
// SameMapper implements IMapper and provides same name between struct and
 | 
						|
// database table
 | 
						|
type SameMapper struct {
 | 
						|
}
 | 
						|
 | 
						|
func (m SameMapper) Obj2Table(o string) string {
 | 
						|
	return o
 | 
						|
}
 | 
						|
 | 
						|
func (m SameMapper) Table2Obj(t string) string {
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
// SnakeMapper implements IMapper and provides name transaltion between
 | 
						|
// struct and database table
 | 
						|
type SnakeMapper struct {
 | 
						|
}
 | 
						|
 | 
						|
func snakeCasedName(name string) string {
 | 
						|
	newstr := make([]rune, 0)
 | 
						|
	for idx, chr := range name {
 | 
						|
		if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
 | 
						|
			if idx > 0 {
 | 
						|
				newstr = append(newstr, '_')
 | 
						|
			}
 | 
						|
			chr -= ('A' - 'a')
 | 
						|
		}
 | 
						|
		newstr = append(newstr, chr)
 | 
						|
	}
 | 
						|
 | 
						|
	return string(newstr)
 | 
						|
}
 | 
						|
 | 
						|
func (mapper SnakeMapper) Obj2Table(name string) string {
 | 
						|
	return snakeCasedName(name)
 | 
						|
}
 | 
						|
 | 
						|
func titleCasedName(name string) string {
 | 
						|
	newstr := make([]rune, 0)
 | 
						|
	upNextChar := true
 | 
						|
 | 
						|
	name = strings.ToLower(name)
 | 
						|
 | 
						|
	for _, chr := range name {
 | 
						|
		switch {
 | 
						|
		case upNextChar:
 | 
						|
			upNextChar = false
 | 
						|
			if 'a' <= chr && chr <= 'z' {
 | 
						|
				chr -= ('a' - 'A')
 | 
						|
			}
 | 
						|
		case chr == '_':
 | 
						|
			upNextChar = true
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		newstr = append(newstr, chr)
 | 
						|
	}
 | 
						|
 | 
						|
	return string(newstr)
 | 
						|
}
 | 
						|
 | 
						|
func (mapper SnakeMapper) Table2Obj(name string) string {
 | 
						|
	return titleCasedName(name)
 | 
						|
}
 | 
						|
 | 
						|
// GonicMapper implements IMapper. It will consider initialisms when mapping names.
 | 
						|
// E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid
 | 
						|
type GonicMapper map[string]bool
 | 
						|
 | 
						|
func isASCIIUpper(r rune) bool {
 | 
						|
	return 'A' <= r && r <= 'Z'
 | 
						|
}
 | 
						|
 | 
						|
func toASCIIUpper(r rune) rune {
 | 
						|
	if 'a' <= r && r <= 'z' {
 | 
						|
		r -= ('a' - 'A')
 | 
						|
	}
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func gonicCasedName(name string) string {
 | 
						|
	newstr := make([]rune, 0, len(name)+3)
 | 
						|
	for idx, chr := range name {
 | 
						|
		if isASCIIUpper(chr) && idx > 0 {
 | 
						|
			if !isASCIIUpper(newstr[len(newstr)-1]) {
 | 
						|
				newstr = append(newstr, '_')
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if !isASCIIUpper(chr) && idx > 1 {
 | 
						|
			l := len(newstr)
 | 
						|
			if isASCIIUpper(newstr[l-1]) && isASCIIUpper(newstr[l-2]) {
 | 
						|
				newstr = append(newstr, newstr[l-1])
 | 
						|
				newstr[l-1] = '_'
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		newstr = append(newstr, chr)
 | 
						|
	}
 | 
						|
	return strings.ToLower(string(newstr))
 | 
						|
}
 | 
						|
 | 
						|
func (mapper GonicMapper) Obj2Table(name string) string {
 | 
						|
	return gonicCasedName(name)
 | 
						|
}
 | 
						|
 | 
						|
func (mapper GonicMapper) Table2Obj(name string) string {
 | 
						|
	newstr := make([]rune, 0)
 | 
						|
 | 
						|
	name = strings.ToLower(name)
 | 
						|
	parts := strings.Split(name, "_")
 | 
						|
 | 
						|
	for _, p := range parts {
 | 
						|
		_, isInitialism := mapper[strings.ToUpper(p)]
 | 
						|
		for i, r := range p {
 | 
						|
			if i == 0 || isInitialism {
 | 
						|
				r = toASCIIUpper(r)
 | 
						|
			}
 | 
						|
			newstr = append(newstr, r)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return string(newstr)
 | 
						|
}
 | 
						|
 | 
						|
// LintGonicMapper is A GonicMapper that contains a list of common initialisms taken from golang/lint
 | 
						|
var LintGonicMapper = GonicMapper{
 | 
						|
	"API":   true,
 | 
						|
	"ASCII": true,
 | 
						|
	"CPU":   true,
 | 
						|
	"CSS":   true,
 | 
						|
	"DNS":   true,
 | 
						|
	"EOF":   true,
 | 
						|
	"GUID":  true,
 | 
						|
	"HTML":  true,
 | 
						|
	"HTTP":  true,
 | 
						|
	"HTTPS": true,
 | 
						|
	"ID":    true,
 | 
						|
	"IP":    true,
 | 
						|
	"JSON":  true,
 | 
						|
	"LHS":   true,
 | 
						|
	"QPS":   true,
 | 
						|
	"RAM":   true,
 | 
						|
	"RHS":   true,
 | 
						|
	"RPC":   true,
 | 
						|
	"SLA":   true,
 | 
						|
	"SMTP":  true,
 | 
						|
	"SSH":   true,
 | 
						|
	"TLS":   true,
 | 
						|
	"TTL":   true,
 | 
						|
	"UI":    true,
 | 
						|
	"UID":   true,
 | 
						|
	"UUID":  true,
 | 
						|
	"URI":   true,
 | 
						|
	"URL":   true,
 | 
						|
	"UTF8":  true,
 | 
						|
	"VM":    true,
 | 
						|
	"XML":   true,
 | 
						|
	"XSRF":  true,
 | 
						|
	"XSS":   true,
 | 
						|
}
 | 
						|
 | 
						|
// PrefixMapper provides prefix table name support
 | 
						|
type PrefixMapper struct {
 | 
						|
	Mapper IMapper
 | 
						|
	Prefix string
 | 
						|
}
 | 
						|
 | 
						|
func (mapper PrefixMapper) Obj2Table(name string) string {
 | 
						|
	return mapper.Prefix + mapper.Mapper.Obj2Table(name)
 | 
						|
}
 | 
						|
 | 
						|
func (mapper PrefixMapper) Table2Obj(name string) string {
 | 
						|
	return mapper.Mapper.Table2Obj(name[len(mapper.Prefix):])
 | 
						|
}
 | 
						|
 | 
						|
func NewPrefixMapper(mapper IMapper, prefix string) PrefixMapper {
 | 
						|
	return PrefixMapper{mapper, prefix}
 | 
						|
}
 | 
						|
 | 
						|
// SuffixMapper provides suffix table name support
 | 
						|
type SuffixMapper struct {
 | 
						|
	Mapper IMapper
 | 
						|
	Suffix string
 | 
						|
}
 | 
						|
 | 
						|
func (mapper SuffixMapper) Obj2Table(name string) string {
 | 
						|
	return mapper.Mapper.Obj2Table(name) + mapper.Suffix
 | 
						|
}
 | 
						|
 | 
						|
func (mapper SuffixMapper) Table2Obj(name string) string {
 | 
						|
	return mapper.Mapper.Table2Obj(name[:len(name)-len(mapper.Suffix)])
 | 
						|
}
 | 
						|
 | 
						|
func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper {
 | 
						|
	return SuffixMapper{mapper, suffix}
 | 
						|
}
 |