internal void ReorderEntries(PwObjectPool ppOrgStructure, PwObjectPool ppSrcStructure) { GroupHandler gh = delegate(PwGroup pg) { ReorderObjectList<PwEntry>(pg.Entries, ppOrgStructure, ppSrcStructure, true); return true; }; ReorderObjectList<PwEntry>(m_pgRootGroup.Entries, ppOrgStructure, ppSrcStructure, true); m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, null); }
internal void RelocateGroups(PwObjectPool ppOrgStructure, PwObjectPool ppSrcStructure) { PwObjectList<PwGroup> vGroups = m_pgRootGroup.GetGroups(true); foreach(PwGroup pg in vGroups) { if((m_slStatus != null) && !m_slStatus.ContinueWork()) break; // PwGroup pgOrg = pgOrgStructure.FindGroup(pg.Uuid, true); IStructureItem ptOrg = ppOrgStructure.Get(pg.Uuid); if(ptOrg == null) continue; // PwGroup pgSrc = pgSrcStructure.FindGroup(pg.Uuid, true); IStructureItem ptSrc = ppSrcStructure.Get(pg.Uuid); if(ptSrc == null) continue; PwGroup pgOrgParent = ptOrg.ParentGroup; PwGroup pgSrcParent = ptSrc.ParentGroup; if(pgOrgParent.Uuid.EqualsValue(pgSrcParent.Uuid)) { pg.LocationChanged = ((ptSrc.LocationChanged > ptOrg.LocationChanged) ? ptSrc.LocationChanged : ptOrg.LocationChanged); continue; } if(ptSrc.LocationChanged > ptOrg.LocationChanged) { PwGroup pgLocal = m_pgRootGroup.FindGroup(pgSrcParent.Uuid, true); if(pgLocal == null) { Debug.Assert(false); continue; } if(pgLocal.IsContainedIn(pg)) continue; pg.ParentGroup.Groups.Remove(pg); pgLocal.AddGroup(pg, true); pg.LocationChanged = ptSrc.LocationChanged; } else { Debug.Assert(pg.ParentGroup.Uuid.EqualsValue(pgOrgParent.Uuid)); Debug.Assert(pg.LocationChanged == ptOrg.LocationChanged); } } Debug.Assert(m_pgRootGroup.GetGroups(true).UCount == vGroups.UCount); }
/// <summary> /// Method to check whether a reordering is required. This fast test /// allows to skip the reordering routine, resulting in a large /// performance increase. /// </summary> internal bool ObjectListRequiresReorder<T>(PwObjectList<T> vItems, PwObjectPool ppOrgStructure, PwObjectPool ppSrcStructure, bool bEntries) where T : class, IStructureItem, IDeepClonable<T> { Debug.Assert(ppOrgStructure.ContainsOnlyType(bEntries ? typeof(PwEntry) : typeof(PwGroup))); Debug.Assert(ppSrcStructure.ContainsOnlyType(bEntries ? typeof(PwEntry) : typeof(PwGroup))); if(vItems.UCount <= 1) return false; if((m_slStatus != null) && !m_slStatus.ContinueWork()) return false; T ptFirst = vItems.GetAt(0); // IStructureItem ptOrg = pgOrgStructure.FindObject(ptFirst.Uuid, true, bEntries); IStructureItem ptOrg = ppOrgStructure.Get(ptFirst.Uuid); if(ptOrg == null) return true; // IStructureItem ptSrc = pgSrcStructure.FindObject(ptFirst.Uuid, true, bEntries); IStructureItem ptSrc = ppSrcStructure.Get(ptFirst.Uuid); if(ptSrc == null) return true; if(ptFirst.ParentGroup == null) { Debug.Assert(false); return true; } PwGroup pgOrgParent = ptOrg.ParentGroup; if(pgOrgParent == null) return true; // Root might be in tree PwGroup pgSrcParent = ptSrc.ParentGroup; if(pgSrcParent == null) return true; // Root might be in tree if(!ptFirst.ParentGroup.Uuid.EqualsValue(pgOrgParent.Uuid)) return true; if(!pgOrgParent.Uuid.EqualsValue(pgSrcParent.Uuid)) return true; List<IStructureItem> lOrg = pgOrgParent.GetObjects(false, bEntries); List<IStructureItem> lSrc = pgSrcParent.GetObjects(false, bEntries); if(vItems.UCount != (uint)lOrg.Count) return true; if(lOrg.Count != lSrc.Count) return true; for(uint u = 0; u < vItems.UCount; ++u) { IStructureItem pt = vItems.GetAt(u); Debug.Assert(pt.ParentGroup == ptFirst.ParentGroup); if(!pt.Uuid.EqualsValue(lOrg[(int)u].Uuid)) return true; if(!pt.Uuid.EqualsValue(lSrc[(int)u].Uuid)) return true; if(pt.LocationChanged != lOrg[(int)u].LocationChanged) return true; if(pt.LocationChanged != lSrc[(int)u].LocationChanged) return true; } return false; }
internal void RelocateEntries(PwObjectPool ppOrgStructure, PwObjectPool ppSrcStructure) { PwObjectList<PwEntry> vEntries = m_pgRootGroup.GetEntries(true); foreach(PwEntry pe in vEntries) { if((m_slStatus != null) && !m_slStatus.ContinueWork()) break; // PwEntry peOrg = pgOrgStructure.FindEntry(pe.Uuid, true); IStructureItem ptOrg = ppOrgStructure.Get(pe.Uuid); if(ptOrg == null) continue; // PwEntry peSrc = pgSrcStructure.FindEntry(pe.Uuid, true); IStructureItem ptSrc = ppSrcStructure.Get(pe.Uuid); if(ptSrc == null) continue; PwGroup pgOrg = ptOrg.ParentGroup; PwGroup pgSrc = ptSrc.ParentGroup; if(pgOrg.Uuid.EqualsValue(pgSrc.Uuid)) { pe.LocationChanged = ((ptSrc.LocationChanged > ptOrg.LocationChanged) ? ptSrc.LocationChanged : ptOrg.LocationChanged); continue; } if(ptSrc.LocationChanged > ptOrg.LocationChanged) { PwGroup pgLocal = m_pgRootGroup.FindGroup(pgSrc.Uuid, true); if(pgLocal == null) { Debug.Assert(false); continue; } pe.ParentGroup.Entries.Remove(pe); pgLocal.AddEntry(pe, true); pe.LocationChanged = ptSrc.LocationChanged; } else { Debug.Assert(pe.ParentGroup.Uuid.EqualsValue(pgOrg.Uuid)); Debug.Assert(pe.LocationChanged == ptOrg.LocationChanged); } } Debug.Assert(m_pgRootGroup.GetEntries(true).UCount == vEntries.UCount); }
internal static uint FindLocationChangedPivot<T>(PwObjectList<T> vItems, KeyValuePair<uint, uint> kvpRange, PwObjectPool ppOrgStructure, PwObjectPool ppSrcStructure, Queue<PwUuid> qBefore, Queue<PwUuid> qAfter, bool bEntries) where T : class, IStructureItem, IDeepClonable<T> { uint uPosMax = kvpRange.Key; DateTime dtMax = DateTime.MinValue; List<IStructureItem> vNeighborSrc = null; for(uint u = kvpRange.Key; u <= kvpRange.Value; ++u) { T pt = vItems.GetAt(u); // IStructureItem ptOrg = pgOrgStructure.FindObject(pt.Uuid, true, bEntries); IStructureItem ptOrg = ppOrgStructure.Get(pt.Uuid); if((ptOrg != null) && (ptOrg.LocationChanged > dtMax)) { uPosMax = u; dtMax = ptOrg.LocationChanged; // No 'continue' vNeighborSrc = ptOrg.ParentGroup.GetObjects(false, bEntries); } // IStructureItem ptSrc = pgSrcStructure.FindObject(pt.Uuid, true, bEntries); IStructureItem ptSrc = ppSrcStructure.Get(pt.Uuid); if((ptSrc != null) && (ptSrc.LocationChanged > dtMax)) { uPosMax = u; dtMax = ptSrc.LocationChanged; // No 'continue' vNeighborSrc = ptSrc.ParentGroup.GetObjects(false, bEntries); } } GetNeighborItems(vNeighborSrc, vItems.GetAt(uPosMax).Uuid, qBefore, qAfter); return uPosMax; }
internal void ReorderObjectList<T>(PwObjectList<T> vItems, PwObjectPool ppOrgStructure, PwObjectPool ppSrcStructure, bool bEntries) where T : class, IStructureItem, IDeepClonable<T> { if(!ObjectListRequiresReorder<T>(vItems, ppOrgStructure, ppSrcStructure, bEntries)) return; #if DEBUG PwObjectList<T> vOrgListItems = vItems.CloneShallow(); #endif Queue<KeyValuePair<uint, uint>> qToDo = new Queue<KeyValuePair<uint, uint>>(); qToDo.Enqueue(new KeyValuePair<uint, uint>(0, vItems.UCount - 1)); while(qToDo.Count > 0) { if((m_slStatus != null) && !m_slStatus.ContinueWork()) break; KeyValuePair<uint, uint> kvp = qToDo.Dequeue(); if(kvp.Value <= kvp.Key) { Debug.Assert(false); continue; } Queue<PwUuid> qRelBefore = new Queue<PwUuid>(); Queue<PwUuid> qRelAfter = new Queue<PwUuid>(); uint uPivot = FindLocationChangedPivot<T>(vItems, kvp, ppOrgStructure, ppSrcStructure, qRelBefore, qRelAfter, bEntries); T ptPivot = vItems.GetAt(uPivot); List<T> vToSort = vItems.GetRange(kvp.Key, kvp.Value); Queue<T> qBefore = new Queue<T>(); Queue<T> qAfter = new Queue<T>(); bool bBefore = true; foreach(T pt in vToSort) { if(pt == ptPivot) { bBefore = false; continue; } bool bAdded = false; foreach(PwUuid puBefore in qRelBefore) { if(puBefore.EqualsValue(pt.Uuid)) { qBefore.Enqueue(pt); bAdded = true; break; } } if(bAdded) continue; foreach(PwUuid puAfter in qRelAfter) { if(puAfter.EqualsValue(pt.Uuid)) { qAfter.Enqueue(pt); bAdded = true; break; } } if(bAdded) continue; if(bBefore) qBefore.Enqueue(pt); else qAfter.Enqueue(pt); } Debug.Assert(bBefore == false); uint uPos = kvp.Key; while(qBefore.Count > 0) vItems.SetAt(uPos++, qBefore.Dequeue()); vItems.SetAt(uPos++, ptPivot); while(qAfter.Count > 0) vItems.SetAt(uPos++, qAfter.Dequeue()); Debug.Assert(uPos == (kvp.Value + 1)); int iNewPivot = vItems.IndexOf(ptPivot); if((iNewPivot < (int)kvp.Key) || (iNewPivot > (int)kvp.Value)) { Debug.Assert(false); continue; } if((iNewPivot - 1) > (int)kvp.Key) qToDo.Enqueue(new KeyValuePair<uint, uint>(kvp.Key, (uint)(iNewPivot - 1))); if((iNewPivot + 1) < (int)kvp.Value) qToDo.Enqueue(new KeyValuePair<uint, uint>((uint)(iNewPivot + 1), kvp.Value)); } #if DEBUG foreach(T ptItem in vOrgListItems) { Debug.Assert(vItems.IndexOf(ptItem) >= 0); } #endif }
/// <summary> /// Synchronize the current database with another one. /// </summary> /// <param name="pwSource">Input database to synchronize with. This input /// database is used to update the current one, but is not modified! You /// must copy the current object if you want a second instance of the /// synchronized database. The input database must not be seen as valid /// database any more after calling <c>Synchronize</c>.</param> /// <param name="mm">Merge method.</param> /// <param name="slStatus">Logger to report status messages to. /// May be <c>null</c>.</param> public void MergeIn(PwDatabase pwSource, PwMergeMethod mm, IStatusLogger slStatus) { if(pwSource == null) throw new ArgumentNullException("pwSource"); PwGroup pgOrgStructure = m_pgRootGroup.CloneStructure(); PwGroup pgSrcStructure = pwSource.m_pgRootGroup.CloneStructure(); if(mm == PwMergeMethod.CreateNewUuids) pwSource.RootGroup.CreateNewItemUuids(true, true, true); GroupHandler gh = delegate(PwGroup pg) { if(pg == pwSource.m_pgRootGroup) return true; PwGroup pgLocal = m_pgRootGroup.FindGroup(pg.Uuid, true); if(pgLocal == null) { PwGroup pgSourceParent = pg.ParentGroup; PwGroup pgLocalContainer; if(pgSourceParent == pwSource.m_pgRootGroup) pgLocalContainer = m_pgRootGroup; else pgLocalContainer = m_pgRootGroup.FindGroup(pgSourceParent.Uuid, true); Debug.Assert(pgLocalContainer != null); if(pgLocalContainer == null) pgLocalContainer = m_pgRootGroup; PwGroup pgNew = new PwGroup(false, false); pgNew.Uuid = pg.Uuid; pgNew.AssignProperties(pg, false, true); pgLocalContainer.AddGroup(pgNew, true); } else // pgLocal != null { Debug.Assert(mm != PwMergeMethod.CreateNewUuids); if(mm == PwMergeMethod.OverwriteExisting) pgLocal.AssignProperties(pg, false, false); else if((mm == PwMergeMethod.OverwriteIfNewer) || (mm == PwMergeMethod.Synchronize)) { pgLocal.AssignProperties(pg, true, false); } // else if(mm == PwMergeMethod.KeepExisting) ... } return ((slStatus != null) ? slStatus.ContinueWork() : true); }; EntryHandler eh = delegate(PwEntry pe) { PwEntry peLocal = m_pgRootGroup.FindEntry(pe.Uuid, true); if(peLocal == null) { PwGroup pgSourceParent = pe.ParentGroup; PwGroup pgLocalContainer; if(pgSourceParent == pwSource.m_pgRootGroup) pgLocalContainer = m_pgRootGroup; else pgLocalContainer = m_pgRootGroup.FindGroup(pgSourceParent.Uuid, true); Debug.Assert(pgLocalContainer != null); if(pgLocalContainer == null) pgLocalContainer = m_pgRootGroup; PwEntry peNew = new PwEntry(false, false); peNew.Uuid = pe.Uuid; peNew.AssignProperties(pe, false, true, true); pgLocalContainer.AddEntry(peNew, true); } else // peLocal != null { Debug.Assert(mm != PwMergeMethod.CreateNewUuids); bool bEquals = peLocal.EqualsEntry(pe, true, false, true, true, false); bool bOrgBackup = !bEquals; if(mm != PwMergeMethod.OverwriteExisting) bOrgBackup &= (pe.LastModificationTime > peLocal.LastModificationTime); bOrgBackup &= !pe.HasBackupOfData(peLocal, false, true); if(bOrgBackup) peLocal.CreateBackup(); bool bSrcBackup = !bEquals && (mm != PwMergeMethod.OverwriteExisting); bSrcBackup &= (peLocal.LastModificationTime > pe.LastModificationTime); bSrcBackup &= !peLocal.HasBackupOfData(pe, false, true); if(bSrcBackup) pe.CreateBackup(); if(mm == PwMergeMethod.OverwriteExisting) peLocal.AssignProperties(pe, false, false, false); else if((mm == PwMergeMethod.OverwriteIfNewer) || (mm == PwMergeMethod.Synchronize)) { peLocal.AssignProperties(pe, true, false, false); } // else if(mm == PwMergeMethod.KeepExisting) ... MergeEntryHistory(peLocal, pe, mm); } return ((slStatus != null) ? slStatus.ContinueWork() : true); }; if(!pwSource.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh)) throw new InvalidOperationException(); IStatusLogger slPrevStatus = m_slStatus; m_slStatus = slStatus; if(mm == PwMergeMethod.Synchronize) { ApplyDeletions(pwSource.m_vDeletedObjects, true); ApplyDeletions(m_vDeletedObjects, false); PwObjectPool ppOrgGroups = PwObjectPool.FromGroupRecursive( pgOrgStructure, false); PwObjectPool ppSrcGroups = PwObjectPool.FromGroupRecursive( pgSrcStructure, false); PwObjectPool ppOrgEntries = PwObjectPool.FromGroupRecursive( pgOrgStructure, true); PwObjectPool ppSrcEntries = PwObjectPool.FromGroupRecursive( pgSrcStructure, true); RelocateGroups(ppOrgGroups, ppSrcGroups); ReorderGroups(ppOrgGroups, ppSrcGroups); RelocateEntries(ppOrgEntries, ppSrcEntries); ReorderEntries(ppOrgEntries, ppSrcEntries); Debug.Assert(ValidateUuidUniqueness()); } // Must be called *after* merging groups, because group UUIDs // are required for recycle bin and entry template UUIDs MergeInDbProperties(pwSource, mm); MergeInCustomIcons(pwSource); m_slStatus = slPrevStatus; }