You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
5.7 KiB

// ------------------------------------------------------------------------
// 🚀 Project active3
// 🔗 Active Thing (activething.com) git.activething.com/go
//
// File name reflected_object.go
// ✍️ Created by DEV
// 📅 Modified 16/05/2024
//
// 🔒 Copyright 2024 activething.com
//
// 📜 LICENSE --------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package pkjson
import (
"apack"
"bfrs"
"reflect"
"sort"
)
type (
rObject struct {
fields rObjectFields
indexes map[string]int
}
)
func newRObject(rtype reflect.Type) *rObject {
cur := rObjectFields{}
nxt := rObjectFields{{ rtype: rtype}}
var fls rObjectFields
var cnt, nxCnt map[reflect.Type]int
vis := map[reflect.Type]bool{}
nms := bfrs.NewABuffer()
for len(nxt) > 0 {
cur,nxt = nxt, cur[:0]
cnt,nxCnt = nxCnt,map[reflect.Type]int{}
for _,f := range cur {
if vis[f.rtype] {
continue
}
vis[f.rtype]=true
for i:=0; i<f.rtype.NumField(); i++ {
sf := f.rtype.Field(i)
if sf.Anonymous {
tp := sf.Type
if tp.Kind() == reflect.Pointer {
tp = tp.Elem()
}
if !sf.IsExported() && tp.Kind() != reflect.Struct {
continue
}
} else if !sf.IsExported() {
continue
}
nm, fg, has := apack.FlagsFromPackTag(sf.Tag)
if !has {
nm, fg, has = jsonTags(sf.Tag)
}
if has {
if nm == "-" || fg.Omit() {
continue
}
if !apack.CheckTag(nm) {
nm = ""
}
}
index:=make([]int,len(f.index)+1)
copy(index,f.index)
index[len(f.index)] = i
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Pointer {
ft = ft.Elem()
}
if fg.String() && !fg.Quote() {
switch ft.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64,
reflect.String:
fg = fg| apack.FlagQuote
}
}
if nm != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
tged := nm != ""
if nm == "" {
nm = sf.Name
}
of := rObjectField {
name : nm,
isTagged: tged,
index : index,
rtype : ft,
flags : fg,
}
of.nameB = []byte(of.name)
of.foldFn= apack.GetFoldFunc(of.nameB)
// BuildPack nameEscHTML and nameNonEsc ahead of time.
nms.Reset()
nms.WriteByte('"')
nms.WriteHTML(of.nameB)
nms.Write2Byte('"',':')
of.nameS = nms.String()
of.nameU = `"` + of.name + `":`
fls = append(fls,of)
if cnt[f.rtype] > 1 {
fls = append(fls,fls[len(fls)-1])
}
continue
}
nxCnt[ft]++
if nxCnt[ft] == 1 {
nxt = append(nxt,rObjectField{ name: ft.Name(), index: index, rtype: ft})
}
}
}
}
sort.Slice(fls,func(i,j int) bool {
ls := fls
if ls[i].name != ls[j].name {
return ls[i].name < ls[j].name
}
if len(ls[i].index) != len(ls[j].index) {
return len(ls[i].index) < len(ls[j].index)
}
if ls[i].isTagged != ls[j].isTagged {
return ls[i].isTagged
}
return ls.Less(i, j)
})
out := fls[:0]
for ad,i :=0,0; i< len(fls); i += ad {
fi := fls[i]
nm := fi.name
for ad=1; i+ad <len(fls); ad++ {
fj := fls[i+ad]
if fj.name != nm {
break
}
}
if ad == 1 {
out = append(out,fi)
continue
}
do,ok := fls[i:i+ad].dominant()
if ok {
out = append(out,do)
}
}
fls = out
sort.Sort(fls)
for i:= range fls {
f := &fls[i]
f.packerFn = packReflected(f.typeByIndex(rtype))
}
nmIdx := make(map[string]int,len(fls))
for i,fd := range fls {
nmIdx[fd.name]=i
}
return &rObject{
fields : fls,
indexes: nmIdx,
}
}
func (o rObject) pack (b *Builder, v reflect.Value) {
nx := byte('{')
FieldLoop:
for i:= range o.fields {
fd := &o.fields[i]
fv := v
for _,i := range fd.index {
if fv.Kind() == reflect.Pointer {
if fv.IsNil() {
continue FieldLoop
}
fv = fv.Elem()
}
fv = fv.Field(i)
}
if fd.flags.OmitEmpty() && isEmptyValue(v) {
continue
}
b.B.WriteByte(nx)
nx = ','
if fd.flags.EscapeHTML() {
b.B.WriteString(fd.nameS)
} else {
b.B.WriteString(fd.nameU)
}
fd.packerFn(b,fv)
}
if nx == '{' {
b.B.Write2Byte('{','}')
} else {
b.B.WriteByte('}')
}
}
func jsonTags (tag reflect.StructTag) (string, apack.Flags,bool) {
return apack.FlagsFromTag(tag,jsonTag,func(n string) apack.Flags {
switch n {
case "omitempty" : return apack.FlagOmitEmpty
case "quote" : return apack.FlagQuote
default : return 0
}
})
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Pointer:
return v.IsNil()
default:
return false
}
}

Powered by TurnKey Linux.