blob: 8ff89c4563031687a02e9a50eee72e627ac73bce [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 "github.com/opencord/voltha-go/common/log"
20 "reflect"
21)
22
23func revisionsAreEqual(a, b []Revision) bool {
24 // If one is nil, the other must also be nil.
25 if (a == nil) != (b == nil) {
26 return false
27 }
28
29 if len(a) != len(b) {
30 return false
31 }
32
33 for i := range a {
34 if a[i] != b[i] {
35 return false
36 }
37 }
38
39 return true
40}
41
42type changeAnalysis struct {
43 KeyMap1 map[reflect.Value]int
44 KeyMap2 map[reflect.Value]int
45 AddedKeys map[reflect.Value]struct{}
46 RemovedKeys map[reflect.Value]struct{}
47 ChangedKeys map[reflect.Value]struct{}
48}
49
50func newChangeAnalysis(lst1, lst2 []Revision, keyName string) *changeAnalysis {
51 changes := &changeAnalysis{}
52
53 changes.KeyMap1 = make(map[reflect.Value]int)
54 changes.KeyMap2 = make(map[reflect.Value]int)
55
56 changes.AddedKeys = make(map[reflect.Value]struct{})
57 changes.RemovedKeys = make(map[reflect.Value]struct{})
58 changes.ChangedKeys = make(map[reflect.Value]struct{})
59
60 for i, rev := range lst1 {
61 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
62 changes.KeyMap1[v] = i
63 }
64 for i, rev := range lst2 {
65 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
66 changes.KeyMap2[v] = i
67 }
68 for v, _ := range changes.KeyMap2 {
69 if _, ok := changes.KeyMap1[v]; !ok {
70 changes.AddedKeys[v] = struct{}{}
71 }
72 }
73 for v, _ := range changes.KeyMap1 {
74 if _, ok := changes.KeyMap2[v]; !ok {
75 changes.RemovedKeys[v] = struct{}{}
76 }
77 }
78 for v, _ := range changes.KeyMap1 {
79 if _, ok := changes.KeyMap2[v]; ok && lst1[changes.KeyMap1[v]].GetHash() != lst1[changes.KeyMap2[v]].GetHash() {
80 changes.ChangedKeys[v] = struct{}{}
81 }
82 }
83
84 return changes
85}
86
87func Merge3Way(
88 forkRev, srcRev, dstRev Revision,
89 mergeChildFunc func(Revision) Revision,
90 dryRun bool) (rev Revision, changes map[CallbackType][]interface{}) {
91
92 var configChanged bool
93
94 if dstRev.GetConfig() == forkRev.GetConfig() {
95 configChanged = dstRev.GetConfig() != srcRev.GetConfig()
96 } else {
97 if dstRev.GetConfig().Hash != srcRev.GetConfig().Hash {
98 log.Error("config-collision")
99 }
100 configChanged = true
101 }
102
103 newChildren := reflect.ValueOf(dstRev.GetChildren()).Elem().Interface().(map[string][]Revision)
104 childrenFields := ChildrenFields(forkRev.GetData())
105
106 for fieldName, field := range childrenFields {
107 forkList := forkRev.GetChildren()[fieldName]
108 srcList := srcRev.GetChildren()[fieldName]
109 dstList := dstRev.GetChildren()[fieldName]
110
111 if revisionsAreEqual(forkList, srcList) {
112 for _, rev := range srcList {
113 mergeChildFunc(rev)
114 }
115 continue
116 }
117
118 if field.Key == "" {
119 if revisionsAreEqual(dstList, forkList) {
120 if !revisionsAreEqual(srcList, forkList) {
121 log.Error("we should not be here")
122 } else {
123 for _, rev := range srcList {
124 newChildren[fieldName] = append(newChildren[fieldName], mergeChildFunc(rev))
125 }
126 if field.IsContainer {
127 changes[POST_LISTCHANGE] = append(
128 changes[POST_LISTCHANGE],
129 NewOperationContext("", nil, fieldName, ""),
130 )
131 }
132 }
133 } else {
134 if !revisionsAreEqual(srcList, forkList) {
135 log.Error("cannot merge - single child node or un-keyed children list has changed")
136 }
137 }
138 } else {
139 if revisionsAreEqual(dstList, forkList) {
140 src := newChangeAnalysis(forkList, srcList, field.Key)
141
142 newList := reflect.ValueOf(srcList).Elem().Interface().([]Revision)
143
144 for key, _ := range src.AddedKeys {
145 idx := src.KeyMap1[key]
146 newRev := mergeChildFunc(newList[idx])
147 newList[idx] = newRev
148
149 changes[POST_ADD] = append(
150 changes[POST_ADD],
151 newRev.GetData(),
152 )
153 }
154 for key, _ := range src.RemovedKeys {
155 oldRev := forkList[src.KeyMap1[key]]
156
157 changes[POST_REMOVE] = append(
158 changes[POST_REMOVE],
159 oldRev.GetData(),
160 )
161 }
162 for key, _ := range src.ChangedKeys {
163 idx := src.KeyMap2[key]
164 newRev := mergeChildFunc(newList[idx])
165 newList[idx] = newRev
166 }
167
168 newChildren[fieldName] = newList
169 } else {
170 src := newChangeAnalysis(forkList, srcList, field.Key)
171 dst := newChangeAnalysis(forkList, dstList, field.Key)
172
173 newList := reflect.ValueOf(dstList).Elem().Interface().([]Revision)
174
175 for key, _ := range src.AddedKeys {
176 if _, exists := dst.AddedKeys[key]; exists {
177 childDstRev := dstList[dst.KeyMap2[key]]
178 childSrcRev := srcList[src.KeyMap2[key]]
179 if childDstRev.GetHash() == childSrcRev.GetHash() {
180 mergeChildFunc(childDstRev)
181 } else {
182 log.Error("conflict error - revision has been added is different")
183 }
184 } else {
185 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
186 newList = append(newList, newRev)
187 changes[POST_ADD] = append(
188 changes[POST_ADD],
189 newRev.GetData(),
190 )
191 }
192 }
193 for key, _ := range src.ChangedKeys {
194 if _, removed := dst.RemovedKeys[key]; removed {
195 log.Error("conflict error - revision has been removed")
196 } else if _, changed := dst.ChangedKeys[key]; changed {
197 childDstRev := dstList[dst.KeyMap2[key]]
198 childSrcRev := srcList[src.KeyMap2[key]]
199 if childDstRev.GetHash() == childSrcRev.GetHash() {
200 mergeChildFunc(childSrcRev)
201 } else if childDstRev.GetConfig().Hash != childSrcRev.GetConfig().Hash {
202 log.Error("conflict error - revision has been changed and is different")
203 } else {
204 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
205 newList[dst.KeyMap2[key]] = newRev
206 }
207 } else {
208 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
209 newList[dst.KeyMap2[key]] = newRev
210 }
211 }
212
213 // TODO: how do i sort this map in reverse order?
214 for key, _ := range src.RemovedKeys {
215 if _, changed := dst.ChangedKeys[key]; changed {
216 log.Error("conflict error - revision has changed")
217 }
218 if _, removed := dst.ChangedKeys[key]; !removed {
219 dstIdx := dst.KeyMap2[key]
220 oldRev := newList[dstIdx]
221
222 copy(newList[dstIdx:], newList[dstIdx+1:])
223 newList[len(newList)-1] = nil
224 newList = newList[:len(newList)-1]
225
226 changes[POST_REMOVE] = append(
227 changes[POST_REMOVE],
228 oldRev.GetData(),
229 )
230 }
231 }
232
233 newChildren[fieldName] = newList
234
235 }
236 }
237 }
238
239 if !dryRun {
240 if configChanged {
241 rev = srcRev
242 } else {
243 rev = dstRev
244 }
245
246 rev = rev.UpdateAllChildren(newChildren, dstRev.GetBranch())
247
248 if configChanged {
249 changes[POST_UPDATE] = append(
250 changes[POST_UPDATE],
251 rev.GetData(),
252 )
253 }
254 return rev, changes
255 }
256
257 return nil, nil
258}