#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library 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 IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_SUPPORT_COLLECTIONS_OBSERVABLEDYNAMICARRAY_H #define NUCLEX_SUPPORT_COLLECTIONS_OBSERVABLEDYNAMICARRAY_H #include "IndexedCollection.h" #include "ObservableCollection.h" #include "ObservableIndexedCollection.h" #include // for std::vector namespace Nuclex { namespace Support { namespace Collections { // ------------------------------------------------------------------------------------------- // /// Dynamic array that sends out change notifications /// Type of values contained in the dynamic array /// /// /// This collection sends out notifications to any interested party when its contents /// change (items being reordered, added or removed). It has no way of knowing when /// internal changes to an item itself occur. /// /// /// It is both a useful container for small numbers of items and a demonstration case /// on how to implement the interfaces and events defined by the Nuclex::Support library. /// /// template class ObservableDynamicArray : public IndexedCollection, public virtual ObservableCollection, public virtual ObservableIndexedCollection { /// Invalid index used to indicate when a requested item wasn't found public: using IndexedCollection::InvalidIndex; /// Initializes a new dynamic array public: ObservableDynamicArray() = default; /// Frees all memory used by the collection public: virtual ~ObservableDynamicArray() = default; /// Initializes a dynamic array reserving memory up-front /// Capacity for which memory will be reserved public: void Reserve(std::size_t capacity) { this->items.reserve(capacity); } /// Determines the index of the specified item in the collection /// Item whose index will be determined /// The index of the specified item public: std::size_t GetIndexOf(const TValue &value) const override { std::size_t count = this->items.size(); for(std::size_t index = 0; index < count; ++index) { if(this->items[index] == value) { return index; } } return InvalidIndex; } /// Retrieves the item at the specified index /// Index of the item that will be retrieved /// The item at the specified index public: const TValue &GetAt(std::size_t index) const override { return this->items.at(index); } /// Accesses the item at the specified index /// Index of the item that will be accessed /// The item at the specified index public: TValue &GetAt(std::size_t index) override { return this->items.at(index); } /// Assigns the specified item to the specified index /// Index at which the item will be stored /// Item that will be stored at the specified index public: void SetAt(std::size_t index, const TValue &value) override { if(index < this->items.size()) { bool removedItemNeeded = ( (ObservableIndexedCollection::ItemReplaced.CountSubscribers() > 0) || (ObservableCollection::ItemRemoved.CountSubscribers() > 0) ); if(removedItemNeeded) { TValue old = this->items[index]; this->items[index] = value; ObservableIndexedCollection::ItemReplaced(index, old, value); ObservableCollection::ItemRemoved(old); ObservableCollection::ItemAdded(value); } else { this->items[index] = value; ObservableCollection::ItemAdded(value); } } else { // Let .at() throw the appropriate out-of-bounds exception this->items.at(index) = value; } } /// Inserts the specified item at a specified index /// Index at which the item will be inserted /// Item that will be inserted into the collection public: void InsertAt(std::size_t index, const TValue &value) override { typename std::vector::iterator where = this->items.begin() + index; this->items.insert(where, value); ObservableIndexedCollection::ItemAdded(index, value); ObservableCollection::ItemAdded(value); } /// Removes the item at the specified index from the collection /// Index at which the item will be removed public: void RemoveAt(std::size_t index) override { typename std::vector::iterator where = this->items.begin() + index; bool erasedItemNeeded = ( (ObservableIndexedCollection::ItemRemoved.CountSubscribers() > 0) || (ObservableCollection::ItemRemoved.CountSubscribers() > 0) ); if(erasedItemNeeded) { TValue value = *where; this->items.erase(where); ObservableIndexedCollection::ItemRemoved(index, value); ObservableCollection::ItemRemoved(value); } else { this->items.erase(where); } } /// Adds the specified item to the collection /// Item that will be added to the collection public: void Add(const TValue &item) override { this->items.push_back(item); ObservableIndexedCollection::ItemAdded(this->items.size() - 1, item); ObservableCollection::ItemAdded(item); } /// Removes the specified item from the collection /// Item that will be removed from the collection /// True if the item existed in the collection and was removed public: bool Remove(const TValue &item) override { for( typename std::vector::iterator iterator = this->items.begin(); iterator != this->items.end(); ++iterator ) { if(*iterator == item) { this->items.erase(iterator); ObservableIndexedCollection::ItemRemoved(iterator - this->items.begin, item); ObservableCollection::ItemRemoved(item); return true; } } return false; } /// Removes all items from the collection public: void Clear() override { std::size_t count = this->items.size(); bool erasedItemNeeded = ( (ObservableIndexedCollection::ItemRemoved.CountSubscribers() > 0) || (ObservableCollection::ItemRemoved.CountSubscribers() > 0) ); if(erasedItemNeeded && (count > 0)) { std::vector removed; removed.reserve(this->items.capacity()); removed.swap(this->items); while(count > 0) { --count; ObservableIndexedCollection::ItemRemoved(count, removed[count]); ObservableCollection::ItemRemoved(removed[count]); } } else { this->items.clear(); } } /// Checks if the collection contains the specified item /// Item the collection will be checked for /// True if the collection contain the specified item, false otherwise public: bool Contains(const TValue &item) const override { std::size_t count = this->items.size(); for(std::size_t index = 0; index < count; ++index) { if(this->items[index] == item) { return true; } } return false; } /// Counts the number of items in the collection /// The number of items the collection contains public: std::size_t Count() const override { return this->items.size(); } /// Checks if the collection is empty /// True if the collection is empty public: bool IsEmpty() const override { return this->items.empty(); } /// Items stored in the dynamic array private: std::vector items; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Support::Collections #endif // NUCLEX_SUPPORT_COLLECTIONS_OBSERVABLEDYNAMICARRAY_H