blob: 6d1b2d5d440d69bd9d355392241707db1a05bdae [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
* 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 model
import (
"reflect"
"strings"
)
// IsProtoMessage determines if the specified implements proto.Message type
func IsProtoMessage(object interface{}) bool {
var ok = false
if object != nil {
st := reflect.TypeOf(object)
_, ok = st.MethodByName("ProtoMessage")
}
return ok
}
// FindOwnerType will traverse a data structure and find the parent type of the specified object
func FindOwnerType(obj reflect.Value, name string, depth int, found bool) reflect.Type {
prefix := ""
for d := 0; d < depth; d++ {
prefix += ">>"
}
k := obj.Kind()
switch k {
case reflect.Ptr:
if found {
return obj.Type()
}
t := obj.Type().Elem()
n := reflect.New(t)
if rc := FindOwnerType(n.Elem(), name, depth+1, found); rc != nil {
return rc
}
case reflect.Struct:
if found {
return obj.Type()
}
for i := 0; i < obj.NumField(); i++ {
v := reflect.Indirect(obj)
json := strings.Split(v.Type().Field(i).Tag.Get("json"), ",")
if json[0] == name {
return FindOwnerType(obj.Field(i), name, depth+1, true)
}
if rc := FindOwnerType(obj.Field(i), name, depth+1, found); rc != nil {
return rc
}
}
case reflect.Slice:
s := reflect.MakeSlice(obj.Type(), 1, 1)
n := reflect.New(obj.Type())
n.Elem().Set(s)
for i := 0; i < n.Elem().Len(); i++ {
if found {
return reflect.ValueOf(n.Elem().Index(i).Interface()).Type()
}
}
for i := 0; i < obj.Len(); i++ {
if found {
return obj.Index(i).Type()
}
if rc := FindOwnerType(obj.Index(i), name, depth+1, found); rc != nil {
return rc
}
}
default:
}
return nil
}
// FindKeyOwner will traverse a structure to find the owner type of the specified name
func FindKeyOwner(iface interface{}, name string, depth int) interface{} {
obj := reflect.ValueOf(iface)
k := obj.Kind()
switch k {
case reflect.Ptr:
t := obj.Type().Elem()
n := reflect.New(t)
if rc := FindKeyOwner(n.Elem().Interface(), name, depth+1); rc != nil {
return rc
}
case reflect.Struct:
for i := 0; i < obj.NumField(); i++ {
json := strings.Split(obj.Type().Field(i).Tag.Get("json"), ",")
if json[0] == name {
return obj.Type().Field(i).Type
}
if rc := FindKeyOwner(obj.Field(i).Interface(), name, depth+1); rc != nil {
return rc
}
}
case reflect.Slice:
s := reflect.MakeSlice(obj.Type(), 1, 1)
n := reflect.New(obj.Type())
n.Elem().Set(s)
for i := 0; i < n.Elem().Len(); i++ {
if rc := FindKeyOwner(n.Elem().Index(i).Interface(), name, depth+1); rc != nil {
return rc
}
}
default:
}
return nil
}
// GetAttributeValue traverse a structure to find the value of an attribute
// FIXME: Need to figure out if GetAttributeValue and GetAttributeStructure can become one
// Code is repeated in both, but outputs have a different purpose
// Left as-is for now to get things working
func GetAttributeValue(data interface{}, name string, depth int) (string, reflect.Value) {
var attribName string
var attribValue reflect.Value
obj := reflect.ValueOf(data)
if !obj.IsValid() {
return attribName, attribValue
}
k := obj.Kind()
switch k {
case reflect.Ptr:
if obj.IsNil() {
return attribName, attribValue
}
if attribName, attribValue = GetAttributeValue(obj.Elem().Interface(), name, depth+1); attribValue.IsValid() {
return attribName, attribValue
}
case reflect.Struct:
for i := 0; i < obj.NumField(); i++ {
json := strings.Split(obj.Type().Field(i).Tag.Get("json"), ",")
if json[0] == name {
return obj.Type().Field(i).Name, obj.Field(i)
}
if obj.Field(i).IsValid() {
if attribName, attribValue = GetAttributeValue(obj.Field(i).Interface(), name, depth+1); attribValue.IsValid() {
return attribName, attribValue
}
}
}
case reflect.Slice:
s := reflect.MakeSlice(obj.Type(), 1, 1)
n := reflect.New(obj.Type())
n.Elem().Set(s)
for i := 0; i < obj.Len(); i++ {
if attribName, attribValue = GetAttributeValue(obj.Index(i).Interface(), name, depth+1); attribValue.IsValid() {
return attribName, attribValue
}
}
default:
}
return attribName, attribValue
}
// GetAttributeStructure will traverse a structure to find the data structure for the named attribute
// FIXME: See GetAttributeValue(...) comment
func GetAttributeStructure(data interface{}, name string, depth int) reflect.StructField {
var result reflect.StructField
obj := reflect.ValueOf(data)
if !obj.IsValid() {
return result
}
k := obj.Kind()
switch k {
case reflect.Ptr:
t := obj.Type().Elem()
n := reflect.New(t)
if rc := GetAttributeStructure(n.Elem().Interface(), name, depth+1); rc.Name != "" {
return rc
}
case reflect.Struct:
for i := 0; i < obj.NumField(); i++ {
v := reflect.Indirect(obj)
json := strings.Split(obj.Type().Field(i).Tag.Get("json"), ",")
if json[0] == name {
return v.Type().Field(i)
}
if obj.Field(i).IsValid() {
if rc := GetAttributeStructure(obj.Field(i).Interface(), name, depth+1); rc.Name != "" {
return rc
}
}
}
case reflect.Slice:
s := reflect.MakeSlice(obj.Type(), 1, 1)
n := reflect.New(obj.Type())
n.Elem().Set(s)
for i := 0; i < obj.Len(); i++ {
if rc := GetAttributeStructure(obj.Index(i).Interface(), name, depth+1); rc.Name != "" {
return rc
}
}
default:
}
return result
}