非test文件 恢复 原样

This commit is contained in:
piaohao 2019-06-13 12:41:20 +08:00
parent d305d25935
commit 80cf3e833b
3 changed files with 708 additions and 715 deletions

View File

@ -17,17 +17,17 @@ import (
) )
const ( const (
// Separator char for hierarchical data access. // Separator char for hierarchical data access.
gDEFAULT_SPLIT_CHAR = '.' gDEFAULT_SPLIT_CHAR = '.'
) )
// The customized JSON struct. // The customized JSON struct.
type Json struct { type Json struct {
mu *rwmutex.RWMutex mu *rwmutex.RWMutex
p *interface{} // Pointer for hierarchical data access, it's the root of data in default. p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
c byte // Char separator('.' in default). c byte // Char separator('.' in default).
vc bool // Violence Check(false in default), which is used to access data vc bool // Violence Check(false in default), which is used to access data
// when the hierarchical data key contains separator char. // when the hierarchical data key contains separator char.
} }
// Set <value> by <pattern>. // Set <value> by <pattern>.
@ -35,306 +35,300 @@ type Json struct {
// 1. If value is nil and removed is true, means deleting this value; // 1. If value is nil and removed is true, means deleting this value;
// 2. It's quite complicated in hierarchical data search, node creating and data assignment; // 2. It's quite complicated in hierarchical data search, node creating and data assignment;
func (j *Json) setValue(pattern string, value interface{}, removed bool) error { func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
array := strings.Split(pattern, string(j.c)) array := strings.Split(pattern, string(j.c))
length := len(array) length := len(array)
value = j.convertValue(value) value = j.convertValue(value)
// 初始化判断 // 初始化判断
if *j.p == nil { if *j.p == nil {
if gstr.IsNumeric(array[0]) { if gstr.IsNumeric(array[0]) {
*j.p = make([]interface{}, 0) *j.p = make([]interface{}, 0)
} else { } else {
*j.p = make(map[string]interface{}) *j.p = make(map[string]interface{})
} }
} }
var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级) var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级)
var pointer *interface{} = j.p // 当前操作层级项 var pointer *interface{} = j.p // 当前操作层级项
j.mu.Lock() j.mu.Lock()
defer j.mu.Unlock() defer j.mu.Unlock()
for i := 0; i < length; i++ { for i:= 0; i < length; i++ {
switch (*pointer).(type) { switch (*pointer).(type) {
case map[string]interface{}: case map[string]interface{}:
if i == length-1 { if i == length - 1 {
if removed && value == nil { if removed && value == nil {
// 删除map元素 // 删除map元素
delete((*pointer).(map[string]interface{}), array[i]) delete((*pointer).(map[string]interface{}), array[i])
} else { } else {
(*pointer).(map[string]interface{})[array[i]] = value (*pointer).(map[string]interface{})[array[i]] = value
} }
} else { } else {
// 当键名不存在的情况这里会进行处理 // 当键名不存在的情况这里会进行处理
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok { if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
if removed && value == nil { if removed && value == nil {
goto done goto done
} }
// 创建新节点 // 创建新节点
if gstr.IsNumeric(array[i+1]) { if gstr.IsNumeric(array[i + 1]) {
// 创建array节点 // 创建array节点
n, _ := strconv.Atoi(array[i+1]) n, _ := strconv.Atoi(array[i + 1])
var v interface{} = make([]interface{}, n+1) var v interface{} = make([]interface{}, n + 1)
pparent = j.setPointerWithValue(pointer, array[i], v) pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v pointer = &v
} else { } else {
// 创建map节点 // 创建map节点
var v interface{} = make(map[string]interface{}) var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v) pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v pointer = &v
} }
} else { } else {
pparent = pointer pparent = pointer
pointer = &v pointer = &v
} }
} }
case []interface{}: case []interface{}:
// 键名与当前指针类型不符合,需要执行**覆盖操作** // 键名与当前指针类型不符合,需要执行**覆盖操作**
if !gstr.IsNumeric(array[i]) { if !gstr.IsNumeric(array[i]) {
if i == length-1 { if i == length - 1 {
*pointer = map[string]interface{}{array[i]: value} *pointer = map[string]interface{}{ array[i] : value }
} else { } else {
var v interface{} = make(map[string]interface{}) var v interface{} = make(map[string]interface{})
*pointer = v *pointer = v
pparent = pointer pparent = pointer
pointer = &v pointer = &v
} }
continue continue
} }
valn, err := strconv.Atoi(array[i]) valn, err := strconv.Atoi(array[i])
if err != nil { if err != nil {
return err return err
} }
// 叶子节点 // 叶子节点
if i == length-1 { if i == length - 1 {
if len((*pointer).([]interface{})) > valn { if len((*pointer).([]interface{})) > valn {
if removed && value == nil { if removed && value == nil {
// 删除数据元素 // 删除数据元素
j.setPointerWithValue(pparent, array[i-1], append((*pointer).([]interface{})[:valn], (*pointer).([]interface{})[valn+1:]...)) j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
} else { } else {
(*pointer).([]interface{})[valn] = value (*pointer).([]interface{})[valn] = value
} }
} else { } else {
if removed && value == nil { if removed && value == nil {
goto done goto done
} }
if pparent == nil { if pparent == nil {
// 表示根节点 // 表示根节点
j.setPointerWithValue(pointer, array[i], value) j.setPointerWithValue(pointer, array[i], value)
} else { } else {
// 非根节点 // 非根节点
s := make([]interface{}, valn+1) s := make([]interface{}, valn + 1)
copy(s, (*pointer).([]interface{})) copy(s, (*pointer).([]interface{}))
s[valn] = value s[valn] = value
j.setPointerWithValue(pparent, array[i-1], s) j.setPointerWithValue(pparent, array[i - 1], s)
} }
} }
} else { } else {
if gstr.IsNumeric(array[i+1]) { if gstr.IsNumeric(array[i + 1]) {
n, _ := strconv.Atoi(array[i+1]) n, _ := strconv.Atoi(array[i + 1])
if len((*pointer).([]interface{})) > valn { if len((*pointer).([]interface{})) > valn {
(*pointer).([]interface{})[valn] = make([]interface{}, n+1) (*pointer).([]interface{})[valn] = make([]interface{}, n + 1)
pparent = pointer pparent = pointer
pointer = &(*pointer).([]interface{})[valn] pointer = &(*pointer).([]interface{})[valn]
} else { } else {
if removed && value == nil { if removed && value == nil {
goto done goto done
} }
var v interface{} = make([]interface{}, n+1) var v interface{} = make([]interface{}, n + 1)
pparent = j.setPointerWithValue(pointer, array[i], v) pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v pointer = &v
} }
} else { } else {
var v interface{} = make(map[string]interface{}) var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v) pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v pointer = &v
} }
} }
// 如果当前指针指向的变量不是引用类型的, // 如果当前指针指向的变量不是引用类型的,
// 那么修改变量必须通过父级进行修改,即 pparent // 那么修改变量必须通过父级进行修改,即 pparent
default: default:
if removed && value == nil { if removed && value == nil {
goto done goto done
} }
if gstr.IsNumeric(array[i]) { if gstr.IsNumeric(array[i]) {
n, _ := strconv.Atoi(array[i]) n, _ := strconv.Atoi(array[i])
s := make([]interface{}, n+1) s := make([]interface{}, n + 1)
if i == length-1 { if i == length - 1 {
s[n] = value s[n] = value
} }
if pparent != nil { if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i-1], s) pparent = j.setPointerWithValue(pparent, array[i - 1], s)
} else { } else {
*pointer = s *pointer = s
pparent = pointer pparent = pointer
} }
} else { } else {
var v interface{} = make(map[string]interface{}) var v interface{} = make(map[string]interface{})
if i == length-1 { if i == length - 1 {
v = map[string]interface{}{ v = map[string]interface{}{
array[i]: value, array[i] : value,
} }
} }
if pparent != nil { if pparent != nil {
pparent = j.setPointerWithValue(pparent, array[i-1], v) pparent = j.setPointerWithValue(pparent, array[i - 1], v)
} else { } else {
*pointer = v *pointer = v
pparent = pointer pparent = pointer
} }
pointer = &v pointer = &v
} }
} }
} }
done: done:
return nil return nil
} }
// Convert <value> to map[string]interface{} or []interface{}, // Convert <value> to map[string]interface{} or []interface{},
// which can be supported for hierarchical data access. // which can be supported for hierarchical data access.
func (j *Json) convertValue(value interface{}) interface{} { func (j *Json) convertValue(value interface{}) interface{} {
switch value.(type) { switch value.(type) {
case map[string]interface{}: case map[string]interface{}:
return value return value
case []interface{}: case []interface{}:
return value return value
default: default:
rv := reflect.ValueOf(value) rv := reflect.ValueOf(value)
kind := rv.Kind() kind := rv.Kind()
if kind == reflect.Ptr { if kind == reflect.Ptr {
rv = rv.Elem() rv = rv.Elem()
kind = rv.Kind() kind = rv.Kind()
} }
switch kind { switch kind {
case reflect.Array: case reflect.Array: return gconv.Interfaces(value)
return gconv.Interfaces(value) case reflect.Slice: return gconv.Interfaces(value)
case reflect.Slice: case reflect.Map: return gconv.Map(value)
return gconv.Interfaces(value) case reflect.Struct: return gconv.Map(value)
case reflect.Map: default:
return gconv.Map(value) // Use json decode/encode at last.
case reflect.Struct: b, _ := Encode(value)
return gconv.Map(value) v, _ := Decode(b)
default: return v
// Use json decode/encode at last. }
b, _ := Encode(value) }
v, _ := Decode(b)
return v
}
}
} }
// Set <key>:<value> to <pointer>, the <key> may be a map key or slice index. // Set <key>:<value> to <pointer>, the <key> may be a map key or slice index.
// It returns the pointer to the new value set. // It returns the pointer to the new value set.
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} { func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
switch (*pointer).(type) { switch (*pointer).(type) {
case map[string]interface{}: case map[string]interface{}:
(*pointer).(map[string]interface{})[key] = value (*pointer).(map[string]interface{})[key] = value
return &value return &value
case []interface{}: case []interface{}:
n, _ := strconv.Atoi(key) n, _ := strconv.Atoi(key)
if len((*pointer).([]interface{})) > n { if len((*pointer).([]interface{})) > n {
(*pointer).([]interface{})[n] = value (*pointer).([]interface{})[n] = value
return &(*pointer).([]interface{})[n] return &(*pointer).([]interface{})[n]
} else { } else {
s := make([]interface{}, n+1) s := make([]interface{}, n + 1)
copy(s, (*pointer).([]interface{})) copy(s, (*pointer).([]interface{}))
s[n] = value s[n] = value
*pointer = s *pointer = s
return &s[n] return &s[n]
} }
default: default:
*pointer = value *pointer = value
} }
return pointer return pointer
} }
// Get a pointer to the value by specified <pattern>. // Get a pointer to the value by specified <pattern>.
func (j *Json) getPointerByPattern(pattern string) *interface{} { func (j *Json) getPointerByPattern(pattern string) *interface{} {
if j.vc { if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern) return j.getPointerByPatternWithViolenceCheck(pattern)
} else { } else {
return j.getPointerByPatternWithoutViolenceCheck(pattern) return j.getPointerByPatternWithoutViolenceCheck(pattern)
} }
} }
// Get a pointer to the value of specified <pattern> with violence check. // Get a pointer to the value of specified <pattern> with violence check.
func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} { func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
//todo 此判断冗余 if !j.vc {
if !j.vc { return j.getPointerByPatternWithoutViolenceCheck(pattern)
return j.getPointerByPatternWithoutViolenceCheck(pattern) }
} index := len(pattern)
index := len(pattern) start := 0
start := 0 length := 0
length := 0 pointer := j.p
pointer := j.p if index == 0 {
if index == 0 { return pointer
return pointer }
} for {
for { if r := j.checkPatternByPointer(pattern[start:index], pointer); r != nil {
if r := j.checkPatternByPointer(pattern[start:index], pointer); r != nil { length += index - start
length += index - start if start > 0 {
if start > 0 { length += 1
length += 1 }
} start = index + 1
start = index + 1 index = len(pattern)
index = len(pattern) if length == len(pattern) {
if length == len(pattern) { return r
return r } else {
} else { pointer = r
pointer = r }
} } else {
} else { // Get the position for next separator char.
// Get the position for next separator char. index = strings.LastIndexByte(pattern[start:index], j.c)
index = strings.LastIndexByte(pattern[start:index], j.c) if index != -1 && length > 0 {
if index != -1 && length > 0 { index += length + 1
index += length + 1 }
} }
} if start >= index {
if start >= index { break
break }
} }
} return nil
return nil
} }
// Get a pointer to the value of specified <pattern>, with no violence check. // Get a pointer to the value of specified <pattern>, with no violence check.
func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} { func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
//todo 此判断冗余 if j.vc {
if j.vc { return j.getPointerByPatternWithViolenceCheck(pattern)
return j.getPointerByPatternWithViolenceCheck(pattern) }
} pointer := j.p
pointer := j.p if len(pattern) == 0 {
if len(pattern) == 0 { return pointer
return pointer }
} array := strings.Split(pattern, string(j.c))
array := strings.Split(pattern, string(j.c)) for k, v := range array {
for k, v := range array { if r := j.checkPatternByPointer(v, pointer); r != nil {
if r := j.checkPatternByPointer(v, pointer); r != nil { if k == len(array) - 1 {
if k == len(array)-1 { return r
return r } else {
} else { pointer = r
pointer = r }
} } else {
} else { break
break }
} }
} return nil
return nil
} }
// Check whether there's value by <key> in specified <pointer>. // Check whether there's value by <key> in specified <pointer>.
// It returns a pointer to the value. // It returns a pointer to the value.
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} { func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
switch (*pointer).(type) { switch (*pointer).(type) {
case map[string]interface{}: case map[string]interface{}:
if v, ok := (*pointer).(map[string]interface{})[key]; ok { if v, ok := (*pointer).(map[string]interface{})[key]; ok {
return &v return &v
} }
case []interface{}: case []interface{}:
if gstr.IsNumeric(key) { if gstr.IsNumeric(key) {
n, err := strconv.Atoi(key) n, err := strconv.Atoi(key)
if err == nil && len((*pointer).([]interface{})) > n { if err == nil && len((*pointer).([]interface{})) > n {
return &(*pointer).([]interface{})[n] return &(*pointer).([]interface{})[n]
} }
} }
} }
return nil return nil
} }

