CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_4_1_8_patch9/src/DataFormats/Common/interface/MultiAssociation.h

Go to the documentation of this file.
00001 #ifndef DataFormats_Common_MultiAssociation_h
00002 #define DataFormats_Common_MultiAssociation_h
00003 /* \class MultiAssociation
00004  *
00005  * \author Giovanni Petrucciani, SNS Pisa and CERN PH-CMG
00006  * 
00007  * One-to-Many variant of edm::Association<C> / edm::ValueMap<T>, 
00008  *
00009  * Given a key, it will return a range of iterators within a collection (fast access), or collection by value (slow access mode)
00010  *
00011  * The range of iterators is handled through boost::sub_range, so it should feel like a collection:
00012  *   1) it has a '::const_iterator', a 'begin()' and an 'end()'
00013  *   2) it has a 'size()' and an 'empty()'
00014  *   3) it has a 'front()', 'back()'
00015  *   4) it works as an array  (i.e. you can use range[index] to pick an element)
00016  *   5) if your MultiAssociation is not const, you can modify the values associated to each key
00017  *      (but you can't push_back new values for a given key)
00018  * ( details at https://www.boost.org/doc/libs/1_37_0/libs/range/doc/utility_class.html#sub_range )
00019  *
00020  * The collection can be a RefVector<C> (to work a la edm::Association<C>), a std::vector<T> (to work a la ValueMap<T>), a PtrVector<T>...
00021  * The collection must be compatible with sub_range and support a few other features. Usually you need:
00022  *  - that it has a default constructor
00023  *  - that it has a const_iterator, and "begin() const" returns such const_iterator. 
00024  *  - that it has an iterator, and "begin()" returns such iterator (note: it doesn't have to be writable, it can be const as well) 
00025  *  - that it has a swap method
00026  *  - that 'begin() + offset' is legal (and fast, otherwise this thing is will be awfully slow)
00027  *  - that you can push_back on a C the * of a C::const_iterator. Namely this should be legal
00028  *    <code>
00029  *       C::const_iterator it = ..something..
00030  *       C someOtherC = ...
00031  *       someOtherC.push_back(*it);
00032  *    </code>
00033  *
00034  * It can be filled through a FastFiller or a LazyFiller. 
00035  * FastFiller is probably faster, but has many constraints:
00036  * - you must fill only one product id at time
00037  * - you must fill items in strict key order
00038  * - there is no way to abort a filling operation
00039  * Try LazyFiller first, unless you're doing some sort of batch task that satisfies the FastFiller requirements
00040  *
00041  * It stores:
00042  *  - for each collection of keys: a product id and an offset  
00043  *  - for each key (even those not mapped to any value): one offset
00044  *  - for each value: one value
00045  * With respect to ValueMap / Association, there is one extra int32 for each key (but we don't store null values)
00046  *
00047  * Its backbone is given by edm::helper::IndexRangeAssociation, that maps keys to ranges, and is not templated.
00048  * \version $Id: MultiAssociation.h,v 1.1 2009/03/03 09:14:59 gpetrucc Exp $
00049  *
00050  */
00051 
00052 #include <vector>
00053 #include <map>
00054 #include <boost/shared_ptr.hpp>
00055 #include <boost/utility.hpp>
00056 #include <boost/range.hpp>
00057 #include "DataFormats/Provenance/interface/ProductID.h"
00058 
00059 namespace edm {
00060   namespace helper {
00065 
00072       class IndexRangeAssociation {
00073           public:
00074               typedef std::pair<unsigned int, unsigned int> range;
00075             
00076               IndexRangeAssociation() : isFilling_(false) {}
00077 
00080               template<typename RefKey>
00081               range operator[](const RefKey &r) const {  
00082                   return get(r.id(), r.key()); 
00083               }
00084               
00087               range get(const edm::ProductID & id, unsigned int t) const ;
00088 
00090               bool  contains(ProductID id) const ;
00091 
00093               unsigned int size() const { return ref_offsets_.empty() ? 0 : ref_offsets_.size() - 1; }
00094             
00096               bool empty() const { return ref_offsets_.empty(); }
00097 
00102               class FastFiller : boost::noncopyable {
00103                   public:
00105                       FastFiller(IndexRangeAssociation &assoc, ProductID id, unsigned int size) ;
00106 
00108                       ~FastFiller() ;
00109 
00111                       template<typename RefKey> 
00112                       void insert(const RefKey &r, unsigned int startingOffset, unsigned int size) {
00113                          insert(r.id(), r.key(), startingOffset, size);
00114                       }
00115 
00117                       void insert(edm::ProductID id, unsigned int key, unsigned int startingOffset, unsigned int size) ;
00118                   private:
00119                       IndexRangeAssociation & assoc_;
00120                       const ProductID         id_;
00121                       unsigned int start_, end_; // indices into assoc_.ref_offsets_ (end_ points to the end marker, so it's valid)
00123                       int lastKey_; 
00124               }; // FastFiller
00125               friend class FastFiller;
00126 
00127               void swap(IndexRangeAssociation &other) ;
00128 
00129               static void throwUnexpectedProductID(ProductID found, ProductID expected, const char *where) ;
00130           private:
00131               typedef std::pair<edm::ProductID,unsigned int> id_off_pair;
00132               typedef std::vector<id_off_pair> id_offset_vector; // sorted by product id
00133               typedef std::vector<int> offset_vector; 
00134               id_offset_vector id_offsets_;  
00135               offset_vector    ref_offsets_; 
00136 
00137               bool isFilling_; // transient, to check no two fillers exist at the same time
00138               struct IDComparator { 
00139                 bool operator()(const id_off_pair &p, const edm::ProductID &id) const {  return p.first < id; }
00140               }; // IDComparator
00141 
00142               
00143     };
00144 
00145     // Free swap function
00146     inline void swap(IndexRangeAssociation& lhs, IndexRangeAssociation& rhs) { lhs.swap(rhs); }
00147 
00148   } // Helper Namespace
00149  
00150   template<typename C>
00151   class MultiAssociation {
00152   public:
00153     typedef C Collection;
00154     typedef boost::sub_range<const Collection> const_range;
00155     typedef boost::sub_range<Collection>       range;
00156 
00157     MultiAssociation() {}
00158 
00160     template<typename RefKey> 
00161     const_range operator[](const RefKey &r) const { 
00162         return get(r.id(), r.key()); 
00163     }
00164     // ---- and the non-const sister
00166     template<typename RefKey> 
00167     range operator[](const RefKey &r) { 
00168         return get(r.id(), r.key()); 
00169     }
00170 
00172     template<typename RefKey> 
00173     Collection getValues(const RefKey &r) const { 
00174         return getValues(r.id(), r.key()); 
00175     }
00176 
00178     bool contains(const edm::ProductID &id) const { return indices_.contains(id); }
00179 
00181     const_range get(const edm::ProductID &id, unsigned int t) const ;
00182     // ---- and the non-const sister
00184     range get(const edm::ProductID &id, unsigned int t) ;
00185 
00187     Collection getValues(const edm::ProductID &id, unsigned int t) const ;
00188     
00189     void swap(MultiAssociation &other) {
00190         indices_.swap(other.indices_);
00191         data_.swap(other.data_);
00192     }
00193 
00195     unsigned int dataSize()    const { return data_.size();    }
00196 
00198     unsigned int size() const { return indices_.size(); }
00199 
00201     bool empty() const { return indices_.empty(); }
00202 
00207     class FastFiller {
00208         public:
00209             template<typename HandleType>
00210             FastFiller(MultiAssociation &assoc, const HandleType &handle) : 
00211                 assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, handle.id(), handle->size())) {}
00212 
00213             FastFiller(MultiAssociation &assoc, edm::ProductID id, unsigned int size) : 
00214                 assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, id, size)) {}
00215 
00216             ~FastFiller() {}
00217         
00219             template<typename KeyRef>
00220             void setValues(const KeyRef &k, const Collection &refs) { setValues(k.id(), k.key(), refs); }
00221 
00223             void setValues(const edm::ProductID &id, unsigned int key, const Collection &refs);
00224         private:
00225             MultiAssociation &  assoc_;
00226             typedef edm::helper::IndexRangeAssociation::FastFiller IndexFiller;
00227             boost::shared_ptr<IndexFiller> indexFiller_;
00228 
00229     }; // FastFiller
00230     friend class FastFiller;
00231 
00232     template<typename HandleType>
00233     FastFiller fastFiller(const HandleType &handle) { return FastFiller(*this, handle); }
00234 
00240     class LazyFiller {
00241         public:
00242             template<typename HandleType>
00243             LazyFiller(MultiAssociation &assoc, const HandleType &handle, bool fillOnExit=false) :
00244                 assoc_(assoc), 
00245                 id_(handle.id()), size_(handle->size()), 
00246                 tempValues_(new TempValues()), fillOnExit_(fillOnExit) {}
00247             ~LazyFiller() { if (fillOnExit_) fill(); }
00248 
00253             void fill();
00254 
00256             void setFillOnExit(bool fillOnExit) { fillOnExit_ = fillOnExit; }
00257 
00259             template<typename KeyRef>
00260             void setValues(const KeyRef &k, const Collection &refs) ;
00261 
00264             template<typename KeyRef>
00265             void swapValues(const KeyRef &k, Collection &refs) ;
00266         private:
00267             typedef std::map<unsigned int, Collection> TempValues;
00268             MultiAssociation & assoc_;
00269             ProductID id_; 
00270             unsigned int size_;
00271             boost::shared_ptr<TempValues> tempValues_;
00272             bool fillOnExit_;
00273     }; // LazyFiller
00274     friend class LazyFiller;
00275 
00276     template<typename HandleType>
00277     LazyFiller lazyFiller(const HandleType &h, bool fillOnExit=false) { return LazyFiller(*this, h, fillOnExit); }
00278 
00279   private:
00280     typedef helper::IndexRangeAssociation Indices;
00281     Indices    indices_;
00282     Collection data_;
00283 
00284 }; // class
00285 
00286   // Free swap function
00287   template <typename C>
00288   inline void swap(MultiAssociation<C>& lhs, MultiAssociation<C>& rhs) { lhs.swap(rhs); }
00289 
00290   //============= IMPLEMENTATION OF THE METHODS =============
00291   template<typename C>
00292   typename MultiAssociation<C>::const_range
00293   MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) const {
00294     Indices::range idxrange = indices_.get(id,key);
00295     return const_range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
00296   }
00297 
00298   template<typename C>
00299   typename MultiAssociation<C>::range
00300   MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) {
00301     Indices::range idxrange = indices_.get(id,key);
00302     return range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
00303   }
00304 
00305 
00306   template<typename C>
00307   typename MultiAssociation<C>::Collection
00308   MultiAssociation<C>::getValues(const edm::ProductID & id, unsigned int key) const {
00309      Collection ret;
00310      const_range values = get(id,key);
00311      for (typename const_range::const_iterator it = values.begin(), ed = values.end(); it != ed; ++it) {
00312         ret.push_back(*it);
00313      }
00314      return ret;
00315   }
00316 
00317   template<typename C>
00318   void MultiAssociation<C>::FastFiller::setValues(const edm::ProductID & id, unsigned int key, const Collection &vals) {
00319      indexFiller_->insert(id, key, assoc_.data_.size(), vals.size());
00320      for (typename Collection::const_iterator it = vals.begin(), ed = vals.end(); it != ed; ++it) {
00321         assoc_.data_.push_back(*it);
00322      }
00323   } 
00324   
00325   template<typename C>
00326   template<typename KeyRef>
00327   void MultiAssociation<C>::LazyFiller::setValues(const KeyRef &k, const Collection &vals) {
00328      if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");  
00329      (*tempValues_)[k.key()] = vals;
00330   }
00331 
00332   template<typename C>
00333   template<typename KeyRef>
00334   void MultiAssociation<C>::LazyFiller::swapValues(const KeyRef &k, Collection &vals) {
00335      if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");  
00336      vals.swap((*tempValues_)[k.key()]);
00337   }
00338 
00339 
00340   template<typename C>
00341   void MultiAssociation<C>::LazyFiller::fill() {
00342      if (id_ != ProductID()) { // protection against double filling
00343         typename MultiAssociation<C>::FastFiller filler(assoc_, id_, size_);
00344         for (typename TempValues::const_iterator it = tempValues_->begin(), ed = tempValues_->end(); it != ed; ++it) {
00345            filler.setValues(id_, it->first, it->second);                
00346         }
00347         id_ = ProductID();     // protection against double filling
00348      }
00349   }
00350 
00351  
00352 } // namespace
00353 
00354 #endif