|
14 | 14 | using Xtensive.Reflection; |
15 | 15 | using Xtensive.Modelling.Attributes; |
16 | 16 | using Xtensive.Modelling.Comparison.Hints; |
| 17 | +using Xtensive.Orm; |
17 | 18 |
|
18 | 19 |
|
19 | 20 | namespace Xtensive.Modelling.Comparison |
@@ -149,21 +150,27 @@ protected virtual Difference VisitNode(Node source, Node target) |
149 | 150 | { |
150 | 151 | using (TryActivate(source, target, (s, t) => new NodeDifference(s, t))) { |
151 | 152 | IgnoreHint ignoreHint = null; |
152 | | - if (source!=null) |
| 153 | + if (source!=null) { |
153 | 154 | ignoreHint = Hints.GetHint<IgnoreHint>(source); |
154 | | - if (ignoreHint!=null) |
| 155 | + } |
| 156 | + |
| 157 | + if (ignoreHint!=null) { |
155 | 158 | return null; |
| 159 | + } |
156 | 160 |
|
157 | 161 | var context = Context; |
158 | 162 | var difference = (NodeDifference) context.Difference; |
159 | | - if (difference==null) |
| 163 | + if (difference==null) { |
160 | 164 | throw new NullReferenceException(); |
| 165 | + } |
| 166 | + |
161 | 167 | var any = source ?? target; |
162 | | - if (any==null) |
| 168 | + if (any==null) { |
163 | 169 | throw Exceptions.InternalError(Strings.ExBothSourceAndTargetAreNull, CoreLog.Instance); |
| 170 | + } |
164 | 171 |
|
165 | 172 |
|
166 | | - bool isNewDifference = TryRegisterDifference(source, target, difference); |
| 173 | + var isNewDifference = TryRegisterDifference(source, target, difference); |
167 | 174 | if (isNewDifference) { |
168 | 175 | // Build movement info |
169 | 176 | difference.MovementInfo = BuildMovementInfo(source, target); |
@@ -353,68 +360,74 @@ protected IEnumerable<NodeDifference> GetPropertyDifferences(NodeDifference diff |
353 | 360 | /// <exception cref="NullReferenceException">Current difference is not <see cref="NodeCollectionDifference"/>.</exception> |
354 | 361 | protected virtual Difference VisitNodeCollection(NodeCollection source, NodeCollection target) |
355 | 362 | { |
356 | | - using (TryActivate(source, target, (s,t) => new NodeCollectionDifference(s,t))) { |
| 363 | + using (TryActivate(source, target, (s, t) => new NodeCollectionDifference(s, t))) { |
357 | 364 | var context = Context; |
358 | 365 | var difference = (NodeCollectionDifference) context.Difference; |
359 | | - if (difference==null) |
| 366 | + if (difference == null) { |
360 | 367 | throw new NullReferenceException(); |
| 368 | + } |
361 | 369 |
|
362 | | - bool isNewDifference = TryRegisterDifference(source, target, difference); |
| 370 | + TryRegisterDifference(source, target, difference); |
363 | 371 | difference.ItemChanges.Clear(); |
364 | 372 |
|
365 | | - // Inlining 2 below lines leads to error in PEVerify.exe! |
366 | | - // (well-known issue with null coalescing operator + cast) |
367 | | - var sourceItems = (IEnumerable) source; |
368 | | - var targetItems = (IEnumerable) target; |
| 373 | + if (source?.Count == 0 && target?.Count == 0) { |
| 374 | + return null; |
| 375 | + } |
| 376 | + |
| 377 | + var sourceSize = source?.Count ?? 0; |
| 378 | + var sourceKeyMap = new Dictionary<string, Node>(sourceSize, StringComparer.OrdinalIgnoreCase); |
| 379 | + for (var index = sourceSize; index-- > 0;) { |
| 380 | + var node = source[index]; |
| 381 | + sourceKeyMap.Add(GetNodeComparisonKey(node), node); |
| 382 | + } |
369 | 383 |
|
370 | | - var src = sourceItems ?? new ReadOnlyList<Node>(new Node[] {}); |
371 | | - var tgt = targetItems ?? new ReadOnlyList<Node>(new Node[] {}); |
| 384 | + var targetSize = target?.Count ?? 0; |
| 385 | + var targetKeyMap = new Dictionary<string, Node>(targetSize, StringComparer.OrdinalIgnoreCase); |
| 386 | + for (var index = targetSize; index-- > 0;) { |
| 387 | + var node = target[index]; |
| 388 | + targetKeyMap.Add(GetNodeComparisonKey(node), node); |
| 389 | + } |
372 | 390 |
|
373 | | - var srcCount = source!=null ? source.Count : 0; |
374 | | - var tgtCount = target!=null ? target.Count : 0; |
| 391 | + foreach (var sourceItem in sourceKeyMap) { |
| 392 | + if (!targetKeyMap.ContainsKey(sourceItem.Key)) { |
| 393 | + var d = Visit(sourceKeyMap[sourceItem.Key], null); |
| 394 | + if (d != null) { |
| 395 | + difference.ItemChanges.Add((NodeDifference) d); |
| 396 | + } |
| 397 | + } |
| 398 | + } |
375 | 399 |
|
376 | | - if (srcCount==0 && tgtCount==0) |
377 | | - return null; |
378 | | - var someItems = srcCount!=0 ? src : tgt; |
379 | | - var someItem = someItems.Cast<Node>().First(); |
380 | | - |
381 | | - Func<Node, Pair<Node, string>> keyExtractor = |
382 | | - n => new Pair<Node, string>(n, GetNodeComparisonKey(n)); |
383 | | - |
384 | | - var sourceKeyMap = src |
385 | | - .Cast<Node>() |
386 | | - .Select(keyExtractor) |
387 | | - .ToDictionary(pair => pair.Second, pair => pair.First, StringComparer.OrdinalIgnoreCase); |
388 | | - var targetKeyMap = tgt |
389 | | - .Cast<Node>() |
390 | | - .Select(keyExtractor) |
391 | | - .ToDictionary(pair => pair.Second, pair => pair.First, StringComparer.OrdinalIgnoreCase); |
392 | | - |
393 | | - var sourceKeys = src.Cast<Node>().Select(n => keyExtractor(n).Second); |
394 | | - var targetKeys = tgt.Cast<Node>().Select(n => keyExtractor(n).Second); |
395 | | - var commonKeys = sourceKeys.Intersect(targetKeys, StringComparer.OrdinalIgnoreCase); |
396 | | - |
397 | | - var sequence = |
398 | | - sourceKeys.Except(commonKeys, StringComparer.OrdinalIgnoreCase) |
399 | | - .Select(k => new {Index = sourceKeyMap[k].Index, Type = 0, |
400 | | - Source = sourceKeyMap[k], Target = (Node) null}) |
401 | | - .Concat(commonKeys |
402 | | - .Select(k => new {Index = targetKeyMap[k].Index, Type = 1, |
403 | | - Source = sourceKeyMap[k], Target = targetKeyMap[k]})) |
404 | | - .Concat(targetKeys.Except(commonKeys, StringComparer.OrdinalIgnoreCase) |
405 | | - .Select(k => new {Index = targetKeyMap[k].Index, Type = 2, |
406 | | - Source = (Node) null, Target = targetKeyMap[k]})) |
407 | | - .OrderBy(i => i.Type!=0).ThenBy(i => i.Index).ThenBy(i => i.Type); |
408 | | - |
409 | | - foreach (var i in sequence) { |
410 | | - var d = Visit(i.Source, i.Target); |
411 | | - if (d!=null) |
| 400 | + foreach (var targetItem in targetKeyMap) { |
| 401 | + var (s, t) = sourceKeyMap.ContainsKey(targetItem.Key) |
| 402 | + ? (sourceKeyMap[targetItem.Key], targetKeyMap[targetItem.Key]) |
| 403 | + : (null, targetKeyMap[targetItem.Key]); |
| 404 | + var d = Visit(s, t); |
| 405 | + if (d != null) { |
412 | 406 | difference.ItemChanges.Add((NodeDifference) d); |
| 407 | + } |
| 408 | + |
413 | 409 | } |
| 410 | + difference.ItemChanges.Sort(CompareNodeDifference); |
414 | 411 |
|
415 | | - return (difference.ItemChanges.Count!=0) ? difference : null; |
| 412 | + return difference.ItemChanges.Count != 0 ? difference : null; |
416 | 413 | } |
417 | 414 | } |
| 415 | + |
| 416 | + // Sort by items only with source, then by (target ?? source).Index then with source and target and then only with target |
| 417 | + private static int CompareNodeDifference(NodeDifference curr, NodeDifference other) |
| 418 | + { |
| 419 | + var currType = curr.Source != null && curr.Target != null ? 1 : curr.Source == null ? 3 : 0; |
| 420 | + var otherType = other.Source != null && other.Target != null ? 1 : other.Source == null ? 3 : 0; |
| 421 | + var typeIsNot0Comparison = (currType != 0).CompareTo(otherType != 0); |
| 422 | + if (typeIsNot0Comparison != 0) { |
| 423 | + return typeIsNot0Comparison; |
| 424 | + } |
| 425 | + |
| 426 | + var currIndex = (curr.Target ?? curr.Source)?.Index ?? 0; |
| 427 | + var otherIndex = (other.Target ?? other.Source)?.Index ?? 0; |
| 428 | + var indexComparison = currIndex.CompareTo(otherIndex); |
| 429 | + return indexComparison != 0 ? indexComparison : currType.CompareTo(otherType); |
| 430 | + } |
418 | 431 |
|
419 | 432 | /// <summary> |
420 | 433 | /// Visits specified objects. |
|
0 commit comments