CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_4_4_5_patch3/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 http://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.2 2011/03/08 18:47:15 chrjones 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/Common/interface/CMS_CLASS_VERSION.h"
00058 #include "DataFormats/Provenance/interface/ProductID.h"
00059 
00060 namespace edm {
00061   namespace helper {
00066 
00073       class IndexRangeAssociation {
00074           public:
00075               typedef std::pair<unsigned int, unsigned int> range;
00076             
00077               IndexRangeAssociation() : isFilling_(false) {}
00078 
00081               template<typename RefKey>
00082               range operator[](const RefKey &r) const {  
00083                   return get(r.id(), r.key()); 
00084               }
00085               
00088               range get(const edm::ProductID & id, unsigned int t) const ;
00089 
00091               bool  contains(ProductID id) const ;
00092 
00094               unsigned int size() const { return ref_offsets_.empty() ? 0 : ref_offsets_.size() - 1; }
00095             
00097               bool empty() const { return ref_offsets_.empty(); }
00098 
00103               class FastFiller : boost::noncopyable {
00104                   public:
00106                       FastFiller(IndexRangeAssociation &assoc, ProductID id, unsigned int size) ;
00107 
00109                       ~FastFiller() ;
00110 
00112                       template<typename RefKey> 
00113                       void insert(const RefKey &r, unsigned int startingOffset, unsigned int size) {
00114                          insert(r.id(), r.key(), startingOffset, size);
00115                       }
00116 
00118                       void insert(edm::ProductID id, unsigned int key, unsigned int startingOffset, unsigned int size) ;
00119                   private:
00120                       IndexRangeAssociation & assoc_;
00121                       const ProductID         id_;
00122                       unsigned int start_, end_; // indices into assoc_.ref_offsets_ (end_ points to the end marker, so it's valid)
00124                       int lastKey_; 
00125               }; // FastFiller
00126               friend class FastFiller;
00127 
00128               void swap(IndexRangeAssociation &other) ;
00129 
00130               static void throwUnexpectedProductID(ProductID found, ProductID expected, const char *where) ;
00131           private:
00132               typedef std::pair<edm::ProductID,unsigned int> id_off_pair;
00133               typedef std::vector<id_off_pair> id_offset_vector; // sorted by product id
00134               typedef std::vector<int> offset_vector; 
00135               id_offset_vector id_offsets_;  
00136               offset_vector    ref_offsets_; 
00137 
00138               bool isFilling_; // transient, to check no two fillers exist at the same time
00139               struct IDComparator { 
00140                 bool operator()(const id_off_pair &p, const edm::ProductID &id) const {  return p.first < id; }
00141               }; // IDComparator
00142 
00143               
00144     };
00145 
00146     // Free swap function
00147     inline void swap(IndexRangeAssociation& lhs, IndexRangeAssociation& rhs) { lhs.swap(rhs); }
00148 
00149   } // Helper Namespace
00150  
00151   template<typename C>
00152   class MultiAssociation {
00153   public:
00154     typedef C Collection;
00155     typedef boost::sub_range<const Collection> const_range;
00156     typedef boost::sub_range<Collection>       range;
00157 
00158     MultiAssociation() {}
00159 
00161     template<typename RefKey> 
00162     const_range operator[](const RefKey &r) const { 
00163         return get(r.id(), r.key()); 
00164     }
00165     // ---- and the non-const sister
00167     template<typename RefKey> 
00168     range operator[](const RefKey &r) { 
00169         return get(r.id(), r.key()); 
00170     }
00171 
00173     template<typename RefKey> 
00174     Collection getValues(const RefKey &r) const { 
00175         return getValues(r.id(), r.key()); 
00176     }
00177 
00179     bool contains(const edm::ProductID &id) const { return indices_.contains(id); }
00180 
00182     const_range get(const edm::ProductID &id, unsigned int t) const ;
00183     // ---- and the non-const sister
00185     range get(const edm::ProductID &id, unsigned int t) ;
00186 
00188     Collection getValues(const edm::ProductID &id, unsigned int t) const ;
00189     
00190     void swap(MultiAssociation &other) {
00191         indices_.swap(other.indices_);
00192         data_.swap(other.data_);
00193     }
00194 
00196     unsigned int dataSize()    const { return data_.size();    }
00197 
00199     unsigned int size() const { return indices_.size(); }
00200 
00202     bool empty() const { return indices_.empty(); }
00203 
00208     class FastFiller {
00209         public:
00210             template<typename HandleType>
00211             FastFiller(MultiAssociation &assoc, const HandleType &handle) : 
00212                 assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, handle.id(), handle->size())) {}
00213 
00214             FastFiller(MultiAssociation &assoc, edm::ProductID id, unsigned int size) : 
00215                 assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, id, size)) {}
00216 
00217             ~FastFiller() {}
00218         
00220             template<typename KeyRef>
00221             void setValues(const KeyRef &k, const Collection &refs) { setValues(k.id(), k.key(), refs); }
00222 
00224             void setValues(const edm::ProductID &id, unsigned int key, const Collection &refs);
00225         private:
00226             MultiAssociation &  assoc_;
00227             typedef edm::helper::IndexRangeAssociation::FastFiller IndexFiller;
00228             boost::shared_ptr<IndexFiller> indexFiller_;
00229 
00230     }; // FastFiller
00231     friend class FastFiller;
00232 
00233     template<typename HandleType>
00234     FastFiller fastFiller(const HandleType &handle) { return FastFiller(*this, handle); }
00235 
00241     class LazyFiller {
00242         public:
00243             template<typename HandleType>
00244             LazyFiller(MultiAssociation &assoc, const HandleType &handle, bool fillOnExit=false) :
00245                 assoc_(assoc), 
00246                 id_(handle.id()), size_(handle->size()), 
00247                 tempValues_(new TempValues()), fillOnExit_(fillOnExit) {}
00248             ~LazyFiller() { if (fillOnExit_) fill(); }
00249 
00254             void fill();
00255 
00257             void setFillOnExit(bool fillOnExit) { fillOnExit_ = fillOnExit; }
00258 
00260             template<typename KeyRef>
00261             void setValues(const KeyRef &k, const Collection &refs) ;
00262 
00265             template<typename KeyRef>
00266             void swapValues(const KeyRef &k, Collection &refs) ;
00267         private:
00268             typedef std::map<unsigned int, Collection> TempValues;
00269             MultiAssociation & assoc_;
00270             ProductID id_; 
00271             unsigned int size_;
00272             boost::shared_ptr<TempValues> tempValues_;
00273             bool fillOnExit_;
00274     }; // LazyFiller
00275     friend class LazyFiller;
00276 
00277     template<typename HandleType>
00278     LazyFiller lazyFiller(const HandleType &h, bool fillOnExit=false) { return LazyFiller(*this, h, fillOnExit); }
00279 
00280     //Used by ROOT storage
00281     CMS_CLASS_VERSION(10)
00282 
00283   private:
00284     typedef helper::IndexRangeAssociation Indices;
00285     Indices    indices_;
00286     Collection data_;
00287 
00288 }; // class
00289 
00290   // Free swap function
00291   template <typename C>
00292   inline void swap(MultiAssociation<C>& lhs, MultiAssociation<C>& rhs) { lhs.swap(rhs); }
00293 
00294   //============= IMPLEMENTATION OF THE METHODS =============
00295   template<typename C>
00296   typename MultiAssociation<C>::const_range
00297   MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) const {
00298     Indices::range idxrange = indices_.get(id,key);
00299     return const_range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
00300   }
00301 
00302   template<typename C>
00303   typename MultiAssociation<C>::range
00304   MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) {
00305     Indices::range idxrange = indices_.get(id,key);
00306     return range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
00307   }
00308 
00309 
00310   template<typename C>
00311   typename MultiAssociation<C>::Collection
00312   MultiAssociation<C>::getValues(const edm::ProductID & id, unsigned int key) const {
00313      Collection ret;
00314      const_range values = get(id,key);
00315      for (typename const_range::const_iterator it = values.begin(), ed = values.end(); it != ed; ++it) {
00316         ret.push_back(*it);
00317      }
00318      return ret;
00319   }
00320 
00321   template<typename C>
00322   void MultiAssociation<C>::FastFiller::setValues(const edm::ProductID & id, unsigned int key, const Collection &vals) {
00323      indexFiller_->insert(id, key, assoc_.data_.size(), vals.size());
00324      for (typename Collection::const_iterator it = vals.begin(), ed = vals.end(); it != ed; ++it) {
00325         assoc_.data_.push_back(*it);
00326      }
00327   } 
00328   
00329   template<typename C>
00330   template<typename KeyRef>
00331   void MultiAssociation<C>::LazyFiller::setValues(const KeyRef &k, const Collection &vals) {
00332      if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");  
00333      (*tempValues_)[k.key()] = vals;
00334   }
00335 
00336   template<typename C>
00337   template<typename KeyRef>
00338   void MultiAssociation<C>::LazyFiller::swapValues(const KeyRef &k, Collection &vals) {
00339      if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");  
00340      vals.swap((*tempValues_)[k.key()]);
00341   }
00342 
00343 
00344   template<typename C>
00345   void MultiAssociation<C>::LazyFiller::fill() {
00346      if (id_ != ProductID()) { // protection against double filling
00347         typename MultiAssociation<C>::FastFiller filler(assoc_, id_, size_);
00348         for (typename TempValues::const_iterator it = tempValues_->begin(), ed = tempValues_->end(); it != ed; ++it) {
00349            filler.setValues(id_, it->first, it->second);                
00350         }
00351         id_ = ProductID();     // protection against double filling
00352      }
00353   }
00354 
00355  
00356 } // namespace
00357 
00358 #endif