FDO API Reference Feature Data Objects

NamedCollection.h

Go to the documentation of this file.
00001 #ifndef FDO_NAMED_COLLECTION_H
00002 #define FDO_NAMED_COLLECTION_H        1
00003 // 
00004 
00005 //
00006 // Copyright (C) 2004-2006  Autodesk, Inc.
00007 // 
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of version 2.1 of the GNU Lesser
00010 // General Public License as published by the Free Software Foundation.
00011 // 
00012 // This library is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 // Lesser General Public License for more details.
00016 // 
00017 // You should have received a copy of the GNU Lesser General Public
00018 // License along with this library; if not, write to the Free Software
00019 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 //
00021 
00022 #ifdef _WIN32
00023 #pragma once
00024 #endif
00025 
00026 #include <FdoCommon.h>
00027 #include <Common/Collection.h>
00028 #include <map>
00029 
00030 // Build a map when the collection exceeds this number of elements.
00031 // GetItem by name is faster through the map, when the collection
00032 // exceeds 10 elements. The threshold is set a bit higher to account
00033 // for the overhead of building and deleting the map. 
00034 #define FDO_COLL_MAP_THRESHOLD 50
00035 
00036 /// \brief
00037 /// FdoNamedCollection is a template for collections keyed by element name.
00038 /// The OBJ class must provide a GetName() function that returns the element name 
00039 /// as a FdoString* and a CanSetName() function that returns true if the class 
00040 /// allows modification of the name (implements SetName()) and false if it does not. 
00041 /// This class also provides fast access by name for large collections.
00042 /// When CanSetName() returns true, the access by name is a bit less 
00043 /// efficient since linear searches need to be done in some cases. The 
00044 /// reason for this is that after an object changes name, it is no longer
00045 /// in the right position in this collection's name map.
00046 template <class OBJ, class EXC> class FdoNamedCollection : public FdoCollection<OBJ, EXC>
00047 {
00048 public: 
00049     /// \brief
00050     /// Gets the item in the collection at the specified index. Throws an invalid argument exception if the index is out of range.
00051     /// 
00052     /// \param index 
00053     /// Input index
00054     /// 
00055     /// \return
00056     /// Returns the item in the collection at the specified index
00057     /// 
00058     virtual OBJ* GetItem(FdoInt32 index) const
00059     {
00060         return FdoCollection<OBJ, EXC>::GetItem(index);
00061     }
00062 
00063     /// \brief
00064     /// Gets the item in the collection with the specified name. Throws an exception if the item is not found.
00065     /// 
00066     /// \param name 
00067     /// Input item name
00068     /// 
00069     /// \return
00070     /// Returns the item in the collection with the specified name
00071     /// 
00072     virtual OBJ* GetItem(const wchar_t* name) const
00073     {
00074         OBJ* item = FindItem( name );
00075         if ( !item ) 
00076             throw EXC::Create(
00077                 FdoException::NLSGetMessage(
00078                     FDO_NLSID(FDO_38_ITEMNOTFOUND),
00079                     name
00080                 )
00081             );
00082 
00083         return(item);
00084     }
00085 
00086     /// \brief
00087     /// Finds the item in the collection with the specified name.
00088     /// 
00089     /// \param name 
00090     /// Input item name
00091     /// 
00092     /// \return
00093     /// Returns the item in the collection with the specified name.
00094     /// Returns NULL if the item was not found.
00095     /// 
00096     virtual OBJ* FindItem(const wchar_t* name) const
00097     {
00098     /// trigger the building of a map when the collection reaches the threshold size.
00099         ((FdoNamedCollection<OBJ,EXC>*) this)->InitMap();
00100 
00101         OBJ* obj = NULL;
00102 
00103         if ( mpNameMap ) {
00104     /// Accessing the map is faster for large collections, so use it if built.
00105             obj = GetMap(name);
00106 
00107     /// If the object name can't be modified then we're done.
00108     /// Otherwise, there's a chance the object name was modified,
00109     /// meaning that it can be in the collection but not the map,
00110     /// or in the wrong place in the map.
00111             if ( (obj != NULL) && !obj->CanSetName() )
00112                 return(obj);
00113 
00114     /// If the found object's name is the same as the given name
00115     /// then we're done. Otherwise, this object's name has changed
00116     /// and a linear search is needed to find the requested object.
00117             if ( (obj != NULL) && (Compare(obj->GetName(), name) != 0) )
00118                 FDO_SAFE_RELEASE( obj );
00119         }
00120 
00121         if ( obj == NULL ) {
00122     /// No map so do linear search.
00123             for ( FdoInt32 i = 0; i < FdoCollection<OBJ, EXC>::GetCount(); i++ ) {
00124                 OBJ* obj = GetItem(i);
00125 
00126                 if ( Compare(name, obj->GetName()) == 0 )
00127                     return(obj);
00128 
00129                 FDO_SAFE_RELEASE(obj);
00130             }
00131         }
00132 
00133         return (obj);
00134     }
00135 
00136     /// \brief
00137     /// Sets the item in the collection at the specified index to the specified value. Throws an invalid argument exception if the index is out of range.
00138     /// 
00139     /// \param index 
00140     /// Input index
00141     /// \param value 
00142     /// Input value
00143     /// 
00144     /// \return
00145     /// Returns nothing
00146     /// 
00147     virtual void SetItem(FdoInt32 index, OBJ* value)
00148     {
00149         CheckDuplicate( value, index );
00150 
00151     /// Remove the old item from the map 
00152         if ( mpNameMap ) 
00153             RemoveMapAt(index);
00154 
00155     /// Add the new item to the map
00156         if ( mpNameMap && value ) 
00157             InsertMap( value );
00158 
00159     /// Set the new item in the collection.
00160         FdoCollection<OBJ,EXC>::SetItem(index, value);
00161     }
00162 
00163     /// \brief
00164     /// Adds the specified item to the end of the collection. Returns the index of the newly added item.
00165     /// 
00166     /// \param value 
00167     /// Input value
00168     /// 
00169     /// \return
00170     /// Returns the index of the newly added item
00171     /// 
00172     virtual FdoInt32 Add( OBJ* value)
00173     {
00174         CheckDuplicate( value, -1 );
00175 
00176     /// Insert the new item in the map
00177         if ( mpNameMap && value ) {
00178             InsertMap(value);
00179         }
00180 
00181     /// add it to the list.
00182         return( FdoCollection<OBJ,EXC>::Add(value) );
00183     }
00184 
00185     /// \brief
00186     /// Inserts the specified item at the specified index within the collection. 
00187     /// Items following the insertion point are moved down to accommodate the new item. 
00188     /// Throws an invalid argument exception if the specified index is out of range.
00189     /// 
00190     /// \param index 
00191     /// Input index
00192     /// \param value 
00193     /// Input value
00194     /// 
00195     /// \return
00196     /// Returns nothing
00197     /// 
00198     virtual void Insert( FdoInt32 item, OBJ* value)
00199     {
00200         CheckDuplicate( value, -1 );
00201 
00202     /// Insert the new item in the map
00203         if ( mpNameMap ) {
00204             InsertMap(value);
00205         }
00206 
00207     /// Insert it in the list
00208         return( FdoCollection<OBJ,EXC>::Insert(item, value) );
00209     }
00210 
00211     /// \brief
00212     /// Removes all items from the collection.
00213     /// 
00214     /// \return
00215     /// Returns nothing
00216     /// 
00217     virtual void Clear()
00218     {
00219     /// Clear the map
00220         if (mpNameMap ) {
00221             delete mpNameMap;
00222             mpNameMap = NULL;
00223         }
00224 
00225     /// Clear the list
00226         FdoCollection<OBJ,EXC>::Clear();
00227     }
00228 
00229     /// \brief
00230     /// Removes the specified item from the collection. Throws an invalid argument exception if the item does not exist within the collection.
00231     /// 
00232     /// \param value 
00233     /// Input value
00234     /// 
00235     /// \return
00236     /// Returns nothing
00237     /// 
00238     virtual void Remove(const OBJ* value)
00239     {
00240     /// Remove the item from the map.
00241         if ( mpNameMap ) 
00242             RemoveMap( value );
00243 
00244     /// Remove it from the list
00245         FdoCollection<OBJ,EXC>::Remove(value);
00246     }
00247 
00248     /// \brief
00249     /// Removes the specified item from the collection. Throws an invalid argument exception if the item does not exist within the collection.
00250     /// 
00251     /// \param index 
00252     /// Input index
00253     /// 
00254     /// \return
00255     /// Returns nothing
00256     /// 
00257     virtual void RemoveAt(FdoInt32 index)
00258     {
00259     /// Remove the item from the map.
00260         if ( mpNameMap ) 
00261             RemoveMapAt(index);
00262 
00263     /// Remove it from the list
00264         FdoCollection<OBJ,EXC>::RemoveAt(index);
00265     }
00266 
00267     /// \brief
00268     /// Returns true if the collection contains the specified item, false otherwise.
00269     /// 
00270     /// \param value 
00271     /// Input value
00272     /// 
00273     /// \return
00274     /// Returns true if the collection contains the specified item, false otherwise
00275     /// 
00276     virtual bool Contains(const OBJ* value) const
00277     {
00278     /// trigger the building of a map when the collection reaches the threshold size.
00279         ((FdoNamedCollection<OBJ,EXC>*) this)->InitMap();
00280 
00281         if (mpNameMap )
00282         {
00283     /// If map is built, use it since it is faster. 
00284             FdoPtr <FdoIDisposable> temp = GetMap (((OBJ*)value)->GetName());
00285             bool ret = (temp != NULL);
00286             return (ret);
00287         }
00288         else // Otherwise, linear search
00289         {
00290             FdoString * valueName = ((OBJ*)value)->GetName();
00291             FdoInt32 count = FdoCollection<OBJ, EXC>::GetCount();
00292             bool ret = false;
00293             for (FdoInt32 i = 0; !ret && i < count; i++)
00294             {
00295                 FdoPtr<OBJ> item = GetItem(i);
00296                 FdoString * itemName = item->GetName();
00297                 ret = (Compare(itemName, valueName)==0);
00298             }
00299             return ret;
00300         }
00301     }
00302 
00303     /// \brief
00304     /// Returns true if the collection contains the specified item, false otherwise.
00305     /// 
00306     /// \param name 
00307     /// Input the item name
00308     /// 
00309     /// \return
00310     /// Returns true if the collection contains the specified item, false otherwise
00311     /// 
00312     virtual bool Contains(FdoString* name) const
00313     {
00314         OBJ* item = FindItem(name);
00315         bool found = (item != NULL);
00316 
00317         FDO_SAFE_RELEASE(item);
00318 
00319         return(found);
00320     }
00321 
00322     /// \brief
00323     /// Returns the index of the specified item in the collection or -1 if the item does not exist.
00324     /// 
00325     /// \param value 
00326     /// Input value
00327     /// 
00328     /// \return
00329     /// Returns the index of the specified item in the collection or -1 if the item does not exist
00330     /// 
00331     virtual FdoInt32 IndexOf(const OBJ* value) const
00332     {
00333         return(FdoCollection<OBJ,EXC>::IndexOf(value));
00334     }
00335 
00336     /// \brief
00337     /// Returns the index of the specified item (by name) in the collection or -1 if the item does not exist.
00338     /// 
00339     /// \param name 
00340     /// Input the item name
00341     /// 
00342     /// \return
00343     /// Returns the index of the specified item in the collection or -1 if the item does not exist
00344     /// 
00345     virtual FdoInt32 IndexOf(FdoString* name) const
00346     {
00347         if (name == NULL)
00348             throw EXC::Create(FdoException::NLSGetMessage(FDO_NLSID(FDO_2_BADPARAMETER)));
00349 
00350         FdoInt32    size = FdoCollection <OBJ, EXC>::GetCount();
00351         for (FdoInt32 i = 0; i < size; i++)
00352         {
00353             FdoPtr<OBJ> pitem = FdoNamedCollection<OBJ, EXC>::GetItem(i);
00354             if (pitem != NULL && pitem->GetName() != (FdoString*) NULL && Compare(name, pitem->GetName()) == 0) {
00355                 return i;
00356             }
00357         }
00358 
00359         return(-1);
00360     }
00361 
00362 protected:
00363     FdoNamedCollection( bool caseSensitive = true )
00364     {
00365         mbCaseSensitive = caseSensitive;
00366         mpNameMap = NULL;
00367     }
00368 
00369     virtual ~FdoNamedCollection(void)
00370     {
00371         if (mpNameMap ) 
00372             delete mpNameMap;
00373     }
00374 
00375 /// \cond DOXYGEN-IGNORE
00376     int Compare( FdoString* str1, FdoString* str2 ) const
00377     {
00378         if ( mbCaseSensitive )
00379             return ( wcscmp(str1, str2) );
00380 
00381     /// Try case-insensitive comparison.
00382 #ifdef _WIN32
00383         return ( _wcsicmp(str1, str2) );
00384 #else
00385         return ( wcscasecmp(str1, str2) );
00386 #endif
00387     }
00388 
00389     void CheckDuplicate( OBJ* item, FdoInt32 index )
00390     {
00391         FdoPtr<OBJ> foundItem1 = FindItem( item->GetName() );
00392         FdoPtr<OBJ> foundItem2;
00393 
00394         if ( index >= 0 ) {
00395             foundItem2 = GetItem(index);
00396         }
00397 
00398         if ( (foundItem1 !=NULL) && (foundItem1.p != foundItem2.p ) ) { 
00399             throw EXC::Create(
00400                 FdoException::NLSGetMessage(
00401                     FDO_NLSID(FDO_45_ITEMINCOLLECTION),
00402                     (FdoString*) item->GetName()
00403                 )
00404             );
00405         }
00406     }
00407 /// \endcond
00408 
00409 private:
00410     void InitMap()
00411     {
00412     /// Build the map if not already built and the collection has hit the threshold
00413         if ( !mpNameMap && ( FdoCollection <OBJ, EXC>::GetCount() > FDO_COLL_MAP_THRESHOLD ) ) {
00414             mpNameMap = new std::map<FdoStringP,OBJ*>();
00415 
00416     /// Put all the current elements into the map
00417             for ( FdoInt32 i = (FdoCollection <OBJ, EXC>::GetCount() - 1); i >= 0; i-- ) 
00418                 InsertMap( FdoPtr<OBJ>(GetItem(i)) );
00419         }
00420     }
00421 
00422     void InsertMap( OBJ* value ) const
00423     {
00424     /// Add an element to the map. Elements are keyed by name, which may or may not be case sensitive.
00425     /// Case insensitive names are stored in lower case.
00426         if ( mbCaseSensitive ) 
00427             mpNameMap->insert( std::pair<FdoStringP,OBJ*> ( value->GetName(), value ) );
00428         else
00429             mpNameMap->insert( std::pair<FdoStringP,OBJ*> ( FdoStringP(value->GetName()).Lower(), value ) );            
00430     }
00431 
00432     /// Remove the element at the specified index, from the map
00433     void RemoveMapAt( FdoInt32 index )
00434     {
00435     /// Remove the old item from the map 
00436         OBJ* pItem = FdoCollection<OBJ,EXC>::GetItem(index);
00437 
00438         if ( pItem ) {
00439             RemoveMap( pItem );
00440             pItem->Release();
00441         }
00442     }
00443 
00444     /// Remove the given element from the map.
00445     void RemoveMap( const OBJ* value )
00446     {
00447     /// handle the current case sensitivity of the element name.
00448         if ( mbCaseSensitive ) 
00449             mpNameMap->erase( FdoStringP( ((OBJ*)value)->GetName() ) );
00450         else
00451             mpNameMap->erase( FdoStringP( ((OBJ*)value)->GetName() ).Lower() );            
00452     }
00453 
00454     /// Get the named element from the map. Returns NULL if element not in map.
00455     OBJ* GetMap( const wchar_t* name ) const
00456     {
00457         OBJ* pItem = NULL;
00458 
00459         typename std::map <FdoStringP,OBJ*> :: const_iterator iter;
00460 
00461         if ( mbCaseSensitive )
00462             iter = mpNameMap->find( FdoStringP(name) );
00463         else 
00464     /// Case sensitive names are stored in lower case.
00465             iter = mpNameMap->find( FdoStringP(name).Lower() );
00466 
00467         if ( iter != mpNameMap->end() ) { 
00468             pItem = (OBJ*) iter->second;
00469             FDO_SAFE_ADDREF(pItem);
00470         }
00471 
00472         return( pItem );
00473     }
00474 
00475     bool mbCaseSensitive;
00476 
00477     /// A map keyed by name, to help performance of large collections.
00478     std::map<FdoStringP,OBJ*> *mpNameMap;
00479 };
00480 
00481 #endif
00482 
00483 

Comments or suggestions? Send us feedback.