blob: 29cadf7d02d798227bf5a7156f518ecec45c18e4 [file] [log] [blame]
Stephane Barbarieec0919b2018-09-05 14:14: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 */
16package model
17
18import (
19 "bytes"
20 "crypto/md5"
21 "fmt"
Stephane Barbarie126101e2018-10-11 16:18:48 -040022 "github.com/golang/protobuf/proto"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040023 "reflect"
24 "sort"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040025)
26
27var (
28 RevisionCache = make(map[string]interface{})
29)
30
31type NonPersistedRevision struct {
32 Config *DataRevision
33 Children map[string][]Revision
34 Hash string
35 Branch *Branch
36 WeakRef string
37}
38
39func NewNonPersistedRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
40 cr := &NonPersistedRevision{}
41 cr.Branch = branch
42 cr.Config = NewDataRevision(data)
43 cr.Children = children
44 cr.Finalize()
45
46 return cr
47}
48
49func (npr *NonPersistedRevision) SetConfig(config *DataRevision) {
50 npr.Config = config
51}
52
53func (npr *NonPersistedRevision) GetConfig() *DataRevision {
54 return npr.Config
55}
56
57func (npr *NonPersistedRevision) SetChildren(children map[string][]Revision) {
58 npr.Children = children
59}
60
61func (npr *NonPersistedRevision) GetChildren() map[string][]Revision {
62 return npr.Children
63}
64
65func (npr *NonPersistedRevision) SetHash(hash string) {
66 npr.Hash = hash
67}
68
69func (npr *NonPersistedRevision) GetHash() string {
70 return npr.Hash
71}
72
73func (npr *NonPersistedRevision) ClearHash() {
74 npr.Hash = ""
75}
76
77func (npr *NonPersistedRevision) SetBranch(branch *Branch) {
78 npr.Branch = branch
79}
80
81func (npr *NonPersistedRevision) GetBranch() *Branch {
82 return npr.Branch
83}
84
85func (npr *NonPersistedRevision) GetData() interface{} {
86 if npr.Config == nil {
87 return nil
88 }
89 return npr.Config.Data
90}
91
Stephane Barbarie06c4a742018-10-01 11:09:32 -040092func (npr *NonPersistedRevision) GetNode() *node {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040093 return npr.Branch.Node
94}
95
96func (npr *NonPersistedRevision) Finalize() {
97 npr.SetHash(npr.hashContent())
98
99 if _, exists := RevisionCache[npr.Hash]; !exists {
100 RevisionCache[npr.Hash] = npr
101 }
102 if _, exists := RevisionCache[npr.Config.Hash]; !exists {
103 RevisionCache[npr.Config.Hash] = npr.Config
104 } else {
105 npr.Config = RevisionCache[npr.Config.Hash].(*DataRevision)
106 }
107}
108
109func (npr *NonPersistedRevision) hashContent() string {
110 var buffer bytes.Buffer
111 var childrenKeys []string
112
113 if npr.Config != nil {
114 buffer.WriteString(npr.Config.Hash)
115 }
116
117 for key, _ := range npr.Children {
118 childrenKeys = append(childrenKeys, key)
119 }
120 sort.Strings(childrenKeys)
121
122 if npr.Children != nil && len(npr.Children) > 0 {
123 // Loop through sorted Children keys
124 for _, key := range childrenKeys {
125 for _, child := range npr.Children[key] {
126 if child != nil && child.GetHash() != "" {
127 buffer.WriteString(child.GetHash())
128 }
129 }
130 }
131 }
132
133 return fmt.Sprintf("%x", md5.Sum(buffer.Bytes()))[:12]
134}
135
136func (npr *NonPersistedRevision) Get(depth int) interface{} {
137 originalData := npr.GetData()
138 data := reflect.ValueOf(originalData).Interface()
139
140 if depth != 0 {
141 for fieldName, field := range ChildrenFields(npr.GetData()) {
142 childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
143 if field.IsContainer {
144 for _, rev := range npr.Children[fieldName] {
145 childData := rev.Get(depth - 1)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400146 foundEntry := false
147 for i := 0; i < childDataHolder.Len(); i++ {
148 if reflect.DeepEqual(childDataHolder.Index(i).Interface(), childData) {
149 foundEntry = true
150 break
151 }
152 }
153 if !foundEntry {
154 // avoid duplicates by adding if the child was not found in the holder
155 childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
156 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400157 }
158 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400159 if revs := npr.Children[fieldName]; revs != nil && len(revs) > 0 {
160 rev := npr.Children[fieldName][0]
161 if rev != nil {
162 childData := rev.Get(depth - 1)
163 if reflect.TypeOf(childData) == reflect.TypeOf(childDataHolder.Interface()) {
164 childDataHolder = reflect.ValueOf(childData)
165 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400166 }
167 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400168 }
169 // Merge child data with cloned object
170 reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
171 }
172 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400173
174 result := data
175
176 if result != nil {
177 clone := proto.Clone(data.(proto.Message))
178 result = reflect.ValueOf(clone).Interface()
179 }
180
181 return result
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400182}
183
184func (npr *NonPersistedRevision) UpdateData(data interface{}, branch *Branch) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400185 // TODO: Need to keep the hash for the old revision.
186 // TODO: This will allow us to get rid of the unnecessary data
187
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400188 newRev := reflect.ValueOf(npr).Elem().Interface().(NonPersistedRevision)
189 newRev.SetBranch(branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400190 newRev.SetConfig(NewDataRevision(data))
191 newRev.Finalize()
192
193 return &newRev
194}
195
196func (npr *NonPersistedRevision) UpdateChildren(name string, children []Revision, branch *Branch) Revision {
197 newChildren := make(map[string][]Revision)
198 for entryName, childrenEntry := range npr.Children {
199 for _, revisionEntry := range childrenEntry {
200 newEntry := reflect.ValueOf(revisionEntry).Interface().(Revision)
201 newChildren[entryName] = append(newChildren[entryName], newEntry)
202 }
203 }
204 newChildren[name] = children
205
206 newRev := reflect.ValueOf(npr).Elem().Interface().(NonPersistedRevision)
207 newRev.SetBranch(branch)
208 newRev.SetChildren(newChildren)
209 newRev.Finalize()
210
211 return &newRev
212}
213
214func (npr *NonPersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400215 newRev := reflect.ValueOf(npr).Elem().Interface().(NonPersistedRevision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400216 newRev.SetBranch(branch)
217 newRev.SetChildren(children)
218 newRev.Finalize()
219
220 return &newRev
221}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400222
223func (npr *NonPersistedRevision) Drop(txid string, includeConfig bool) {
224 //npr.SetConfig(nil)
225}