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 item 
00191     /// Input item
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_137_NAMED_COLLECTION_INDEX_NAME_ERROR),
00349                                                           L"FdoNamedCollection::IndexOf"));
00350 
00351         FdoInt32    size = FdoCollection <OBJ, EXC>::GetCount();
00352         for (FdoInt32 i = 0; i < size; i++)
00353         {
00354             FdoPtr<OBJ> pitem = FdoNamedCollection<OBJ, EXC>::GetItem(i);
00355             if (pitem != NULL && pitem->GetName() != (FdoString*) NULL && Compare(name, pitem->GetName()) == 0) {
00356                 return i;
00357             }
00358         }
00359 
00360         return(-1);
00361     }
00362 
00363 protected:
00364     FdoNamedCollection( bool caseSensitive = true )
00365     {
00366         mbCaseSensitive = caseSensitive;
00367         mpNameMap = NULL;
00368     }
00369 
00370     virtual ~FdoNamedCollection(void)
00371     {
00372         if (mpNameMap ) 
00373             delete mpNameMap;
00374     }
00375 
00376 /// \cond DOXYGEN-IGNORE
00377     int Compare( FdoString* str1, FdoString* str2 ) const
00378     {
00379         if ( mbCaseSensitive )
00380             return ( wcscmp(str1, str2) );
00381 
00382     // Try case-insensitive comparison.
00383 #ifdef _WIN32
00384         return ( _wcsicmp(str1, str2) );
00385 #else
00386         return ( wcscasecmp(str1, str2) );
00387 #endif
00388     }
00389 
00390     void CheckDuplicate( OBJ* item, FdoInt32 index )
00391     {
00392         FdoPtr<OBJ> foundItem1 = FindItem( item->GetName() );
00393         FdoPtr<OBJ> foundItem2;
00394 
00395         if ( index >= 0 ) {
00396             foundItem2 = GetItem(index);
00397         }
00398 
00399         if ( (foundItem1 !=NULL) && (foundItem1.p != foundItem2.p ) ) { 
00400             throw EXC::Create(
00401                 FdoException::NLSGetMessage(
00402                     FDO_NLSID(FDO_45_ITEMINCOLLECTION),
00403                     (FdoString*) item->GetName()
00404                 )
00405             );
00406         }
00407     }
00408 /// \endcond
00409 
00410 private:
00411     void InitMap()
00412     {
00413     // Build the map if not already built and the collection has hit the threshold
00414         if ( !mpNameMap && ( FdoCollection <OBJ, EXC>::GetCount() > FDO_COLL_MAP_THRESHOLD ) ) {
00415             mpNameMap = new std::map<FdoStringP,OBJ*>();
00416 
00417     // Put all the current elements into the map
00418             for ( FdoInt32 i = (FdoCollection <OBJ, EXC>::GetCount() - 1); i >= 0; i-- ) 
00419                 InsertMap( FdoPtr<OBJ>(GetItem(i)) );
00420         }
00421     }
00422 
00423     void InsertMap( OBJ* value ) const
00424     {
00425     // Add an element to the map. Elements are keyed by name, which may or may not be case sensitive.
00426     // Case insensitive names are stored in lower case.
00427         if ( mbCaseSensitive ) 
00428             mpNameMap->insert( std::pair<FdoStringP,OBJ*> ( value->GetName(), value ) );
00429         else
00430             mpNameMap->insert( std::pair<FdoStringP,OBJ*> ( FdoStringP(value->GetName()).Lower(), value ) );            
00431     }
00432 
00433     // Remove the element at the specified index, from the map
00434     void RemoveMapAt( FdoInt32 index )
00435     {
00436     // Remove the old item from the map 
00437         OBJ* pItem = FdoCollection<OBJ,EXC>::GetItem(index);
00438 
00439         if ( pItem ) {
00440             RemoveMap( pItem );
00441             pItem->Release();
00442         }
00443     }
00444 
00445     // Remove the given element from the map.
00446     void RemoveMap( const OBJ* value )
00447     {
00448     // handle the current case sensitivity of the element name.
00449         if ( mbCaseSensitive ) 
00450             mpNameMap->erase( FdoStringP( ((OBJ*)value)->GetName() ) );
00451         else
00452             mpNameMap->erase( FdoStringP( ((OBJ*)value)->GetName() ).Lower() );            
00453     }
00454 
00455     // Get the named element from the map. Returns NULL if element not in map.
00456     OBJ* GetMap( const wchar_t* name ) const
00457     {
00458         OBJ* pItem = NULL;
00459 
00460         typename std::map <FdoStringP,OBJ*> :: const_iterator iter;
00461 
00462         if ( mbCaseSensitive )
00463             iter = mpNameMap->find( FdoStringP(name) );
00464         else 
00465     // Case sensitive names are stored in lower case.
00466             iter = mpNameMap->find( FdoStringP(name).Lower() );
00467 
00468         if ( iter != mpNameMap->end() ) { 
00469             pItem = (OBJ*) iter->second;
00470             FDO_SAFE_ADDREF(pItem);
00471         }
00472 
00473         return( pItem );
00474     }
00475 
00476     bool mbCaseSensitive;
00477 
00478     // A map keyed by name, to help performance of large collections.
00479     std::map<FdoStringP,OBJ*> *mpNameMap;
00480 };
00481 
00482 #endif
00483 
00484 

Comments or suggestions? Send us feedback.