You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

323 lines
9.7 KiB

5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright 2017 CERN
  5. * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Maciej Suminski <maciej.suminski@cern.ch>
  8. * @author Bernhard Stegmaier <stegmaier@sw-systems.de>
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #ifndef MULTIVECTOR_H
  28. #define MULTIVECTOR_H
  29. #include <boost/ptr_container/ptr_vector.hpp>
  30. #include <stdexcept>
  31. /**
  32. * Multivector container type.
  33. *
  34. * Keeps items segregated by their type in multiple ptr_vectors. Provides both
  35. * access as a flat list as well as access by type of item.
  36. *
  37. * T is the stored type, needs to provide Type() method used to segregate items.
  38. * FIRST_TYPE_VAL is the lower boundary value of the types stored in the container.
  39. * LAST_TYPE_VAL is the upper boundary value of the types stored in the container.
  40. */
  41. template<typename T, int FIRST_TYPE_VAL, int LAST_TYPE_VAL>
  42. class MULTIVECTOR
  43. {
  44. public:
  45. /**
  46. * Type value to indicate no specific type. Mostly used to access the container as a flat list
  47. * or to return data for the whole container.
  48. */
  49. static constexpr int UNDEFINED_TYPE = 0;
  50. static_assert( FIRST_TYPE_VAL > UNDEFINED_TYPE,
  51. "FIRST_TYPE_VAL must be greater than UNDEFINED_TYPE" );
  52. static_assert( FIRST_TYPE_VAL < LAST_TYPE_VAL,
  53. "FIRST_TYPE_VAL must be greater than LAST_TYPE_VAL" );
  54. /**
  55. * Helper for defining a list of library draw object pointers.
  56. *
  57. * The Boost pointer containers are responsible for deleting object pointers placed
  58. * in them. If you access a object pointer from the list, do not delete it directly.
  59. */
  60. typedef boost::ptr_vector<T> ITEM_PTR_VECTOR;
  61. /**
  62. * Generic implementation of a flat const/non-const iterator over contained items.
  63. */
  64. template<typename ITEM_TYPE, typename ITEM_CONTAINER, typename ITEM_CONTAINER_IT>
  65. class ITERATOR_BASE
  66. {
  67. public:
  68. ITEM_TYPE& operator*()
  69. {
  70. return *m_it;
  71. }
  72. ITEM_TYPE* operator->()
  73. {
  74. return &( *m_it );
  75. }
  76. ITERATOR_BASE& operator++()
  77. {
  78. if( m_it != (*m_parent)[ m_curType ].end() )
  79. ++m_it;
  80. validate();
  81. return *this;
  82. }
  83. bool operator!=( const ITERATOR_BASE& aOther ) const
  84. {
  85. if( aOther.m_parent != m_parent )
  86. return true;
  87. if( aOther.m_filter != m_filter )
  88. return true;
  89. if( aOther.m_curType != m_curType )
  90. return true;
  91. return aOther.m_it != m_it;
  92. }
  93. protected:
  94. /**
  95. * @param aItems is the container to wrap.
  96. * @param aIt is the iterator to initialize this iterator (usually some begin() or end()
  97. * iterator).
  98. * @param aBucket is the type ID of the given iterator.
  99. * @param aType enables item type filtering. When aType is UNDEFINED_TYPE, there is no
  100. * filtering and all item types are accessible by the iterator.
  101. */
  102. ITERATOR_BASE( ITEM_CONTAINER* aItems, ITEM_CONTAINER_IT aIt,
  103. int aBucket, int aType = UNDEFINED_TYPE )
  104. : m_parent( aItems ), m_it( aIt ), m_curType( aBucket )
  105. {
  106. m_filter = ( aType != UNDEFINED_TYPE );
  107. }
  108. ///< Assures the iterator is in a valid state.
  109. void validate()
  110. {
  111. // for all-items iterators (unfiltered): check if this is the end of the
  112. // current type container, if so switch to the next non-empty container
  113. if( !m_filter && m_it == (*m_parent)[ m_curType ].end() )
  114. {
  115. // switch to the next type (look for a not empty container)
  116. int nextType = m_curType;
  117. do
  118. ++nextType;
  119. while( ( nextType <= LAST_TYPE ) && (*m_parent)[ nextType ].empty() );
  120. // there is another not empty container, so make the iterator point to it,
  121. // otherwise it means the iterator points to the last item
  122. if( nextType <= LAST_TYPE )
  123. {
  124. m_curType = nextType;
  125. m_it = (*m_parent)[ m_curType ].begin();
  126. }
  127. }
  128. }
  129. ///< Wrapped container
  130. ITEM_CONTAINER* m_parent;
  131. ///< Iterator for one of the ptr_vector containers stored in the array
  132. ITEM_CONTAINER_IT m_it;
  133. ///< Flag indicating whether type filtering is enabled
  134. bool m_filter;
  135. ///< Type of the currently iterated items
  136. int m_curType;
  137. friend class MULTIVECTOR;
  138. };
  139. ///< The non-const iterator
  140. typedef ITERATOR_BASE<T, MULTIVECTOR<T, FIRST_TYPE_VAL, LAST_TYPE_VAL>,
  141. typename ITEM_PTR_VECTOR::iterator> ITERATOR;
  142. ///< The const iterator
  143. typedef ITERATOR_BASE<const T, const MULTIVECTOR<T, FIRST_TYPE_VAL, LAST_TYPE_VAL>,
  144. typename ITEM_PTR_VECTOR::const_iterator> CONST_ITERATOR;
  145. MULTIVECTOR()
  146. {
  147. }
  148. void push_back( T* aItem )
  149. {
  150. operator[]( aItem->Type() ).push_back( aItem );
  151. }
  152. ITERATOR erase( const ITERATOR& aIterator )
  153. {
  154. ITERATOR it( aIterator );
  155. it.m_it = (*aIterator.m_parent)[ aIterator.m_curType ].erase( aIterator.m_it );
  156. it.validate();
  157. return it;
  158. }
  159. ITERATOR begin( int aType = UNDEFINED_TYPE )
  160. {
  161. int bucket = ( aType != UNDEFINED_TYPE ) ? aType : first();
  162. return ITERATOR( this, operator[]( bucket ).begin(), bucket, aType );
  163. }
  164. ITERATOR end( int aType = UNDEFINED_TYPE )
  165. {
  166. int bucket = ( aType != UNDEFINED_TYPE ) ? aType : last();
  167. return ITERATOR( this, operator[]( bucket ).end(), bucket, aType );
  168. }
  169. CONST_ITERATOR begin( int aType = UNDEFINED_TYPE ) const
  170. {
  171. int bucket = ( aType != UNDEFINED_TYPE ) ? aType : first();
  172. return CONST_ITERATOR( this, operator[]( bucket ).begin(), bucket, aType );
  173. }
  174. CONST_ITERATOR end( int aType = UNDEFINED_TYPE ) const
  175. {
  176. int bucket = ( aType != UNDEFINED_TYPE ) ? aType : last();
  177. return CONST_ITERATOR( this, operator[]( bucket ).end(), bucket, aType );
  178. }
  179. void clear( int aType = UNDEFINED_TYPE )
  180. {
  181. if( aType != UNDEFINED_TYPE )
  182. {
  183. operator[]( aType ).clear();
  184. }
  185. else
  186. {
  187. for( int i = 0; i < TYPES_COUNT; ++i)
  188. m_data[ i ].clear();
  189. }
  190. }
  191. size_t size( int aType = UNDEFINED_TYPE ) const
  192. {
  193. if( aType != UNDEFINED_TYPE )
  194. {
  195. return operator[]( aType ).size();
  196. }
  197. else
  198. {
  199. size_t cnt = 0;
  200. for( int i = 0; i < TYPES_COUNT; ++i)
  201. cnt += m_data[ i ].size();
  202. return cnt;
  203. }
  204. }
  205. bool empty( int aType = UNDEFINED_TYPE ) const
  206. {
  207. return ( size( aType ) == 0 );
  208. }
  209. void sort()
  210. {
  211. for( int i = 0; i < TYPES_COUNT; ++i )
  212. m_data[ i ].sort();
  213. }
  214. /**
  215. * Remove duplicate elements in list
  216. */
  217. void unique()
  218. {
  219. for( int i = 0; i < TYPES_COUNT; ++i )
  220. {
  221. if( m_data[ i ].size() > 1 )
  222. m_data[ i ].unique();
  223. }
  224. }
  225. ITEM_PTR_VECTOR& operator[]( int aType )
  226. {
  227. if( ( aType < FIRST_TYPE ) || ( aType > LAST_TYPE ) )
  228. {
  229. wxFAIL_MSG( "Attempted access to type not within MULTIVECTOR" );
  230. // return type is a reference so we have to return something...
  231. aType = FIRST_TYPE;
  232. }
  233. return m_data[ aType - FIRST_TYPE ];
  234. }
  235. const ITEM_PTR_VECTOR& operator[]( int aType ) const
  236. {
  237. if( ( aType < FIRST_TYPE ) || ( aType > LAST_TYPE ) )
  238. {
  239. wxFAIL_MSG( "Attempted access to type not within MULTIVECTOR" );
  240. // return type is a reference so we have to return something...
  241. aType = FIRST_TYPE;
  242. }
  243. return m_data[ aType - FIRST_TYPE ];
  244. }
  245. // Range of valid types handled by the iterator
  246. static constexpr int FIRST_TYPE = FIRST_TYPE_VAL;
  247. static constexpr int LAST_TYPE = LAST_TYPE_VAL;
  248. static constexpr int TYPES_COUNT = LAST_TYPE - FIRST_TYPE + 1;
  249. private:
  250. ///< Get first non-empty type or first type if all are empty.
  251. int first() const
  252. {
  253. int i = 0;
  254. while( ( i < TYPES_COUNT ) && ( m_data[ i ].empty() ) )
  255. ++i;
  256. return ( i == TYPES_COUNT ) ? FIRST_TYPE : FIRST_TYPE + i;
  257. }
  258. ///< Get last non-empty type or first type if all are empty.
  259. int last() const
  260. {
  261. int i = TYPES_COUNT - 1;
  262. while( ( i >= 0 ) && ( m_data[ i ].empty() ) )
  263. --i;
  264. return ( i < 0 ) ? FIRST_TYPE : FIRST_TYPE + i;
  265. }
  266. ///< Contained items by type
  267. ITEM_PTR_VECTOR m_data[TYPES_COUNT];
  268. };
  269. #endif /* MULTIVECTOR_H */