/* KeePass Password Safe - The Open-Source Password Manager Copyright (C) 2003-2020 Dominik Reichl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using ModernKeePassLib.Interfaces; namespace ModernKeePassLib.Collections { /// /// List of objects that implement IDeepCloneable, /// and cannot be null. /// /// Type specifier. public sealed class PwObjectList : IEnumerable where T : class, IDeepCloneable { private List m_vObjects = new List(); /// /// Get number of objects in this list. /// public uint UCount { get { return (uint)m_vObjects.Count; } } /// /// Construct a new list of objects. /// public PwObjectList() { } IEnumerator IEnumerable.GetEnumerator() { return m_vObjects.GetEnumerator(); } public IEnumerator GetEnumerator() { return m_vObjects.GetEnumerator(); } public void Clear() { // Do not destroy contained objects! m_vObjects.Clear(); } /// /// Clone the current PwObjectList, including all /// stored objects (deep copy). /// /// New PwObjectList. public PwObjectList CloneDeep() { PwObjectList pl = new PwObjectList(); foreach(T po in m_vObjects) pl.Add(po.CloneDeep()); return pl; } public PwObjectList CloneShallow() { PwObjectList tNew = new PwObjectList(); foreach(T po in m_vObjects) tNew.Add(po); return tNew; } public List CloneShallowToList() { PwObjectList tNew = CloneShallow(); return tNew.m_vObjects; } /// /// Add an object to this list. /// /// Object to be added. /// Thrown if the input /// parameter is null. public void Add(T pwObject) { Debug.Assert(pwObject != null); if(pwObject == null) throw new ArgumentNullException("pwObject"); m_vObjects.Add(pwObject); } public void Add(PwObjectList vObjects) { Debug.Assert(vObjects != null); if(vObjects == null) throw new ArgumentNullException("vObjects"); foreach(T po in vObjects) { m_vObjects.Add(po); } } public void Add(List vObjects) { Debug.Assert(vObjects != null); if(vObjects == null) throw new ArgumentNullException("vObjects"); foreach(T po in vObjects) { m_vObjects.Add(po); } } public void Insert(uint uIndex, T pwObject) { Debug.Assert(pwObject != null); if(pwObject == null) throw new ArgumentNullException("pwObject"); m_vObjects.Insert((int)uIndex, pwObject); } /// /// Get an object of the list. /// /// Index of the object to get. Must be valid, otherwise an /// exception is thrown. /// Reference to an existing T object. Is never null. public T GetAt(uint uIndex) { Debug.Assert(uIndex < m_vObjects.Count); if(uIndex >= m_vObjects.Count) throw new ArgumentOutOfRangeException("uIndex"); return m_vObjects[(int)uIndex]; } public void SetAt(uint uIndex, T pwObject) { Debug.Assert(pwObject != null); if(pwObject == null) throw new ArgumentNullException("pwObject"); if(uIndex >= (uint)m_vObjects.Count) throw new ArgumentOutOfRangeException("uIndex"); m_vObjects[(int)uIndex] = pwObject; } /// /// Get a range of objects. /// /// Index of the first object to be /// returned (inclusive). /// Index of the last object to be /// returned (inclusive). /// public List GetRange(uint uStartIndexIncl, uint uEndIndexIncl) { if(uStartIndexIncl >= (uint)m_vObjects.Count) throw new ArgumentOutOfRangeException("uStartIndexIncl"); if(uEndIndexIncl >= (uint)m_vObjects.Count) throw new ArgumentOutOfRangeException("uEndIndexIncl"); if(uStartIndexIncl > uEndIndexIncl) throw new ArgumentException(); List list = new List((int)(uEndIndexIncl - uStartIndexIncl) + 1); for(uint u = uStartIndexIncl; u <= uEndIndexIncl; ++u) { list.Add(m_vObjects[(int)u]); } return list; } public int IndexOf(T pwReference) { Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference"); return m_vObjects.IndexOf(pwReference); } /// /// Delete an object of this list. The object to be deleted is identified /// by a reference handle. /// /// Reference of the object to be deleted. /// Returns true if the object was deleted, false if /// the object wasn't found in this list. /// Thrown if the input /// parameter is null. public bool Remove(T pwReference) { Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference"); return m_vObjects.Remove(pwReference); } public void RemoveAt(uint uIndex) { m_vObjects.RemoveAt((int)uIndex); } /// /// Move an object up or down. /// /// The object to be moved. /// Move one up. If false, move one down. public void MoveOne(T tObject, bool bUp) { Debug.Assert(tObject != null); if(tObject == null) throw new ArgumentNullException("tObject"); int nCount = m_vObjects.Count; if(nCount <= 1) return; int nIndex = m_vObjects.IndexOf(tObject); if(nIndex < 0) { Debug.Assert(false); return; } if(bUp && (nIndex > 0)) // No assert for top item { T tTemp = m_vObjects[nIndex - 1]; m_vObjects[nIndex - 1] = m_vObjects[nIndex]; m_vObjects[nIndex] = tTemp; } else if(!bUp && (nIndex != (nCount - 1))) // No assert for bottom item { T tTemp = m_vObjects[nIndex + 1]; m_vObjects[nIndex + 1] = m_vObjects[nIndex]; m_vObjects[nIndex] = tTemp; } } public void MoveOne(T[] vObjects, bool bUp) { Debug.Assert(vObjects != null); if(vObjects == null) throw new ArgumentNullException("vObjects"); List lIndices = new List(); foreach(T t in vObjects) { if(t == null) { Debug.Assert(false); continue; } int p = IndexOf(t); if(p >= 0) lIndices.Add(p); else { Debug.Assert(false); } } MoveOne(lIndices.ToArray(), bUp); } public void MoveOne(int[] vIndices, bool bUp) { Debug.Assert(vIndices != null); if(vIndices == null) throw new ArgumentNullException("vIndices"); int n = m_vObjects.Count; if(n <= 1) return; // No moving possible int m = vIndices.Length; if(m == 0) return; // Nothing to move int[] v = new int[m]; Array.Copy(vIndices, v, m); Array.Sort(v); if((bUp && (v[0] <= 0)) || (!bUp && (v[m - 1] >= (n - 1)))) return; // Moving as a block is not possible int iStart = (bUp ? 0 : (m - 1)); int iExcl = (bUp ? m : -1); int iStep = (bUp ? 1 : -1); for(int i = iStart; i != iExcl; i += iStep) { int p = v[i]; if((p < 0) || (p >= n)) { Debug.Assert(false); continue; } T t = m_vObjects[p]; if(bUp) { Debug.Assert(p > 0); m_vObjects.RemoveAt(p); m_vObjects.Insert(p - 1, t); } else // Down { Debug.Assert(p < (n - 1)); m_vObjects.RemoveAt(p); m_vObjects.Insert(p + 1, t); } } } /// /// Move some of the objects in this list to the top/bottom. /// /// List of objects to be moved. /// Move to top. If false, move to bottom. public void MoveTopBottom(T[] vObjects, bool bTop) { Debug.Assert(vObjects != null); if(vObjects == null) throw new ArgumentNullException("vObjects"); if(vObjects.Length == 0) return; int nCount = m_vObjects.Count; foreach(T t in vObjects) m_vObjects.Remove(t); if(bTop) { int nPos = 0; foreach(T t in vObjects) { m_vObjects.Insert(nPos, t); ++nPos; } } else // Move to bottom { foreach(T t in vObjects) m_vObjects.Add(t); } Debug.Assert(nCount == m_vObjects.Count); if(nCount != m_vObjects.Count) throw new ArgumentException("At least one of the T objects in the vObjects list doesn't exist!"); } public void Sort(IComparer tComparer) { if(tComparer == null) throw new ArgumentNullException("tComparer"); m_vObjects.Sort(tComparer); } public void Sort(Comparison tComparison) { if(tComparison == null) throw new ArgumentNullException("tComparison"); m_vObjects.Sort(tComparison); } public static PwObjectList FromArray(T[] tArray) { if(tArray == null) throw new ArgumentNullException("tArray"); PwObjectList l = new PwObjectList(); foreach(T t in tArray) { l.Add(t); } return l; } public static PwObjectList FromList(List tList) { if(tList == null) throw new ArgumentNullException("tList"); PwObjectList l = new PwObjectList(); l.Add(tList); return l; } } }