| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // Copyright 2019 Yunion
- //
- // 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 compare
- import (
- "fmt"
- "reflect"
- "sort"
- "strings"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- )
- type valueElement struct {
- key string
- value reflect.Value
- }
- type valueSet []valueElement
- func (v valueSet) Len() int {
- return len(v)
- }
- func (v valueSet) Swap(i, j int) {
- v[i], v[j] = v[j], v[i]
- }
- func (v valueSet) Less(i, j int) bool {
- return strings.Compare(v[i].key, v[j].key) < 0
- }
- func valueSet2Array(dbSet interface{}, field string) ([]valueElement, error) {
- dbSetValue := reflect.Indirect(reflect.ValueOf(dbSet))
- if dbSetValue.Kind() != reflect.Slice {
- return nil, fmt.Errorf("input set is not a slice")
- }
- ret := make([]valueElement, dbSetValue.Len())
- for i := 0; i < dbSetValue.Len(); i += 1 {
- val := dbSetValue.Index(i)
- // log.Debugf("valueSet2Array %d %s", i, val)
- funcValue := val.MethodByName(field)
- if !funcValue.IsValid() || funcValue.IsNil() {
- return nil, fmt.Errorf("no such method %s", field)
- }
- outVals := funcValue.Call([]reflect.Value{})
- if len(outVals) != 1 {
- return nil, fmt.Errorf("invalid return value, not 1 string")
- }
- keyVal, ok := outVals[0].Interface().(string)
- if !ok {
- return nil, fmt.Errorf("invalid output value for %s", field)
- }
- ret[i] = valueElement{value: dbSetValue.Index(i), key: keyVal}
- }
- return ret, nil
- }
- type SCompareSet struct {
- DBFunc string
- DBSet interface{}
- ExtFunc string
- ExtSet interface{}
- }
- func CompareSetsFunc(cs SCompareSet, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}, duplicated interface{}) error {
- dbSetArray, err := valueSet2Array(cs.DBSet, cs.DBFunc)
- if err != nil {
- return err
- }
- extSetArray, err := valueSet2Array(cs.ExtSet, cs.ExtFunc)
- if err != nil {
- return err
- }
- sort.Sort(valueSet(dbSetArray))
- dupCheck := map[string][]int{}
- for i := range extSetArray {
- _, ok := dupCheck[extSetArray[i].key]
- if !ok {
- dupCheck[extSetArray[i].key] = []int{}
- }
- dupCheck[extSetArray[i].key] = append(dupCheck[extSetArray[i].key], i)
- }
- var dupValue reflect.Value
- storeDup := false
- if duplicated != nil {
- storeDup = true
- dupValue = reflect.Indirect(reflect.ValueOf(duplicated))
- }
- errs := make([]error, 0)
- newExtSetArray := make([]valueElement, 0)
- duplicateMap := map[string]bool{}
- for k, idx := range dupCheck {
- if len(idx) > 1 {
- if !storeDup {
- log.Warningf("CompareSets Duplicate ID: %s (%d)", k, len(idx))
- errs = append(errs, errors.Wrapf(errors.ErrDuplicateId, "duplicated id: %s (%d)", k, len(idx)))
- } else {
- // store in dupValue
- dupArrays := reflect.MakeSlice(reflect.SliceOf(extSetArray[idx[0]].value.Type()), len(idx), len(idx))
- for i := 0; i < len(idx); i++ {
- dupArrays.Index(i).Set(extSetArray[idx[i]].value)
- }
- dupValue.SetMapIndex(reflect.ValueOf(k), dupArrays)
- duplicateMap[k] = true
- }
- } else {
- newExtSetArray = append(newExtSetArray, extSetArray[idx[0]])
- }
- }
- if len(errs) > 0 {
- return errors.NewAggregate(errs)
- }
- extSetArray = newExtSetArray
- sort.Sort(valueSet(extSetArray))
- removedValue := reflect.Indirect(reflect.ValueOf(removed))
- commonDBValue := reflect.Indirect(reflect.ValueOf(commonDB))
- commonExtValue := reflect.Indirect(reflect.ValueOf(commonExt))
- addedValue := reflect.Indirect(reflect.ValueOf(added))
- i := 0
- j := 0
- for i < len(dbSetArray) || j < len(extSetArray) {
- if i < len(dbSetArray) && j < len(extSetArray) {
- cmp := strings.Compare(dbSetArray[i].key, extSetArray[j].key)
- if cmp == 0 {
- newVal1 := reflect.Append(commonDBValue, dbSetArray[i].value)
- commonDBValue.Set(newVal1)
- newVal2 := reflect.Append(commonExtValue, extSetArray[j].value)
- commonExtValue.Set(newVal2)
- i += 1
- j += 1
- } else if cmp < 0 {
- if _, ok := duplicateMap[dbSetArray[i].key]; !ok && len(dbSetArray[i].key) > 0 {
- newVal := reflect.Append(removedValue, dbSetArray[i].value)
- removedValue.Set(newVal)
- }
- i += 1
- } else {
- newVal := reflect.Append(addedValue, extSetArray[j].value)
- addedValue.Set(newVal)
- j += 1
- }
- } else if i >= len(dbSetArray) {
- newVal := reflect.Append(addedValue, extSetArray[j].value)
- addedValue.Set(newVal)
- j += 1
- } else if j >= len(extSetArray) {
- if _, ok := duplicateMap[dbSetArray[i].key]; !ok && len(dbSetArray[i].key) > 0 {
- newVal := reflect.Append(removedValue, dbSetArray[i].value)
- removedValue.Set(newVal)
- }
- i += 1
- }
- }
- return nil
- }
- func CompareSets(dbSet interface{}, extSet interface{}, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}) error {
- return CompareSets2(dbSet, extSet, removed, commonDB, commonExt, added, nil)
- }
- func CompareSets2(dbSet interface{}, extSet interface{}, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}, duplicated interface{}) error {
- return CompareSetsFunc(SCompareSet{
- DBFunc: "GetExternalId",
- DBSet: dbSet,
- ExtFunc: "GetGlobalId",
- ExtSet: extSet,
- }, removed, commonDB, commonExt, added, duplicated)
- }
|