View File

@ -27,165 +27,163 @@ import (
// or it will make no sense. // or it will make no sense.
// The <unsafe> param specifies whether using this Json object // The <unsafe> param specifies whether using this Json object
// in un-concurrent-safe context, which is false in default. // in un-concurrent-safe context, which is false in default.
func New(data interface{}, unsafe ...bool) *Json { func New(data interface{}, unsafe...bool) *Json {
j := (*Json)(nil) j := (*Json)(nil)
switch data.(type) { switch data.(type) {
case string, []byte: case string, []byte:
if r, err := LoadContent(gconv.Bytes(data)); err == nil { if r, err := LoadContent(gconv.Bytes(data)); err == nil {
j = r j = r
} else { } else {
j = &Json{ j = &Json {
p: &data, p : &data,
c: byte(gDEFAULT_SPLIT_CHAR), c : byte(gDEFAULT_SPLIT_CHAR),
vc: false, vc : false ,
} }
} }
default: default:
rv := reflect.ValueOf(data) rv := reflect.ValueOf(data)
kind := rv.Kind() kind := rv.Kind()
if kind == reflect.Ptr { if kind == reflect.Ptr {
rv = rv.Elem() rv = rv.Elem()
kind = rv.Kind() kind = rv.Kind()
} }
switch kind { switch kind {
case reflect.Slice: case reflect.Slice: fallthrough
fallthrough case reflect.Array:
case reflect.Array: i := interface{}(nil)
i := interface{}(nil) i = gconv.Interfaces(data)
i = gconv.Interfaces(data) j = &Json {
j = &Json{ p : &i,
p: &i, c : byte(gDEFAULT_SPLIT_CHAR),
c: byte(gDEFAULT_SPLIT_CHAR), vc : false ,
vc: false, }
} case reflect.Map: fallthrough
case reflect.Map: case reflect.Struct:
fallthrough i := interface{}(nil)
case reflect.Struct: i = gconv.Map(data, "json")
i := interface{}(nil) j = &Json {
i = gconv.Map(data, "json") p : &i,
j = &Json{ c : byte(gDEFAULT_SPLIT_CHAR),
p: &i, vc : false ,
c: byte(gDEFAULT_SPLIT_CHAR), }
vc: false, default:
} j = &Json {
default: p : &data,
j = &Json{ c : byte(gDEFAULT_SPLIT_CHAR),
p: &data, vc : false ,
c: byte(gDEFAULT_SPLIT_CHAR), }
vc: false, }
} }
} j.mu = rwmutex.New(unsafe...)
} return j
j.mu = rwmutex.New(unsafe...)
return j
} }
// NewUnsafe creates a un-concurrent-safe Json object. // NewUnsafe creates a un-concurrent-safe Json object.
func NewUnsafe(data ...interface{}) *Json { func NewUnsafe(data...interface{}) *Json {
if len(data) > 0 { if len(data) > 0 {
return New(data[0], true) return New(data[0], true)
} }
return New(nil, true) return New(nil, true)
} }
// Valid checks whether <data> is a valid JSON data type. // Valid checks whether <data> is a valid JSON data type.
func Valid(data interface{}) bool { func Valid(data interface{}) bool {
return json.Valid(gconv.Bytes(data)) return json.Valid(gconv.Bytes(data))
} }
// Encode encodes <value> to JSON data type of bytes. // Encode encodes <value> to JSON data type of bytes.
func Encode(value interface{}) ([]byte, error) { func Encode(value interface{}) ([]byte, error) {
return json.Marshal(value) return json.Marshal(value)
} }
// Decode decodes <data>(string/[]byte) to golang variable. // Decode decodes <data>(string/[]byte) to golang variable.
func Decode(data interface{}) (interface{}, error) { func Decode(data interface{}) (interface{}, error) {
var value interface{} var value interface{}
if err := DecodeTo(gconv.Bytes(data), &value); err != nil { if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
return nil, err return nil, err
} else { } else {
return value, nil return value, nil
} }
} }
// Decode decodes <data>(string/[]byte) to specified golang variable <v>. // Decode decodes <data>(string/[]byte) to specified golang variable <v>.
// The <v> should be a pointer type. // The <v> should be a pointer type.
func DecodeTo(data interface{}, v interface{}) error { func DecodeTo(data interface{}, v interface{}) error {
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data))) decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
decoder.UseNumber() decoder.UseNumber()
return decoder.Decode(v) return decoder.Decode(v)
} }
// DecodeToJson codes <data>(string/[]byte) to a Json object. // DecodeToJson codes <data>(string/[]byte) to a Json object.
func DecodeToJson(data interface{}, unsafe ...bool) (*Json, error) { func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
if v, err := Decode(gconv.Bytes(data)); err != nil { if v, err := Decode(gconv.Bytes(data)); err != nil {
return nil, err return nil, err
} else { } else {
return New(v, unsafe...), nil return New(v, unsafe...), nil
} }
} }
// Load loads content from specified file <path>, // Load loads content from specified file <path>,
// and creates a Json object from its content. // and creates a Json object from its content.
func Load(path string, unsafe ...bool) (*Json, error) { func Load(path string, unsafe...bool) (*Json, error) {
return LoadContent(gfcache.GetBinContents(path), unsafe...) return LoadContent(gfcache.GetBinContents(path), unsafe...)
} }
// LoadContent creates a Json object from given content, // LoadContent creates a Json object from given content,
// it checks the data type of <content> automatically, // it checks the data type of <content> automatically,
// supporting JSON, XML, YAML and TOML types of data. // supporting JSON, XML, YAML and TOML types of data.
func LoadContent(data interface{}, unsafe ...bool) (*Json, error) { func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
var err error var err error
var result interface{} var result interface{}
b := gconv.Bytes(data) b := gconv.Bytes(data)
t := "" t := ""
if len(b) == 0 { if len(b) == 0 {
return New(nil, unsafe...), nil return New(nil, unsafe...), nil
} }
// auto check data type // auto check data type
if json.Valid(b) { if json.Valid(b) {
t = "json" t = "json"
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, b) { } else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, b) {
t = "xml" t = "xml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) { } else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
t = "yml" t = "yml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) { } else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
t = "toml" t = "toml"
} else { } else {
return nil, errors.New("unsupported data type") return nil, errors.New("unsupported data type")
} }
// convert to json type data // convert to json type data
switch t { switch t {
case "json", ".json": case "json", ".json":
// ok // ok
case "xml", ".xml": case "xml", ".xml":
// TODO UseNumber // TODO UseNumber
b, err = gxml.ToJson(b) b, err = gxml.ToJson(b)
case "yml", "yaml", ".yml", ".yaml": case "yml", "yaml", ".yml", ".yaml":
// TODO UseNumber // TODO UseNumber
b, err = gyaml.ToJson(b) b, err = gyaml.ToJson(b)
case "toml", ".toml": case "toml", ".toml":
// TODO UseNumber // TODO UseNumber
b, err = gtoml.ToJson(b) b, err = gtoml.ToJson(b)
//todo 不可达
default: default:
err = errors.New("nonsupport type " + t) err = errors.New("nonsupport type " + t)
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
if result == nil { if result == nil {
decoder := json.NewDecoder(bytes.NewReader(b)) decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber() decoder.UseNumber()
if err := decoder.Decode(&result); err != nil { if err := decoder.Decode(&result); err != nil {
return nil, err return nil, err
} }
switch result.(type) { switch result.(type) {
case string, []byte: case string, []byte:
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b)) return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
} }
} }
return New(result, unsafe...), nil return New(result, unsafe...), nil
} }

