blob: a0e15ef977aac9f32fd8b46883e7a684a77e5408 [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
19import (
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040020 desc "github.com/golang/protobuf/descriptor"
21 "github.com/golang/protobuf/proto"
22 "github.com/golang/protobuf/protoc-gen-go/descriptor"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080023 "github.com/opencord/voltha-lib-go/v3/pkg/log"
24 "github.com/opencord/voltha-protos/v3/go/common"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040025 "reflect"
26 "strconv"
27 "sync"
28)
29
Stephane Barbarieef6650d2019-07-18 12:15:09 -040030type childTypesSingleton struct {
31 mutex sync.RWMutex
Kent Hagermana71a54d2020-03-09 12:30:23 -040032 cache map[reflect.Type]map[string]*ChildType
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040033}
34
Kent Hagermana71a54d2020-03-09 12:30:23 -040035var childTypes = &childTypesSingleton{cache: make(map[reflect.Type]map[string]*ChildType)}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040036
Kent Hagermana71a54d2020-03-09 12:30:23 -040037func (s *childTypesSingleton) GetCacheEntry(key reflect.Type) (map[string]*ChildType, bool) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040038 s.mutex.RLock()
39 defer s.mutex.RUnlock()
Kent Hagermana71a54d2020-03-09 12:30:23 -040040 childTypeMap, exists := s.cache[key]
Stephane Barbarieef6650d2019-07-18 12:15:09 -040041 return childTypeMap, exists
42}
43
Kent Hagermana71a54d2020-03-09 12:30:23 -040044func (s *childTypesSingleton) SetCacheEntry(key reflect.Type, value map[string]*ChildType) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040045 s.mutex.Lock()
46 defer s.mutex.Unlock()
Kent Hagermana71a54d2020-03-09 12:30:23 -040047 s.cache[key] = value
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040048}
49
Stephane Barbariedc5022d2018-11-19 15:21:44 -050050// ChildType structure contains construct details of an object
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040051type ChildType struct {
52 ClassModule string
53 ClassType reflect.Type
54 IsContainer bool
55 Key string
56 KeyFromStr func(s string) interface{}
57}
58
Kent Hagermana71a54d2020-03-09 12:30:23 -040059// ChildrenFields retrieves list of child objects associated to a given type
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060func ChildrenFields(cls interface{}) map[string]*ChildType {
61 if cls == nil {
62 return nil
63 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040064
65 msgType := reflect.TypeOf(cls)
66
Kent Hagermana71a54d2020-03-09 12:30:23 -040067 if fields, have := childTypes.GetCacheEntry(msgType); have {
68 return fields
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040069 }
70
Kent Hagermana71a54d2020-03-09 12:30:23 -040071 fields := make(map[string]*ChildType)
72 _, md := desc.ForMessage(cls.(desc.Message))
73
74 // TODO: Do we need to validate MD for nil, panic or exception?
75 for _, field := range md.Field {
76 if options := field.GetOptions(); options != nil {
77 if proto.HasExtension(options, common.E_ChildNode) {
78 isContainer := *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
79 meta, _ := proto.GetExtension(options, common.E_ChildNode)
80
81 var keyFromStr func(string) interface{}
82 var ct ChildType
83
84 parentType := FindOwnerType(reflect.ValueOf(cls), field.GetName(), 0, false)
85 if meta.(*common.ChildNode).GetKey() != "" {
86 keyType := FindKeyOwner(reflect.New(parentType).Elem().Interface(), meta.(*common.ChildNode).GetKey(), 0)
87
88 switch keyType.(reflect.Type).Name() {
89 case "string":
90 keyFromStr = func(s string) interface{} {
91 return s
92 }
93 case "int32":
94 keyFromStr = func(s string) interface{} {
95 i, _ := strconv.Atoi(s)
96 return int32(i)
97 }
98 case "int64":
99 keyFromStr = func(s string) interface{} {
100 i, _ := strconv.Atoi(s)
101 return int64(i)
102 }
103 case "uint32":
104 keyFromStr = func(s string) interface{} {
105 i, _ := strconv.Atoi(s)
106 return uint32(i)
107 }
108 case "uint64":
109 keyFromStr = func(s string) interface{} {
110 i, _ := strconv.Atoi(s)
111 return uint64(i)
112 }
113 default:
114 log.Errorf("Key type not implemented - type: %s\n", keyType.(reflect.Type))
115 }
116 }
117
118 ct = ChildType{
119 ClassModule: parentType.String(),
120 ClassType: parentType,
121 IsContainer: isContainer,
122 Key: meta.(*common.ChildNode).GetKey(),
123 KeyFromStr: keyFromStr,
124 }
125
126 fields[field.GetName()] = &ct
127 }
128 }
129 }
130
131 // If called multiple times in quick succession w/ the same message types, it is possible for different cache entries to be returned.
132 // This should not be an issue, as the cache is merely for optimization purposes.
133 childTypes.SetCacheEntry(msgType, fields)
134 return fields
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400135}