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.

318 lines
9.5 KiB

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