1 /* 2 * "GEDKeeper", the personal genealogical database editor. 3 * Copyright (C) 2009-2021 by Sergey V. Zhdanovskih. 4 * 5 * This file is part of "GEDKeeper". 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 using System; 22 using System.Collections.Generic; 23 using System.Threading; 24 using BSLib; 25 using BSLib.DataViz.SmartGraph; 26 using BSLib.Design.MVP.Controls; 27 using GDModel; 28 using GDModel.Providers.GEDCOM; 29 using GKCore.Controllers; 30 using GKCore.Interfaces; 31 using GKCore.Types; 32 33 namespace GKCore.Tools 34 { 35 /// <summary> 36 /// 37 /// </summary> 38 public static class TreeTools 39 { 40 private const string CRLF = "\r\n"; 41 42 43 #region Patriarchs Search 44 PL_SearchAnc(GDMTree tree, GDMIndividualRecord descendant, GDMIndividualRecord searchRec, bool onlyMaleLine)45 public static bool PL_SearchAnc(GDMTree tree, GDMIndividualRecord descendant, GDMIndividualRecord searchRec, bool onlyMaleLine) 46 { 47 if (descendant == null) return false; 48 49 bool res = (descendant == searchRec); 50 51 if (!res && descendant.ChildToFamilyLinks.Count > 0) 52 { 53 GDMFamilyRecord family = tree.GetPtrValue(descendant.ChildToFamilyLinks[0]); 54 55 GDMIndividualRecord ancestor = tree.GetPtrValue(family.Husband); 56 if (ancestor != null) { 57 res = PL_SearchAnc(tree, ancestor, searchRec, onlyMaleLine); 58 if (res) return true; 59 } 60 61 /*if (!onlyMaleLine) { 62 ancestor = family.GetWife(); 63 if (ancestor != null) { 64 res = PL_SearchAnc2(ancestor, searchRec, onlyMaleLine); 65 if (res) return true; 66 } 67 }*/ 68 } 69 70 return res; 71 } 72 73 /// <summary> 74 /// Search of crossing of two individuals. 75 /// </summary> 76 /// <param name="ancestorRec"></param> 77 /// <param name="searchRec"></param> 78 /// <returns>crossing of two individuals</returns> PL_SearchDesc(GDMTree tree, GDMIndividualRecord ancestorRec, GDMIndividualRecord searchRec)79 public static GDMIndividualRecord PL_SearchDesc(GDMTree tree, GDMIndividualRecord ancestorRec, GDMIndividualRecord searchRec) 80 { 81 GDMIndividualRecord cross = null; 82 83 int num = ancestorRec.SpouseToFamilyLinks.Count; 84 for (int i = 0; i < num; i++) { 85 GDMFamilyRecord family = tree.GetPtrValue(ancestorRec.SpouseToFamilyLinks[i]); 86 GDMIndividualRecord spouse = tree.GetSpouseBy(family, ancestorRec); 87 88 if (spouse != null) { 89 bool res = PL_SearchAnc(tree, spouse, searchRec, (ancestorRec.Sex == GDMSex.svFemale)); 90 if (res) { 91 cross = ancestorRec; 92 return cross; 93 } 94 } 95 96 if (ancestorRec.Sex == GDMSex.svMale) { 97 int num2 = family.Children.Count; 98 for (int j = 0; j < num2; j++) { 99 GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]); 100 cross = PL_SearchDesc(tree, child, searchRec); 101 if (cross != null) return cross; 102 } 103 } 104 } 105 106 return null; 107 } 108 PL_SearchIntersection(GDMTree tree, GDMIndividualRecord ancestor, GDMIndividualRecord searchRec)109 public static GDMFamilyRecord PL_SearchIntersection(GDMTree tree, GDMIndividualRecord ancestor, GDMIndividualRecord searchRec) 110 { 111 int num = ancestor.SpouseToFamilyLinks.Count; 112 for (int i = 0; i < num; i++) { 113 GDMFamilyRecord family = tree.GetPtrValue(ancestor.SpouseToFamilyLinks[i]); 114 GDMIndividualRecord spouse = tree.GetSpouseBy(family, ancestor); 115 116 if (spouse != null) { 117 bool res = PL_SearchAnc(tree, spouse, searchRec, (ancestor.Sex == GDMSex.svFemale)); 118 if (res) return family; 119 } 120 121 if (ancestor.Sex == GDMSex.svMale) { 122 int num2 = family.Children.Count; 123 for (int j = 0; j < num2; j++) { 124 GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]); 125 126 GDMFamilyRecord res = PL_SearchIntersection(tree, child, searchRec); 127 if (res != null) return res; 128 } 129 } 130 } 131 132 return null; 133 } 134 GenPatriarchsGraphviz(IBaseWindow baseWin, string outpath, int minGens, bool loneSuppress = true)135 public static void GenPatriarchsGraphviz(IBaseWindow baseWin, string outpath, int minGens, bool loneSuppress = true) 136 { 137 if (baseWin == null) 138 throw new ArgumentNullException("baseWin"); 139 140 string[] options = { "ratio=auto" }; 141 GraphvizWriter gvw = new GraphvizWriter("Family Tree", options); 142 143 using (ExtList<PatriarchObj> patList = PatriarchsMan.GetPatriarchsLinks(baseWin.Context, minGens, false, loneSuppress)) 144 { 145 int num = patList.Count; 146 for (int i = 0; i < num; i++) { 147 PatriarchObj pObj = patList[i]; 148 149 if (!loneSuppress || pObj.HasLinks) { 150 string color = (pObj.IRec.Sex == GDMSex.svFemale) ? "pink" : "blue"; 151 gvw.WriteNode(pObj.IRec.XRef, GKUtils.GetNameString(pObj.IRec, true, false), "filled", color, "box"); 152 } 153 } 154 155 for (int i = 0; i < num; i++) { 156 PatriarchObj pat1 = patList[i]; 157 158 int num2 = pat1.Links.Count; 159 for (int k = 0; k < num2; k++) { 160 PatriarchObj pat2 = pat1.Links[k]; 161 gvw.WriteEdge(pat1.IRec.XRef, pat2.IRec.XRef); 162 } 163 } 164 } 165 166 gvw.SaveFile(outpath); 167 } 168 169 #endregion 170 171 #region Tree Walk 172 173 public enum TreeWalkMode 174 { 175 twmAll, 176 twmFamily, 177 twmAncestors, 178 twmDescendants, 179 twmNone 180 } 181 WalkProc(GDMIndividualRecord iRec, TreeWalkMode mode, object extData)182 public delegate bool WalkProc(GDMIndividualRecord iRec, TreeWalkMode mode, object extData); 183 WalkTree(GDMTree tree, GDMIndividualRecord iRec, TreeWalkMode mode, WalkProc walkProc, object extData)184 public static void WalkTree(GDMTree tree, GDMIndividualRecord iRec, TreeWalkMode mode, WalkProc walkProc, object extData) 185 { 186 if (tree == null) 187 throw new ArgumentNullException("tree"); 188 189 if (iRec == null) 190 throw new ArgumentNullException("iRec"); 191 192 if (walkProc == null) 193 throw new ArgumentNullException("walkProc"); 194 195 if (extData == null) 196 throw new ArgumentNullException("extData"); 197 198 WalkTreeInt(tree, iRec, mode, walkProc, extData); 199 } 200 WalkTree(GDMTree tree, GDMIndividualRecord iRec, TreeWalkMode mode, List<GDMRecord> walkList)201 public static void WalkTree(GDMTree tree, GDMIndividualRecord iRec, TreeWalkMode mode, List<GDMRecord> walkList) 202 { 203 if (tree == null) 204 throw new ArgumentNullException("tree"); 205 206 if (iRec == null) 207 throw new ArgumentNullException("iRec"); 208 209 if (walkList == null) 210 throw new ArgumentNullException("walkList"); 211 212 WalkTreeInt(tree, iRec, mode, DefaultWalkProc, walkList); 213 } 214 DefaultWalkProc(GDMIndividualRecord iRec, TreeWalkMode mode, object extData)215 private static bool DefaultWalkProc(GDMIndividualRecord iRec, TreeWalkMode mode, object extData) 216 { 217 List<GDMRecord> walkList = (List<GDMRecord>)extData; 218 bool resContinue = (iRec != null && !walkList.Contains(iRec)); 219 if (resContinue) { 220 walkList.Add(iRec); 221 } 222 return resContinue; 223 } 224 WalkTreeInt(GDMTree tree, GDMIndividualRecord iRec, TreeWalkMode mode, WalkProc walkProc, object extData)225 private static void WalkTreeInt(GDMTree tree, GDMIndividualRecord iRec, TreeWalkMode mode, WalkProc walkProc, object extData) 226 { 227 if (!walkProc(iRec, mode, extData)) return; 228 229 if (mode == TreeWalkMode.twmNone) return; 230 231 if (mode == TreeWalkMode.twmAll || mode == TreeWalkMode.twmAncestors) { 232 GDMFamilyRecord family = tree.GetParentsFamily(iRec); 233 if (family != null) { 234 GDMIndividualRecord father, mother; 235 tree.GetSpouses(family, out father, out mother); 236 237 WalkTreeInt(tree, father, mode, walkProc, extData); 238 WalkTreeInt(tree, mother, mode, walkProc, extData); 239 } 240 } 241 242 // twmAll, twmFamily, twmDescendants 243 if (mode < TreeWalkMode.twmAncestors || mode == TreeWalkMode.twmDescendants) { 244 int num = iRec.SpouseToFamilyLinks.Count; 245 for (int i = 0; i < num; i++) { 246 GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]); 247 GDMIndividualRecord spouse = (iRec.Sex == GDMSex.svMale) ? tree.GetPtrValue(family.Wife) : tree.GetPtrValue(family.Husband); 248 249 TreeWalkMode intMode = ((mode == TreeWalkMode.twmAll) ? TreeWalkMode.twmAll : TreeWalkMode.twmNone); 250 WalkTreeInt(tree, spouse, intMode, walkProc, extData); 251 252 switch (mode) { 253 case TreeWalkMode.twmAll: 254 intMode = TreeWalkMode.twmAll; 255 break; 256 257 case TreeWalkMode.twmFamily: 258 intMode = TreeWalkMode.twmNone; 259 break; 260 261 case TreeWalkMode.twmDescendants: 262 intMode = TreeWalkMode.twmDescendants; 263 break; 264 } 265 266 int num2 = family.Children.Count; 267 for (int j = 0; j < num2; j++) { 268 GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]); 269 WalkTreeInt(tree, child, intMode, walkProc, extData); 270 } 271 } 272 } 273 } 274 275 #endregion 276 277 #region Detect cycles 278 279 private enum DCFlag { dcfAncWalk, dcfDescWalk } 280 SetIndiFlag(GKVarCache<GDMIndividualRecord, int> indiFlags, GDMIndividualRecord iRec, DCFlag flag)281 private static void SetIndiFlag(GKVarCache<GDMIndividualRecord, int> indiFlags, GDMIndividualRecord iRec, DCFlag flag) 282 { 283 int flags = indiFlags[iRec]; 284 flags = BitHelper.SetBit(flags, (int)flag); 285 indiFlags[iRec] = flags; 286 } 287 HasIndiFlag(GKVarCache<GDMIndividualRecord, int> indiFlags, GDMIndividualRecord iRec, DCFlag flag)288 private static bool HasIndiFlag(GKVarCache<GDMIndividualRecord, int> indiFlags, GDMIndividualRecord iRec, DCFlag flag) 289 { 290 int flags = indiFlags[iRec]; 291 return BitHelper.IsSetBit(flags, (int)flag); 292 } 293 DetectCycleAncestors(GDMTree tree, GDMIndividualRecord iRec, Stack<GDMIndividualRecord> stack, GKVarCache<GDMIndividualRecord, int> indiFlags)294 private static GDMIndividualRecord DetectCycleAncestors(GDMTree tree, GDMIndividualRecord iRec, 295 Stack<GDMIndividualRecord> stack, 296 GKVarCache<GDMIndividualRecord, int> indiFlags) 297 { 298 if (iRec == null) return null; 299 300 if (stack.Contains(iRec)) return iRec; 301 302 SetIndiFlag(indiFlags, iRec, DCFlag.dcfAncWalk); 303 304 stack.Push(iRec); 305 306 GDMFamilyRecord family = tree.GetParentsFamily(iRec); 307 if (family != null) { 308 var res = DetectCycleAncestors(tree, tree.GetPtrValue(family.Husband), stack, indiFlags); 309 if (res != null) return res; 310 311 res = DetectCycleAncestors(tree, tree.GetPtrValue(family.Wife), stack, indiFlags); 312 if (res != null) return res; 313 } 314 315 stack.Pop(); 316 return null; 317 } 318 DetectCycleDescendants(GDMTree tree, GDMIndividualRecord iRec, Stack<GDMIndividualRecord> stack, GKVarCache<GDMIndividualRecord, int> indiFlags)319 private static GDMIndividualRecord DetectCycleDescendants(GDMTree tree, GDMIndividualRecord iRec, 320 Stack<GDMIndividualRecord> stack, 321 GKVarCache<GDMIndividualRecord, int> indiFlags) 322 { 323 if (iRec == null) return null; 324 325 if (stack.Contains(iRec)) return iRec; 326 327 SetIndiFlag(indiFlags, iRec, DCFlag.dcfDescWalk); 328 329 stack.Push(iRec); 330 331 int num = iRec.SpouseToFamilyLinks.Count; 332 for (int i = 0; i < num; i++) { 333 GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]); 334 if (family == null) continue; 335 336 int num2 = family.Children.Count; 337 for (int j = 0; j < num2; j++) { 338 GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]); 339 if (child == null) continue; 340 341 var res = DetectCycleDescendants(tree, child, stack, indiFlags); 342 if (res != null) return res; 343 } 344 } 345 346 stack.Pop(); 347 return null; 348 } 349 DetectCycle(GDMTree tree, GDMIndividualRecord iRec)350 public static string DetectCycle(GDMTree tree, GDMIndividualRecord iRec) 351 { 352 var stack = new Stack<GDMIndividualRecord>(); 353 var indiDCFlags = new GKVarCache<GDMIndividualRecord, int>(); 354 355 var hasCycle = DetectCycleAncestors(tree, iRec, stack, indiDCFlags); 356 if (hasCycle != null) { 357 var lastRec = stack.Pop(); 358 return iRec.XRef + " ... " + lastRec.XRef + " -> " + hasCycle.XRef; 359 } 360 361 stack.Clear(); 362 363 hasCycle = DetectCycleDescendants(tree, iRec, stack, indiDCFlags); 364 if (hasCycle != null) { 365 var lastRec = stack.Pop(); 366 return iRec.XRef + " ... " + lastRec.XRef + " -> " + hasCycle.XRef; 367 } 368 369 return string.Empty; 370 } 371 CheckCycle(GDMTree tree, GDMIndividualRecord iRec)372 private static string CheckCycle(GDMTree tree, GDMIndividualRecord iRec) 373 { 374 var stack = new Stack<GDMIndividualRecord>(); 375 GDMIndividualRecord hasCycle = null; 376 var indiDCFlags = new GKVarCache<GDMIndividualRecord, int>(); 377 378 if (!HasIndiFlag(indiDCFlags, iRec, DCFlag.dcfAncWalk)) { 379 hasCycle = DetectCycleAncestors(tree, iRec, stack, indiDCFlags); 380 if (hasCycle != null) { 381 var lastRec = stack.Pop(); 382 return iRec.XRef + " ... " + lastRec.XRef + " -> " + hasCycle.XRef; 383 } 384 stack.Clear(); 385 } 386 387 if (!HasIndiFlag(indiDCFlags, iRec, DCFlag.dcfDescWalk)) { 388 hasCycle = DetectCycleDescendants(tree, iRec, stack, indiDCFlags); 389 if (hasCycle != null) { 390 var lastRec = stack.Pop(); 391 return iRec.XRef + " ... " + lastRec.XRef + " -> " + hasCycle.XRef; 392 } 393 } 394 395 return string.Empty; 396 } 397 398 #endregion 399 400 #region Merge trees and records 401 MergeTree(GDMTree mainTree, GDMTree extTree, ITextBox logBox, bool selfTest = false)402 public static void MergeTree(GDMTree mainTree, GDMTree extTree, ITextBox logBox, bool selfTest = false) 403 { 404 if (mainTree == null) 405 throw new ArgumentNullException("mainTree"); 406 407 if (extTree == null) 408 throw new ArgumentNullException("extTree"); 409 410 if (logBox != null) { 411 logBox.Clear(); 412 logBox.AppendText(string.Format(LangMan.LS(LSID.LSID_MainBaseSize), mainTree.RecordsCount.ToString()) + CRLF); 413 } 414 415 List<int> fragments = new List<int>(); 416 if (selfTest) { 417 var tmpFrags = TreeTools.SearchTreeFragments(mainTree, null); 418 for (int i = 0; i < tmpFrags.Count; i++) { 419 fragments.Add(tmpFrags[i].Count); 420 } 421 422 tmpFrags = TreeTools.SearchTreeFragments(extTree, null); 423 for (int i = 0; i < tmpFrags.Count; i++) { 424 fragments.Add(tmpFrags[i].Count); 425 } 426 } 427 428 using (var repMap = new GDMXRefReplacer()) { 429 extTree.Header.Clear(); 430 while (extTree.RecordsCount > 0) { 431 GDMRecord rec = extTree.Extract(0); 432 var oldXRef = rec.XRef; 433 var newXRef = mainTree.NewXRef(rec); 434 repMap.AddXRef(rec, oldXRef, newXRef); 435 rec.ResetTree(mainTree); 436 mainTree.AddRecord(rec); 437 } 438 439 for (int i = 0, num = repMap.Count; i < num; i++) { 440 GDMRecord rec = repMap[i].Rec; 441 rec.ReplaceXRefs(repMap); 442 } 443 444 if (logBox != null) { 445 logBox.AppendText(string.Format(LangMan.LS(LSID.LSID_MainBaseSize), mainTree.RecordsCount.ToString()) + CRLF); 446 } 447 } 448 449 if (selfTest) { 450 var tmpFrags = TreeTools.SearchTreeFragments(mainTree, null); 451 if (fragments.Count != tmpFrags.Count) { 452 ThrowError(logBox, "The number of fragments is not as expected."); 453 } 454 for (int i = 0; i < tmpFrags.Count; i++) { 455 if (fragments[i] != tmpFrags[i].Count) { 456 ThrowError(logBox, "The number of persons in the fragment is not as expected."); 457 } 458 } 459 } 460 } 461 ThrowError(ITextBox logBox, string message)462 private static void ThrowError(ITextBox logBox, string message) 463 { 464 if (logBox != null) { 465 logBox.AppendText(message + CRLF); 466 } else { 467 throw new GKException(message); 468 } 469 } 470 MergeTreeFile(GDMTree mainTree, string fileName, ITextBox logBox, bool selfTest = false)471 public static void MergeTreeFile(GDMTree mainTree, string fileName, ITextBox logBox, bool selfTest = false) 472 { 473 if (mainTree == null) 474 throw new ArgumentNullException("mainTree"); 475 476 if (string.IsNullOrEmpty(fileName)) 477 throw new ArgumentNullException("fileName"); 478 479 using (var extTree = new GDMTree()) { 480 var gedcomProvider = new GEDCOMProvider(extTree); 481 gedcomProvider.LoadFromFile(fileName); 482 483 MergeTree(mainTree, extTree, logBox, selfTest); 484 } 485 } 486 MergeRecord(IBaseWindow baseWin, GDMRecord targetRec, GDMRecord sourceRec, bool bookmark)487 public static void MergeRecord(IBaseWindow baseWin, GDMRecord targetRec, GDMRecord sourceRec, bool bookmark) 488 { 489 if (baseWin == null) 490 throw new ArgumentNullException("baseWin"); 491 492 if (targetRec == null) 493 throw new ArgumentNullException("targetRec"); 494 495 if (sourceRec == null) 496 throw new ArgumentNullException("sourceRec"); 497 498 using (var repMap = new GDMXRefReplacer()) { 499 repMap.AddXRef(sourceRec, sourceRec.XRef, targetRec.XRef); 500 501 GDMTree tree = baseWin.Context.Tree; 502 int num = tree.RecordsCount; 503 for (int i = 0; i < num; i++) { 504 tree[i].ReplaceXRefs(repMap); 505 } 506 507 sourceRec.MoveTo(targetRec); 508 bool res = baseWin.Context.DeleteRecord(sourceRec); 509 510 if (targetRec.RecordType == GDMRecordType.rtIndividual && bookmark) { 511 ((GDMIndividualRecord)targetRec).Bookmark = true; 512 } 513 514 baseWin.NotifyRecord(targetRec, RecordAction.raEdit); 515 baseWin.RefreshLists(false); 516 } 517 } 518 519 #endregion 520 521 #region Base Checks 522 523 public enum CheckDiag 524 { 525 cdPersonLonglived, 526 cdPersonSexless, 527 cdLiveYearsInvalid, 528 cdStrangeSpouse, 529 cdStrangeParent, 530 cdEmptyFamily, 531 cdFatherAsChild, 532 cdMotherAsChild, 533 cdDuplicateChildren, 534 csDateInvalid, 535 csCycle, 536 cdChildWithoutParents, 537 cdFamilyRecordWithoutFamily, 538 cdMediaRecordWithoutFiles, 539 cdStgNotFound, 540 cdArcNotFound, 541 cdFileNotFound, 542 } 543 544 public enum CheckSolve 545 { 546 csSkip, 547 csSetIsDead, 548 csDefineSex, 549 csRemove, 550 csEdit, 551 } 552 553 public sealed class CheckObj 554 { 555 public string Comment; 556 public CheckDiag Diag; 557 public GDMRecord Rec; 558 public CheckSolve Solve; 559 CheckObj(GDMRecord rec, CheckDiag diag, CheckSolve solve)560 public CheckObj(GDMRecord rec, CheckDiag diag, CheckSolve solve) 561 { 562 Rec = rec; 563 Diag = diag; 564 Solve = solve; 565 } 566 GetRecordName(GDMTree tree)567 public string GetRecordName(GDMTree tree) 568 { 569 string result = string.Empty; 570 571 switch (Rec.RecordType) { 572 case GDMRecordType.rtIndividual: 573 result = GKUtils.GetNameString(((GDMIndividualRecord)Rec), true, false); 574 break; 575 576 case GDMRecordType.rtFamily: 577 result = GKUtils.GetFamilyString(tree, (GDMFamilyRecord)Rec); 578 break; 579 } 580 581 result = string.Concat(result, " [ ", Rec.XRef, " ]"); 582 583 return result; 584 } 585 } 586 CheckRecordWithEvents(GDMRecordWithEvents rec, List<CheckObj> checksList)587 private static void CheckRecordWithEvents(GDMRecordWithEvents rec, List<CheckObj> checksList) 588 { 589 var dateZero = new DateTime(0); 590 591 int num = rec.Events.Count; 592 for (int i = 0; i < num; i++) { 593 GDMCustomEvent evt = rec.Events[i]; 594 595 bool invalid = false; 596 try { 597 var dtx = evt.Date.GetDateTime(); 598 599 /*if (dtx == dateZero) { 600 invalid = true; 601 }*/ 602 } catch { 603 invalid = true; 604 } 605 606 if (invalid) { 607 CheckObj checkObj = new CheckObj(rec, CheckDiag.csDateInvalid, CheckSolve.csEdit); 608 checkObj.Comment = LangMan.LS(LSID.LSID_DateInvalid) + " (" + evt.Date.StringValue + ")"; 609 checksList.Add(checkObj); 610 } 611 } 612 } 613 CheckIndividualRecord(GDMTree tree, GDMIndividualRecord iRec, List<CheckObj> checksList)614 private static void CheckIndividualRecord(GDMTree tree, GDMIndividualRecord iRec, List<CheckObj> checksList) 615 { 616 CheckRecordWithEvents(iRec, checksList); 617 618 if (iRec.FindEvent(GEDCOMTagType.DEAT) == null) { 619 int age = GKUtils.GetAge(iRec, -1); 620 621 if (age != -1 && age >= GKData.PROVED_LIFE_LENGTH) { 622 CheckObj checkObj = new CheckObj(iRec, CheckDiag.cdPersonLonglived, CheckSolve.csSetIsDead); 623 checkObj.Comment = string.Format(LangMan.LS(LSID.LSID_PersonLonglived), age); 624 checksList.Add(checkObj); 625 } 626 } 627 628 GDMSex sex = iRec.Sex; 629 if (sex < GDMSex.svMale || sex > GDMSex.svFemale) { 630 CheckObj checkObj = new CheckObj(iRec, CheckDiag.cdPersonSexless, CheckSolve.csDefineSex); 631 checkObj.Comment = LangMan.LS(LSID.LSID_PersonSexless); 632 checksList.Add(checkObj); 633 } 634 635 int yBirth = iRec.GetChronologicalYear(GEDCOMTagName.BIRT); 636 int yDeath = iRec.GetChronologicalYear(GEDCOMTagName.DEAT); 637 if (yBirth != 0 && yDeath != 0) { 638 int delta = (yDeath - yBirth); 639 if (delta < 0) { 640 CheckObj checkObj = new CheckObj(iRec, CheckDiag.cdLiveYearsInvalid, CheckSolve.csSkip); 641 checkObj.Comment = LangMan.LS(LSID.LSID_LiveYearsInvalid); 642 checksList.Add(checkObj); 643 } 644 } 645 646 int iAge = GKUtils.GetMarriageAge(tree, iRec); 647 if (iAge > 0 && (iAge <= 13 || iAge >= 50)) { 648 CheckObj checkObj = new CheckObj(iRec, CheckDiag.cdStrangeSpouse, CheckSolve.csSkip); 649 checkObj.Comment = string.Format(LangMan.LS(LSID.LSID_StrangeSpouse), iAge.ToString()); 650 checksList.Add(checkObj); 651 } 652 653 iAge = GKUtils.GetFirstbornAge(iRec, GKUtils.GetFirstborn(tree, iRec)); 654 if (iAge > 0 && (iAge <= 13 || iAge >= 50)) { 655 CheckObj checkObj = new CheckObj(iRec, CheckDiag.cdStrangeParent, CheckSolve.csSkip); 656 checkObj.Comment = string.Format(LangMan.LS(LSID.LSID_StrangeParent), iAge.ToString()); 657 checksList.Add(checkObj); 658 } 659 660 string cycle = CheckCycle(tree, iRec); 661 if (!string.IsNullOrEmpty(cycle)) { 662 CheckObj checkObj = new CheckObj(iRec, CheckDiag.csCycle, CheckSolve.csSkip); 663 checkObj.Comment = string.Format(LangMan.LS(LSID.LSID_DetectedDataLoop), cycle); 664 checksList.Add(checkObj); 665 } 666 } 667 CheckFamilyRecord(GDMTree tree, GDMFamilyRecord fRec, List<CheckObj> checksList)668 private static void CheckFamilyRecord(GDMTree tree, GDMFamilyRecord fRec, List<CheckObj> checksList) 669 { 670 CheckRecordWithEvents(fRec, checksList); 671 672 var husb = tree.GetPtrValue<GDMIndividualRecord>(fRec.Husband); 673 var wife = tree.GetPtrValue<GDMIndividualRecord>(fRec.Wife); 674 675 bool empty = (!fRec.HasNotes && !fRec.HasSourceCitations && !fRec.HasMultimediaLinks && !fRec.HasUserReferences); 676 empty = empty && (!fRec.HasEvents && fRec.Children.Count == 0); 677 empty = empty && (husb == null && wife == null); 678 679 if (empty) { 680 CheckObj checkObj = new CheckObj(fRec, CheckDiag.cdEmptyFamily, CheckSolve.csRemove); 681 checkObj.Comment = LangMan.LS(LSID.LSID_EmptyFamily); 682 checksList.Add(checkObj); 683 } else { 684 int chNum = fRec.Children.Count; 685 686 if (husb == null && wife == null) { 687 if (chNum > 0) { 688 CheckObj checkObj = new CheckObj(fRec, CheckDiag.cdChildWithoutParents, CheckSolve.csSkip); 689 checkObj.Comment = LangMan.LS(LSID.LSID_ChildWithoutParents); 690 checksList.Add(checkObj); 691 } 692 else { 693 CheckObj checkObj = new CheckObj(fRec, CheckDiag.cdFamilyRecordWithoutFamily, CheckSolve.csSkip); 694 checkObj.Comment = LangMan.LS(LSID.LSID_FamilyRecordWithoutFamily); 695 checksList.Add(checkObj); 696 } 697 } 698 else { 699 if (fRec.IndexOfChild(husb) >= 0) { 700 CheckObj checkObj = new CheckObj(fRec, CheckDiag.cdFatherAsChild, CheckSolve.csRemove); 701 checkObj.Comment = LangMan.LS(LSID.LSID_FatherAsChild); 702 checksList.Add(checkObj); 703 } 704 705 if (fRec.IndexOfChild(wife) >= 0) { 706 CheckObj checkObj = new CheckObj(fRec, CheckDiag.cdMotherAsChild, CheckSolve.csRemove); 707 checkObj.Comment = LangMan.LS(LSID.LSID_MotherAsChild); 708 checksList.Add(checkObj); 709 } 710 } 711 712 bool hasDup = false; 713 for (int i = 0; i < chNum; i++) { 714 var child1 = fRec.Children[i]; 715 for (int k = i + 1; k < chNum; k++) { 716 var child2 = fRec.Children[k]; 717 if (child2.XRef == child1.XRef) { 718 hasDup = true; 719 break; 720 } 721 } 722 if (hasDup) break; 723 } 724 if (hasDup) { 725 CheckObj checkObj = new CheckObj(fRec, CheckDiag.cdDuplicateChildren, CheckSolve.csEdit); 726 checkObj.Comment = LangMan.LS(LSID.LSID_DuplicateChildrenInFamily); 727 checksList.Add(checkObj); 728 } 729 } 730 } 731 732 private static bool StgNotFound; 733 private static bool ArcNotFound; 734 CheckMultimediaRecord(IBaseContext baseContext, GDMMultimediaRecord mmRec, List<CheckObj> checksList)735 private static void CheckMultimediaRecord(IBaseContext baseContext, GDMMultimediaRecord mmRec, List<CheckObj> checksList) 736 { 737 if (mmRec.FileReferences.Count <= 0) { 738 CheckObj checkObj = new CheckObj(mmRec, CheckDiag.cdMediaRecordWithoutFiles, CheckSolve.csRemove); 739 checkObj.Comment = LangMan.LS(LSID.LSID_MediaRecordWithoutFiles); 740 checksList.Add(checkObj); 741 } 742 743 string fileName; 744 MediaStoreStatus storeStatus = baseContext.VerifyMediaFile(mmRec.FileReferences[0], out fileName); 745 746 switch (storeStatus) { 747 case MediaStoreStatus.mssExists: 748 break; 749 750 case MediaStoreStatus.mssFileNotFound: 751 { 752 CheckObj checkObj = new CheckObj(mmRec, CheckDiag.cdFileNotFound, CheckSolve.csSkip); 753 checkObj.Comment = LangMan.LS(LSID.LSID_FileNotFound, fileName); 754 checksList.Add(checkObj); 755 } 756 break; 757 758 case MediaStoreStatus.mssStgNotFound: 759 if (!StgNotFound) { 760 CheckObj checkObj = new CheckObj(mmRec, CheckDiag.cdStgNotFound, CheckSolve.csSkip); 761 checkObj.Comment = LangMan.LS(LSID.LSID_StgNotFound); 762 checksList.Add(checkObj); 763 StgNotFound = true; 764 } 765 break; 766 767 case MediaStoreStatus.mssArcNotFound: 768 if (!ArcNotFound) { 769 CheckObj checkObj = new CheckObj(mmRec, CheckDiag.cdArcNotFound, CheckSolve.csSkip); 770 checkObj.Comment = LangMan.LS(LSID.LSID_ArcNotFound); 771 checksList.Add(checkObj); 772 ArcNotFound = true; 773 } 774 break; 775 776 case MediaStoreStatus.mssBadData: 777 // TODO: can be deleted? 778 break; 779 } 780 } 781 CheckBase(IBaseWindow baseWin, List<CheckObj> checksList)782 public static void CheckBase(IBaseWindow baseWin, List<CheckObj> checksList) 783 { 784 if (baseWin == null) 785 throw new ArgumentNullException("baseWin"); 786 787 if (checksList == null) 788 throw new ArgumentNullException("checksList"); 789 790 StgNotFound = false; 791 ArcNotFound = false; 792 793 IProgressController progress = AppHost.Progress; 794 try { 795 GDMTree tree = baseWin.Context.Tree; 796 progress.ProgressInit(LangMan.LS(LSID.LSID_ToolOp_7), tree.RecordsCount); 797 checksList.Clear(); 798 799 for (int i = 0, num = tree.RecordsCount; i < num; i++) { 800 progress.ProgressStep(); 801 802 GDMRecord rec = tree[i]; 803 switch (rec.RecordType) { 804 case GDMRecordType.rtIndividual: 805 CheckIndividualRecord(tree, rec as GDMIndividualRecord, checksList); 806 break; 807 808 case GDMRecordType.rtFamily: 809 CheckFamilyRecord(tree, rec as GDMFamilyRecord, checksList); 810 break; 811 812 case GDMRecordType.rtMultimedia: 813 CheckMultimediaRecord(baseWin.Context, rec as GDMMultimediaRecord, checksList); 814 break; 815 } 816 } 817 } finally { 818 progress.ProgressDone(); 819 } 820 } 821 RepairProblem(IBaseWindow baseWin, CheckObj checkObj)822 public static void RepairProblem(IBaseWindow baseWin, CheckObj checkObj) 823 { 824 if (baseWin == null) 825 throw new ArgumentNullException("baseWin"); 826 827 if (checkObj == null) 828 throw new ArgumentNullException("checkObj"); 829 830 GDMTree tree = baseWin.Context.Tree; 831 GDMIndividualRecord iRec; 832 833 switch (checkObj.Diag) { 834 case CheckDiag.cdPersonLonglived: 835 iRec = checkObj.Rec as GDMIndividualRecord; 836 baseWin.Context.CreateEventEx(iRec, GEDCOMTagName.DEAT, "", ""); 837 baseWin.NotifyRecord(iRec, RecordAction.raEdit); 838 break; 839 840 case CheckDiag.cdPersonSexless: 841 iRec = checkObj.Rec as GDMIndividualRecord; 842 baseWin.Context.CheckPersonSex(iRec); 843 baseWin.NotifyRecord(iRec, RecordAction.raEdit); 844 break; 845 846 case CheckDiag.cdEmptyFamily: 847 tree.DeleteRecord(checkObj.Rec); 848 break; 849 850 case CheckDiag.cdFatherAsChild: 851 { 852 var fRec = ((GDMFamilyRecord)checkObj.Rec); 853 fRec.DeleteChild(fRec.Husband); 854 } 855 break; 856 857 case CheckDiag.cdMotherAsChild: 858 { 859 var fRec = ((GDMFamilyRecord)checkObj.Rec); 860 fRec.DeleteChild(fRec.Wife); 861 } 862 break; 863 864 case CheckDiag.cdDuplicateChildren: 865 if (checkObj.Solve == CheckSolve.csEdit) { 866 BaseController.EditRecord(baseWin, checkObj.Rec); 867 } 868 break; 869 870 case CheckDiag.csDateInvalid: 871 if (checkObj.Solve == CheckSolve.csEdit) { 872 BaseController.EditRecord(baseWin, checkObj.Rec); 873 } 874 break; 875 } 876 } 877 878 #endregion 879 880 #region Tree Split 881 CheckRelations_AddRel(List<GDMRecord> splitList, GDMRecord rec)882 private static void CheckRelations_AddRel(List<GDMRecord> splitList, GDMRecord rec) 883 { 884 if (rec != null && splitList.IndexOf(rec) < 0) { 885 splitList.Add(rec); 886 } 887 } 888 CheckRelations_CheckNotes(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithNotes tag)889 private static void CheckRelations_CheckNotes(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithNotes tag) 890 { 891 if (tag == null || !tag.HasNotes) return; 892 893 for (int i = 0, num = tag.Notes.Count; i < num; i++) { 894 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(tag.Notes[i])); 895 } 896 } 897 CheckRelations_CheckSourceCit(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithSourceCitations tag)898 private static void CheckRelations_CheckSourceCit(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithSourceCitations tag) 899 { 900 if (tag == null || !tag.HasSourceCitations) return; 901 902 for (int i = 0, num = tag.SourceCitations.Count; i < num; i++) { 903 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(tag.SourceCitations[i])); 904 } 905 } 906 CheckRelations_CheckMediaLink(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithMultimediaLinks tag)907 private static void CheckRelations_CheckMediaLink(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithMultimediaLinks tag) 908 { 909 if (tag == null || !tag.HasMultimediaLinks) return; 910 911 for (int i = 0, num = tag.MultimediaLinks.Count; i < num; i++) { 912 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(tag.MultimediaLinks[i])); 913 } 914 } 915 CheckRelations_CheckSWL(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithLists tag)916 private static void CheckRelations_CheckSWL(GDMTree tree, List<GDMRecord> splitList, IGDMStructWithLists tag) 917 { 918 if (tag == null) return; 919 920 CheckRelations_CheckNotes(tree, splitList, tag); 921 CheckRelations_CheckSourceCit(tree, splitList, tag); 922 CheckRelations_CheckMediaLink(tree, splitList, tag); 923 } 924 CheckRelations_CheckRecord(GDMTree tree, List<GDMRecord> splitList, GDMRecord rec)925 private static void CheckRelations_CheckRecord(GDMTree tree, List<GDMRecord> splitList, GDMRecord rec) 926 { 927 if (rec == null) return; 928 929 CheckRelations_CheckSWL(tree, splitList, rec); 930 } 931 CheckRelations_CheckIndividual(GDMTree tree, List<GDMRecord> splitList, GDMIndividualRecord iRec)932 private static void CheckRelations_CheckIndividual(GDMTree tree, List<GDMRecord> splitList, GDMIndividualRecord iRec) 933 { 934 if (iRec == null) return; 935 936 CheckRelations_CheckRecord(tree, splitList, iRec); 937 938 for (int i = 0, num = iRec.ChildToFamilyLinks.Count; i < num; i++) { 939 var cfl = iRec.ChildToFamilyLinks[i]; 940 CheckRelations_CheckNotes(tree, splitList, cfl); 941 CheckRelations_CheckFamily(tree, splitList, tree.GetPtrValue(cfl)); 942 } 943 944 for (int i = 0, num = iRec.SpouseToFamilyLinks.Count; i < num; i++) { 945 var sfl = iRec.SpouseToFamilyLinks[i]; 946 CheckRelations_CheckNotes(tree, splitList, sfl); 947 CheckRelations_CheckFamily(tree, splitList, tree.GetPtrValue(sfl)); 948 } 949 950 if (iRec.HasEvents) { 951 for (int i = 0, num = iRec.Events.Count; i < num; i++) { 952 CheckRelations_CheckSWL(tree, splitList, iRec.Events[i]); 953 } 954 } 955 956 if (iRec.HasAssociations) { 957 for (int i = 0, num = iRec.Associations.Count; i < num; i++) { 958 var asso = iRec.Associations[i]; 959 CheckRelations_CheckNotes(tree, splitList, asso); 960 CheckRelations_CheckSourceCit(tree, splitList, asso); 961 CheckRelations_CheckIndividual(tree, splitList, tree.GetPtrValue(asso)); 962 } 963 } 964 965 if (iRec.HasGroups) { 966 for (int i = 0, num = iRec.Groups.Count; i < num; i++) { 967 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(iRec.Groups[i])); 968 } 969 } 970 } 971 CheckRelations_CheckFamily(GDMTree tree, List<GDMRecord> splitList, GDMFamilyRecord fRec)972 private static void CheckRelations_CheckFamily(GDMTree tree, List<GDMRecord> splitList, GDMFamilyRecord fRec) 973 { 974 if (fRec == null) return; 975 976 CheckRelations_CheckRecord(tree, splitList, fRec); 977 978 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(fRec.Husband)); 979 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(fRec.Wife)); 980 981 for (int i = 0, num = fRec.Children.Count; i < num; i++) { 982 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(fRec.Children[i])); 983 } 984 985 if (fRec.HasEvents) { 986 for (int i = 0, num = fRec.Events.Count; i < num; i++) { 987 CheckRelations_CheckSWL(tree, splitList, fRec.Events[i]); 988 } 989 } 990 } 991 CheckRelations_CheckSource(GDMTree tree, List<GDMRecord> splitList, GDMSourceRecord sRec)992 private static void CheckRelations_CheckSource(GDMTree tree, List<GDMRecord> splitList, GDMSourceRecord sRec) 993 { 994 if (sRec == null) return; 995 996 CheckRelations_CheckRecord(tree, splitList, sRec); 997 998 for (int i = 0, num = sRec.RepositoryCitations.Count; i < num; i++) { 999 CheckRelations_CheckRecord(tree, splitList, tree.GetPtrValue<GDMRecord>(sRec.RepositoryCitations[i])); 1000 } 1001 } 1002 CheckRelations(GDMTree tree, List<GDMRecord> splitList)1003 public static void CheckRelations(GDMTree tree, List<GDMRecord> splitList) 1004 { 1005 if (splitList == null) 1006 throw new ArgumentNullException("splitList"); 1007 1008 int num = splitList.Count; 1009 for (int i = 0; i < num; i++) { 1010 GDMRecord rec = splitList[i]; 1011 switch (rec.RecordType) { 1012 case GDMRecordType.rtIndividual: 1013 CheckRelations_CheckIndividual(tree, splitList, rec as GDMIndividualRecord); 1014 break; 1015 1016 case GDMRecordType.rtFamily: 1017 CheckRelations_CheckFamily(tree, splitList, rec as GDMFamilyRecord); 1018 break; 1019 1020 case GDMRecordType.rtNote: 1021 CheckRelations_CheckRecord(tree, splitList, rec); 1022 break; 1023 1024 case GDMRecordType.rtMultimedia: 1025 CheckRelations_CheckRecord(tree, splitList, rec); 1026 break; 1027 1028 case GDMRecordType.rtSource: 1029 CheckRelations_CheckSource(tree, splitList, rec as GDMSourceRecord); 1030 break; 1031 1032 case GDMRecordType.rtRepository: 1033 CheckRelations_CheckRecord(tree, splitList, rec); 1034 break; 1035 1036 case GDMRecordType.rtSubmitter: 1037 CheckRelations_CheckRecord(tree, splitList, rec); 1038 break; 1039 } 1040 } 1041 } 1042 1043 #endregion 1044 1045 #region Tree Compare 1046 1047 public class IndividualRecordComparer: IComparer<ULIndividual> 1048 { Compare(ULIndividual x, ULIndividual y)1049 public int Compare(ULIndividual x, ULIndividual y) 1050 { 1051 return string.Compare(x.Family, y.Family, false); 1052 } 1053 } 1054 1055 public class ULIndividual 1056 { 1057 public string Family; 1058 public GDMIndividualRecord IRec; 1059 } 1060 GetUnlinkedNamesakes(IBaseWindow baseWin)1061 public static List<ULIndividual> GetUnlinkedNamesakes(IBaseWindow baseWin) 1062 { 1063 if (baseWin == null) 1064 throw new ArgumentNullException("baseWin"); 1065 1066 GDMTree tree = baseWin.Context.Tree; 1067 1068 List<ULIndividual> result = new List<ULIndividual>(); 1069 1070 Dictionary<string, List<GDMIndividualRecord>> families = new Dictionary<string, List<GDMIndividualRecord>>(); 1071 1072 IProgressController progress = AppHost.Progress; 1073 progress.ProgressInit(LangMan.LS(LSID.LSID_Stage) + "1", tree.RecordsCount, true); 1074 1075 // make a table of surnames and persons, related to these surnames 1076 int num = tree.RecordsCount; 1077 for (int i = 0; i < num; i++) { 1078 GDMRecord rec = tree[i]; 1079 1080 if (rec.RecordType == GDMRecordType.rtIndividual) { 1081 GDMIndividualRecord iRec = (GDMIndividualRecord)rec; 1082 1083 string[] fams = baseWin.Context.Culture.GetSurnames(iRec); 1084 1085 for (int k = 0; k < fams.Length; k++) { 1086 string f = fams[k]; 1087 if (f.Length > 1) { 1088 List<GDMIndividualRecord> ps; 1089 if (!families.TryGetValue(f, out ps)) { 1090 ps = new List<GDMIndividualRecord>(); 1091 families.Add(f, ps); 1092 } 1093 ps.Add(iRec); 1094 } 1095 } 1096 } 1097 1098 if (progress.IsCanceled) break; 1099 progress.ProgressStep(); 1100 } 1101 1102 progress.ProgressInit(LangMan.LS(LSID.LSID_Stage) + "2", families.Count, true); 1103 1104 // find all persons of one surname, not related by ties of kinship 1105 foreach (KeyValuePair<string, List<GDMIndividualRecord>> entry in families) { 1106 string fam = entry.Key; 1107 List<GDMIndividualRecord> ps = entry.Value; 1108 1109 int i = 0; 1110 while (i < ps.Count) { 1111 GDMIndividualRecord iRec = ps[i]; 1112 1113 List<GDMRecord> lst = new List<GDMRecord>(); 1114 WalkTree(tree, iRec, TreeWalkMode.twmAll, lst); 1115 1116 int num3 = lst.Count; 1117 for (int k = 0; k < num3; k++) { 1118 GDMIndividualRecord item = lst[k] as GDMIndividualRecord; 1119 1120 int idx = ps.IndexOf(item); 1121 if (item != iRec && idx >= 0 && idx > i) ps.RemoveAt(idx); 1122 } 1123 1124 i++; 1125 } 1126 1127 int num2 = ps.Count; 1128 for (i = 0; i < num2; i++) { 1129 ULIndividual indiv = new ULIndividual(); 1130 indiv.Family = fam; 1131 indiv.IRec = ps[i]; 1132 result.Add(indiv); 1133 } 1134 1135 if (progress.IsCanceled) break; 1136 progress.ProgressStep(); 1137 } 1138 1139 result.Sort(new IndividualRecordComparer()); 1140 1141 progress.ProgressDone(); 1142 1143 return result; 1144 } 1145 DuplicateFoundFunc(GDMIndividualRecord indivA, GDMIndividualRecord indivB)1146 public delegate void DuplicateFoundFunc(GDMIndividualRecord indivA, GDMIndividualRecord indivB); 1147 FindDuplicates(GDMTree treeA, GDMTree treeB, float matchThreshold, DuplicateFoundFunc foundFunc, IProgressController pc)1148 public static void FindDuplicates(GDMTree treeA, GDMTree treeB, float matchThreshold, 1149 DuplicateFoundFunc foundFunc, IProgressController pc) 1150 { 1151 if (treeA == null) 1152 throw new ArgumentNullException("treeA"); 1153 1154 if (treeB == null) 1155 throw new ArgumentNullException("treeB"); 1156 1157 if (foundFunc == null) 1158 throw new ArgumentNullException("foundFunc"); 1159 1160 if (pc == null) 1161 throw new ArgumentNullException("pc"); 1162 1163 MatchParams mParams; 1164 //mParams.IndistinctMatching = true; 1165 mParams.NamesIndistinctThreshold = 90.0f / 100.0f; 1166 mParams.DatesCheck = true; 1167 mParams.YearsInaccuracy = 3; 1168 mParams.CheckEventPlaces = false; 1169 1170 pc.ProgressInit(LangMan.LS(LSID.LSID_DuplicatesSearch), treeA.RecordsCount, true); 1171 try { 1172 for (int i = 0; i < treeA.RecordsCount; i++) { 1173 GDMRecord recA = treeA[i]; 1174 if (recA.RecordType == GDMRecordType.rtIndividual) { 1175 for (int k = 0; k < treeB.RecordsCount; k++) { 1176 GDMRecord recB = treeB[k]; 1177 if (recB.RecordType == GDMRecordType.rtIndividual) { 1178 GDMIndividualRecord indivA = (GDMIndividualRecord)recA; 1179 GDMIndividualRecord indivB = (GDMIndividualRecord)recB; 1180 1181 if (indivA != indivB && indivA.IsMatch(indivB, mParams) >= matchThreshold) { 1182 foundFunc(indivA, indivB); 1183 } 1184 } 1185 } 1186 } 1187 1188 if (pc.IsCanceled) break; 1189 pc.ProgressStep(); 1190 Thread.Sleep(1); 1191 } 1192 } finally { 1193 pc.ProgressDone(); 1194 } 1195 } 1196 CompareTree(IBaseContext context, string fileName, ITextBox logBox)1197 public static void CompareTree(IBaseContext context, string fileName, ITextBox logBox) 1198 { 1199 if (context == null) 1200 throw new ArgumentNullException("context"); 1201 1202 if (logBox == null) 1203 throw new ArgumentNullException("logBox"); 1204 1205 using (var tempTree = new GDMTree()) { 1206 var gedcomProvider = new GEDCOMProvider(tempTree); 1207 gedcomProvider.LoadFromFile(fileName); 1208 1209 CompareTree(context, tempTree, logBox); 1210 } 1211 } 1212 CompareTree(IBaseContext context, GDMTree tempTree, ITextBox logBox)1213 public static void CompareTree(IBaseContext context, GDMTree tempTree, ITextBox logBox) 1214 { 1215 if (context == null) 1216 throw new ArgumentNullException("context"); 1217 1218 if (tempTree == null) 1219 throw new ArgumentNullException("tempTree"); 1220 1221 if (logBox == null) 1222 throw new ArgumentNullException("logBox"); 1223 1224 GDMTree mainTree = context.Tree; 1225 1226 StringList fams = new StringList(); 1227 StringList names = new StringList(); 1228 1229 try { 1230 logBox.AppendText(LangMan.LS(LSID.LSID_SearchMatches) + CRLF); 1231 1232 int mainCount = mainTree.RecordsCount; 1233 for (int i = 0; i < mainCount; i++) { 1234 GDMIndividualRecord iRec = mainTree[i] as GDMIndividualRecord; 1235 if (iRec != null) { 1236 int idx = names.AddObject(GKUtils.GetNameString(iRec, true, false), new ExtList<GDMIndividualRecord>()); 1237 ((ExtList<GDMIndividualRecord>)names.GetObject(idx)).Add(iRec); 1238 1239 var parts = GKUtils.GetNameParts(mainTree, iRec); 1240 fams.AddObject(context.Culture.NormalizeSurname(parts.Surname, iRec.Sex == GDMSex.svFemale), null); 1241 } 1242 } 1243 1244 int tempCount = tempTree.RecordsCount; 1245 for (int i = 0; i < tempCount; i++) { 1246 GDMIndividualRecord iRec = tempTree[i] as GDMIndividualRecord; 1247 if (iRec != null) { 1248 string tm = GKUtils.GetNameString(iRec, true, false); 1249 int idx = names.IndexOf(tm); 1250 if (idx >= 0) { 1251 ((ExtList<GDMIndividualRecord>)names.GetObject(idx)).Add(iRec); 1252 } 1253 1254 var parts = GKUtils.GetNameParts(tempTree, iRec); 1255 tm = context.Culture.NormalizeSurname(parts.Surname, iRec.Sex == GDMSex.svFemale); 1256 idx = fams.IndexOf(tm); 1257 if (idx >= 0) { 1258 fams.SetObject(idx, 1); 1259 } 1260 } 1261 } 1262 1263 for (int i = fams.Count - 1; i >= 0; i--) { 1264 if (fams.GetObject(i) == null || fams[i] == "?") 1265 fams.Delete(i); 1266 } 1267 1268 for (int i = names.Count - 1; i >= 0; i--) { 1269 ExtList<GDMIndividualRecord> lst = (ExtList<GDMIndividualRecord>)names.GetObject(i); 1270 1271 if (lst.Count == 1) { 1272 lst.Dispose(); 1273 names.Delete(i); 1274 } 1275 } 1276 1277 int famsCount = fams.Count; 1278 if (famsCount != 0) { 1279 logBox.AppendText(LangMan.LS(LSID.LSID_SimilarSurnames) + CRLF); 1280 for (int i = 0; i < famsCount; i++) { 1281 logBox.AppendText(" " + fams[i] + CRLF); 1282 } 1283 } 1284 1285 int namesCount = names.Count; 1286 if (namesCount != 0) { 1287 logBox.AppendText(LangMan.LS(LSID.LSID_SimilarNames) + CRLF); 1288 for (int i = 0; i < namesCount; i++) { 1289 logBox.AppendText(" " + names[i] + CRLF); 1290 ExtList<GDMIndividualRecord> lst = (ExtList<GDMIndividualRecord>)names.GetObject(i); 1291 1292 int num5 = lst.Count; 1293 for (int j = 0; j < num5; j++) { 1294 GDMIndividualRecord iRec = lst[j]; 1295 logBox.AppendText(" * " + GKUtils.GetNameString(iRec, true, false) + " " + GKUtils.GetLifeStr(iRec) + CRLF); 1296 } 1297 } 1298 } 1299 1300 if (famsCount == 0 && namesCount == 0) { 1301 logBox.AppendText(LangMan.LS(LSID.LSID_MatchesNotFound) + CRLF); 1302 } 1303 } finally { 1304 int namesCount = names.Count; 1305 for (int i = 0; i < namesCount; i++) { 1306 IDisposable inst = names.GetObject(i) as IDisposable; 1307 if (inst != null) inst.Dispose(); 1308 } 1309 names.Dispose(); 1310 1311 fams.Dispose(); 1312 } 1313 } 1314 1315 #endregion 1316 1317 #region Places Management 1318 SearchPlaces_Clear(StringList placesList)1319 public static void SearchPlaces_Clear(StringList placesList) 1320 { 1321 if (placesList == null) 1322 throw new ArgumentNullException("placesList"); 1323 1324 for (int i = placesList.Count - 1; i >= 0; i--) ((PlaceObj)placesList.GetObject(i)).Dispose(); 1325 placesList.Clear(); 1326 } 1327 SearchPlaces_CheckEventPlace(GDMTree tree, StringList placesList, GDMCustomEvent evt, bool checkLocation)1328 private static void SearchPlaces_CheckEventPlace(GDMTree tree, StringList placesList, GDMCustomEvent evt, bool checkLocation) 1329 { 1330 if (!evt.HasPlace) return; 1331 string placeStr = evt.Place.StringValue; 1332 if (string.IsNullOrEmpty(placeStr)) return; 1333 1334 if (checkLocation) { 1335 var locRec = tree.GetPtrValue<GDMLocationRecord>(evt.Place.Location); 1336 if (locRec != null) { 1337 placeStr = "[*] " + placeStr; 1338 } 1339 } 1340 1341 int idx = placesList.IndexOf(placeStr); 1342 1343 PlaceObj placeObj; 1344 if (idx >= 0) { 1345 placeObj = (PlaceObj)placesList.GetObject(idx); 1346 } else { 1347 placeObj = new PlaceObj(placeStr); 1348 placesList.AddObject(placeStr, placeObj); 1349 } 1350 placeObj.Facts.Add(evt); 1351 } 1352 SearchPlaces(GDMTree tree, StringList placesList, IProgressController pc, bool checkLocation = true)1353 public static void SearchPlaces(GDMTree tree, StringList placesList, IProgressController pc, bool checkLocation = true) 1354 { 1355 if (tree == null) 1356 throw new ArgumentNullException("tree"); 1357 1358 if (placesList == null) 1359 throw new ArgumentNullException("placesList"); 1360 1361 if (pc == null) 1362 throw new ArgumentNullException("pc"); 1363 1364 SearchPlaces_Clear(placesList); 1365 1366 try { 1367 int recsCount = tree.RecordsCount; 1368 pc.ProgressInit(LangMan.LS(LSID.LSID_PlacesPrepare), recsCount); 1369 1370 for (int i = 0; i < recsCount; i++) { 1371 pc.ProgressStep(); 1372 1373 var evsRec = tree[i] as GDMRecordWithEvents; 1374 if (evsRec != null && evsRec.HasEvents) { 1375 int num2 = evsRec.Events.Count; 1376 for (int j = 0; j < num2; j++) { 1377 GDMCustomEvent evt = evsRec.Events[j]; 1378 1379 SearchPlaces_CheckEventPlace(tree, placesList, evt, checkLocation); 1380 } 1381 } 1382 } 1383 } finally { 1384 pc.ProgressDone(); 1385 } 1386 } 1387 1388 #endregion 1389 1390 #region Tree fragments 1391 SearchTreeFragments(GDMTree tree, IProgressController progress)1392 public static List<List<GDMRecord>> SearchTreeFragments(GDMTree tree, IProgressController progress) 1393 { 1394 List<List<GDMRecord>> result = new List<List<GDMRecord>>(); 1395 1396 if (progress != null) { 1397 progress.ProgressInit(LangMan.LS(LSID.LSID_CheckFamiliesConnection), tree.RecordsCount); 1398 } 1399 1400 List<GDMRecord> prepared = new List<GDMRecord>(); 1401 try { 1402 int num = tree.RecordsCount; 1403 for (int i = 0; i < num; i++) { 1404 GDMIndividualRecord iRec = tree[i] as GDMIndividualRecord; 1405 if (iRec != null) { 1406 if (prepared.IndexOf(iRec) < 0) { 1407 var groupRecords = new List<GDMRecord>(); 1408 WalkTree(tree, iRec, TreeWalkMode.twmAll, groupRecords); 1409 result.Add(groupRecords); 1410 prepared.AddRange(groupRecords); 1411 } 1412 } 1413 1414 if (progress != null) { 1415 progress.ProgressStep(); 1416 } 1417 } 1418 } finally { 1419 prepared.Clear(); 1420 1421 if (progress != null) { 1422 progress.ProgressDone(); 1423 } 1424 } 1425 1426 return result; 1427 } 1428 1429 #endregion 1430 } 1431 } 1432