View File

@ -26,32 +26,32 @@ import (
) )
const ( const (
// Default configuration file name. // Default configuration file name.
DEFAULT_CONFIG_FILE = "config.toml" DEFAULT_CONFIG_FILE = "config.toml"
) )
// Configuration struct. // Configuration struct.
type Config struct { type Config struct {
name *gtype.String // Default configuration file name. name *gtype.String // Default configuration file name.
paths *garray.StringArray // Searching path array. paths *garray.StringArray // Searching path array.
jsons *gmap.StrAnyMap // The pared JSON objects for configuration files. jsons *gmap.StrAnyMap // The pared JSON objects for configuration files.
vc *gtype.Bool // Whether do violence check in value index searching. vc *gtype.Bool // Whether do violence check in value index searching.
// It affects the performance when set true(false in default). // It affects the performance when set true(false in default).
} }
// New returns a new configuration management object. // New returns a new configuration management object.
// The param <file> specifies the default configuration file name for reading. // The param <file> specifies the default configuration file name for reading.
func New(file ...string) *Config { func New(file...string) *Config {
name := DEFAULT_CONFIG_FILE name := DEFAULT_CONFIG_FILE
if len(file) > 0 { if len(file) > 0 {
name = file[0] name = file[0]
} }
c := &Config{ c := &Config {
name: gtype.NewString(name), name : gtype.NewString(name),
paths: garray.NewStringArray(), paths : garray.NewStringArray(),
jsons: gmap.NewStrAnyMap(), jsons : gmap.NewStrAnyMap(),
vc: gtype.NewBool(), vc : gtype.NewBool(),
} }
// Customized dir path from env/cmd. // Customized dir path from env/cmd.
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" { if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" {
if gfile.Exists(envPath) { if gfile.Exists(envPath) {
@ -71,85 +71,85 @@ func New(file ...string) *Config {
_ = c.AddPath(mainPath) _ = c.AddPath(mainPath)
} }
} }
return c return c
} }
// filePath returns the absolute configuration file path for the given filename by <file>. // filePath returns the absolute configuration file path for the given filename by <file>.
func (c *Config) filePath(file ...string) (path string) { func (c *Config) filePath(file...string) (path string) {
name := c.name.Val() name := c.name.Val()
if len(file) > 0 { if len(file) > 0 {
name = file[0] name = file[0]
} }
path = c.FilePath(name) path = c.FilePath(name)
if path == "" { if path == "" {
buffer := bytes.NewBuffer(nil) buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 { if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name))
c.paths.RLockFunc(func(array []string) { c.paths.RLockFunc(func(array []string) {
index := 1 index := 1
for _, v := range array { for _, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
index++ index++
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v + gfile.Separator + "config"))
index++ index++
} }
}) })
} else { } else {
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name)) buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name))
} }
glog.Error(buffer.String()) glog.Error(buffer.String())
} }
return path return path
} }
// SetPath sets the configuration directory path for file search. // SetPath sets the configuration directory path for file search.
// The param <path> can be absolute or relative path, // The param <path> can be absolute or relative path,
// but absolute path is strongly recommended. // but absolute path is strongly recommended.
func (c *Config) SetPath(path string) error { func (c *Config) SetPath(path string) error {
// Absolute path. // Absolute path.
realPath := gfile.RealPath(path) realPath := gfile.RealPath(path)
if realPath == "" { if realPath == "" {
// Relative path. // Relative path.
c.paths.RLockFunc(func(array []string) { c.paths.RLockFunc(func(array []string) {
for _, v := range array { for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" { if path, _ := gspath.Search(v, path); path != "" {
realPath = path realPath = path
break break
} }
} }
}) })
} }
// Path not exist. // Path not exist.
if realPath == "" { if realPath == "" {
buffer := bytes.NewBuffer(nil) buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 { if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path)) buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) { c.paths.RLockFunc(func(array []string) {
for k, v := range array { for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
} }
}) })
} else { } else {
buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path))
} }
err := errors.New(buffer.String()) err := errors.New(buffer.String())
glog.Error(err) glog.Error(err)
return err return err
} }
// Should be a directory. // Should be a directory.
if !gfile.IsDir(realPath) { if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path)) err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err) glog.Error(err)
return err return err
} }
// Repeated path check. // Repeated path check.
if c.paths.Search(realPath) != -1 { if c.paths.Search(realPath) != -1 {
return nil return nil
} }
c.jsons.Clear() c.jsons.Clear()
c.paths.Clear() c.paths.Clear()
c.paths.Append(realPath) c.paths.Append(realPath)
return nil return nil
} }
// SetViolenceCheck sets whether to perform hierarchical conflict check. // SetViolenceCheck sets whether to perform hierarchical conflict check.
@ -159,58 +159,58 @@ func (c *Config) SetPath(path string) error {
// and it is not recommended to allow separators in the key names. // and it is not recommended to allow separators in the key names.
// It is best to avoid this on the application side. // It is best to avoid this on the application side.
func (c *Config) SetViolenceCheck(check bool) { func (c *Config) SetViolenceCheck(check bool) {
c.vc.Set(check) c.vc.Set(check)
c.Clear() c.Clear()
} }
// AddPath adds a absolute or relative path to the search paths. // AddPath adds a absolute or relative path to the search paths.
func (c *Config) AddPath(path string) error { func (c *Config) AddPath(path string) error {
// Absolute path. // Absolute path.
realPath := gfile.RealPath(path) realPath := gfile.RealPath(path)
if realPath == "" { if realPath == "" {
// Relative path. // Relative path.
c.paths.RLockFunc(func(array []string) { c.paths.RLockFunc(func(array []string) {
for _, v := range array { for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" { if path, _ := gspath.Search(v, path); path != "" {
realPath = path realPath = path
break break
} }
} }
}) })
} }
if realPath == "" { if realPath == "" {
buffer := bytes.NewBuffer(nil) buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 { if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path)) buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) { c.paths.RLockFunc(func(array []string) {
for k, v := range array { for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) buffer.WriteString(fmt.Sprintf("\n%d. %s", k + 1, v))
} }
}) })
} else { } else {
buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path))
} }
err := errors.New(buffer.String()) err := errors.New(buffer.String())
glog.Error(err) glog.Error(err)
return err return err
} }
if !gfile.IsDir(realPath) { if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path)) err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err) glog.Error(err)
return err return err
} }
// Repeated path check. // Repeated path check.
if c.paths.Search(realPath) != -1 { if c.paths.Search(realPath) != -1 {
return nil return nil
} }
c.paths.Append(realPath) c.paths.Append(realPath)
//glog.Debug("[gcfg] AddPath:", realPath) //glog.Debug("[gcfg] AddPath:", realPath)
return nil return nil
} }
// Deprecated. // Deprecated.
// Alias of FilePath. // Alias of FilePath.
func (c *Config) GetFilePath(file ...string) (path string) { func (c *Config) GetFilePath(file...string) (path string) {
return c.FilePath(file...) return c.FilePath(file...)
} }
@ -218,274 +218,275 @@ func (c *Config) GetFilePath(file ...string) (path string) {
// If <file> is not passed, it returns the configuration file path of the default name. // If <file> is not passed, it returns the configuration file path of the default name.
// If the specified configuration file does not exist, // If the specified configuration file does not exist,
// an empty string is returned. // an empty string is returned.
func (c *Config) FilePath(file ...string) (path string) { func (c *Config) FilePath(file...string) (path string) {
name := c.name.Val() name := c.name.Val()
if len(file) > 0 { if len(file) > 0 {
name = file[0] name = file[0]
} }
c.paths.RLockFunc(func(array []string) { c.paths.RLockFunc(func(array []string) {
for _, v := range array { for _, v := range array {
if path, _ = gspath.Search(v, name); path != "" { if path, _ = gspath.Search(v, name); path != "" {
break break
} }
if path, _ = gspath.Search(v+gfile.Separator+"config", name); path != "" { if path, _ = gspath.Search(v + gfile.Separator + "config", name); path != "" {
break break
} }
} }
}) })
return return
} }
// SetFileName sets the default configuration file name. // SetFileName sets the default configuration file name.
func (c *Config) SetFileName(name string) { func (c *Config) SetFileName(name string) {
c.name.Set(name) c.name.Set(name)
} }
// GetFileName returns the default configuration file name. // GetFileName returns the default configuration file name.
func (c *Config) GetFileName() string { func (c *Config) GetFileName() string {
return c.name.Val() return c.name.Val()
} }
// getJson returns a gjson.Json object for the specified <file> content. // getJson returns a gjson.Json object for the specified <file> content.
// It would print error if file reading fails. // It would print error if file reading fails.
// If any error occurs, it return nil. // If any error occurs, it return nil.
func (c *Config) getJson(file ...string) *gjson.Json { func (c *Config) getJson(file...string) *gjson.Json {
name := c.name.Val() name := c.name.Val()
//todo 此函数file参数无效没有地方调用且有SetFileName函数提供链式操作因此建议去掉 if len(file) > 0 {
if len(file) > 0 { name = file[0]
name = file[0] }
} r := c.jsons.GetOrSetFuncLock(name, func() interface{} {
r := c.jsons.GetOrSetFuncLock(name, func() interface{} { content := ""
content := "" filePath := ""
filePath := "" if content = GetContent(name); content == "" {
if content = GetContent(name); content == "" { filePath = c.filePath(name)
filePath = c.filePath(name) if filePath == "" {
if filePath == "" { return nil
return nil }
} content = gfile.GetContents(filePath)
content = gfile.GetContents(filePath) }
} if j, err := gjson.LoadContent(content); err == nil {
if j, err := gjson.LoadContent(content); err == nil { j.SetViolenceCheck(c.vc.Val())
j.SetViolenceCheck(c.vc.Val()) // Add monitor for this configuration file,
// Add monitor for this configuration file, // any changes of this file will refresh its cache in Config object.
// any changes of this file will refresh its cache in Config object. if filePath != "" {
if filePath != "" { gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
gfsnotify.Add(filePath, func(event *gfsnotify.Event) { c.jsons.Remove(name)
c.jsons.Remove(name) })
}) }
} return j
return j } else {
} else { if filePath != "" {
if filePath != "" { glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error()) } else {
} else { glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error())
glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error()) }
} }
} return nil
return nil })
}) if r != nil {
if r != nil { return r.(*gjson.Json)
return r.(*gjson.Json) }
} return nil
return nil
} }
func (c *Config) Get(pattern string, def ...interface{}) interface{} { func (c *Config) Get(pattern string, def...interface{}) interface{} {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.Get(pattern, def...) return j.Get(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var { func (c *Config) GetVar(pattern string, def...interface{}) *gvar.Var {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return gvar.New(j.Get(pattern, def...), true) return gvar.New(j.Get(pattern, def...), true)
} }
return gvar.New(nil, true) return gvar.New(nil, true)
} }
func (c *Config) Contains(pattern string) bool { func (c *Config) Contains(pattern string) bool {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.Contains(pattern) return j.Contains(pattern)
} }
return false return false
} }
func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface{} { func (c *Config) GetMap(pattern string, def...interface{}) map[string]interface{} {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetMap(pattern, def...) return j.GetMap(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} { func (c *Config) GetArray(pattern string, def...interface{}) []interface{} {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetArray(pattern, def...) return j.GetArray(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetString(pattern string, def ...interface{}) string { func (c *Config) GetString(pattern string, def...interface{}) string {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetString(pattern, def...) return j.GetString(pattern, def...)
} }
return "" return ""
} }
func (c *Config) GetStrings(pattern string, def ...interface{}) []string { func (c *Config) GetStrings(pattern string, def...interface{}) []string {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetStrings(pattern, def...) return j.GetStrings(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetInterfaces(pattern string, def ...interface{}) []interface{} { func (c *Config) GetInterfaces(pattern string, def...interface{}) []interface{} {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetInterfaces(pattern, def...) return j.GetInterfaces(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetBool(pattern string, def ...interface{}) bool { func (c *Config) GetBool(pattern string, def...interface{}) bool {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetBool(pattern, def...) return j.GetBool(pattern, def...)
} }
return false return false
} }
func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 { func (c *Config) GetFloat32(pattern string, def...interface{}) float32 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetFloat32(pattern, def...) return j.GetFloat32(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 { func (c *Config) GetFloat64(pattern string, def...interface{}) float64 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetFloat64(pattern, def...) return j.GetFloat64(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 { func (c *Config) GetFloats(pattern string, def...interface{}) []float64 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetFloats(pattern, def...) return j.GetFloats(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetInt(pattern string, def ...interface{}) int { func (c *Config) GetInt(pattern string, def...interface{}) int {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetInt(pattern, def...) return j.GetInt(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetInt8(pattern string, def ...interface{}) int8 {
if j := c.getJson(); j != nil { func (c *Config) GetInt8(pattern string, def...interface{}) int8 {
return j.GetInt8(pattern, def...) if j := c.getJson(); j != nil {
} return j.GetInt8(pattern, def...)
return 0 }
return 0
} }
func (c *Config) GetInt16(pattern string, def ...interface{}) int16 { func (c *Config) GetInt16(pattern string, def...interface{}) int16 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetInt16(pattern, def...) return j.GetInt16(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetInt32(pattern string, def ...interface{}) int32 { func (c *Config) GetInt32(pattern string, def...interface{}) int32 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetInt32(pattern, def...) return j.GetInt32(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetInt64(pattern string, def ...interface{}) int64 { func (c *Config) GetInt64(pattern string, def...interface{}) int64 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetInt64(pattern, def...) return j.GetInt64(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetInts(pattern string, def ...interface{}) []int { func (c *Config) GetInts(pattern string, def...interface{}) []int {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetInts(pattern, def...) return j.GetInts(pattern, def...)
} }
return nil return nil
} }
func (c *Config) GetUint(pattern string, def ...interface{}) uint { func (c *Config) GetUint(pattern string, def...interface{}) uint {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetUint(pattern, def...) return j.GetUint(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 { func (c *Config) GetUint8(pattern string, def...interface{}) uint8 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetUint8(pattern, def...) return j.GetUint8(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 { func (c *Config) GetUint16(pattern string, def...interface{}) uint16 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetUint16(pattern, def...) return j.GetUint16(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 { func (c *Config) GetUint32(pattern string, def...interface{}) uint32 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetUint32(pattern, def...) return j.GetUint32(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 { func (c *Config) GetUint64(pattern string, def...interface{}) uint64 {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetUint64(pattern, def...) return j.GetUint64(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetTime(pattern string, format ...string) time.Time { func (c *Config) GetTime(pattern string, format...string) time.Time {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetTime(pattern, format...) return j.GetTime(pattern, format...)
} }
return time.Time{} return time.Time{}
} }
func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration { func (c *Config) GetDuration(pattern string, def...interface{}) time.Duration {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetDuration(pattern, def...) return j.GetDuration(pattern, def...)
} }
return 0 return 0
} }
func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time { func (c *Config) GetGTime(pattern string, format...string) *gtime.Time {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetGTime(pattern, format...) return j.GetGTime(pattern, format...)
} }
return nil return nil
} }
func (c *Config) GetToStruct(pattern string, pointer interface{}, def ...interface{}) error { func (c *Config) GetToStruct(pattern string, pointer interface{}, def...interface{}) error {
if j := c.getJson(); j != nil { if j := c.getJson(); j != nil {
return j.GetToStruct(pattern, pointer) return j.GetToStruct(pattern, pointer)
} }
return errors.New("config file not found") return errors.New("config file not found")
} }
// Deprecated. See Clear. // Deprecated. See Clear.
func (c *Config) Reload() { func (c *Config) Reload() {
c.jsons.Clear() c.jsons.Clear()
} }
// Clear removes all parsed configuration files content cache, // Clear removes all parsed configuration files content cache,
// which will force reload configuration content from file. // which will force reload configuration content from file.
func (c *Config) Clear() { func (c *Config) Clear() {
c.jsons.Clear() c.jsons.Clear()
} }