FDO API Reference Feature Data Objects
Main Page | Modules | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members

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             // The map can become stale if the name of any item changed after it
00108             // was added to the map. Check if the object's class allows name modifications.
00109             // Do this check on found object by default
00110             OBJ* canSetObj = obj;
00111 
00112             if ( !canSetObj && (FdoCollection<OBJ, EXC>::GetCount() > 0))
00113                 // Object was not found, do check on first object in collection.
00114                 canSetObj = GetItem(0);
00115 
00116             // Check if object name can be modified. 
00117             bool canSetName = canSetObj ? canSetObj->CanSetName() : true;
00118 
00119             // If the object name can't be modified then we're done.
00120             // Otherwise, there's a chance the object name was modified,
00121             // meaning that it can be in the collection but not the map,
00122             // or in the wrong place in the map.
00123             if ( !canSetName )
00124                 return(obj);
00125 
00126             // If the found object's name is the same as the given name
00127             // then we're done. Otherwise, this object's name has changed
00128             // and a linear search is needed to find the requested object.
00129             if ( (obj != NULL) && (Compare(obj->GetName(), name) != 0) )
00130                 FDO_SAFE_RELEASE( obj );
00131         }
00132 
00133         if ( obj == NULL ) {
00134             // No map or map might be stale, so do linear search.
00135             for ( FdoInt32 i = 0; i < FdoCollection<OBJ, EXC>::GetCount(); i++ ) {
00136                 OBJ* obj = GetItem(i);
00137 
00138                 if ( Compare(name, obj->GetName()) == 0 )
00139                     return(obj);
00140 
00141                 FDO_SAFE_RELEASE(obj);
00142             }
00143         }
00144 
00145         return (obj);
00146     }
00147 
00148     /// \brief
00149     /// 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.
00150     /// 
00151     /// \param index 
00152     /// Input index
00153     /// \param value 
00154     /// Input value
00155     /// 
00156     /// \return
00157     /// Returns nothing
00158     /// 
00159     virtual void SetItem(FdoInt32 index, OBJ* value)
00160     {
00161         CheckDuplicate( value, index );
00162 
00163     // Remove the old item from the map 
00164         if ( mpNameMap ) 
00165             RemoveMapAt(index);
00166 
00167     // Add the new item to the map
00168         if ( mpNameMap && value ) 
00169             InsertMap( value );
00170 
00171     // Set the new item in the collection.
00172         FdoCollection<OBJ,EXC>::SetItem(index, value);
00173     }
00174 
00175     /// \brief
00176     /// Adds the specified item to the end of the collection. Returns the index of the newly added item.
00177     /// 
00178     /// \param value 
00179     /// Input value
00180     /// 
00181     /// \return
00182     /// Returns the index of the newly added item
00183     /// 
00184     virtual FdoInt32 Add( OBJ* value)
00185     {
00186         CheckDuplicate( value, -1 );
00187 
00188     // Insert the new item in the map
00189         if ( mpNameMap && value ) {
00190             InsertMap(value);
00191         }
00192 
00193     // add it to the list.
00194         return( FdoCollection<OBJ,EXC>::Add(value) );
00195     }
00196 
00197     /// \brief
00198     /// Inserts the specified item at the specified index within the collection. 
00199     /// Items following the insertion point are moved down to accommodate the new item. 
00200     /// Throws an invalid argument exception if the specified index is out of range.
00201     /// 
00202     /// \param item 
00203     /// Input item
00204     /// \param value 
00205     /// Input value
00206     /// 
00207     /// \return
00208     /// Returns nothing
00209     /// 
00210     virtual void Insert( FdoInt32 item, OBJ* value)
00211     {
00212         CheckDuplicate( value, -1 );
00213 
00214     // Insert the new item in the map
00215         if ( mpNameMap ) {
00216             InsertMap(value);
00217         }
00218 
00219     // Insert it in the list
00220         return( FdoCollection<OBJ,EXC>::Insert(item, value) );
00221     }
00222 
00223     /// \brief
00224     /// Removes all items from the collection.
00225     /// 
00226     /// \return
00227     /// Returns nothing
00228     /// 
00229     virtual void Clear()
00230     {
00231     // Clear the map
00232         if (mpNameMap ) {
00233             delete mpNameMap;
00234             mpNameMap = NULL;
00235         }
00236 
00237     // Clear the list
00238         FdoCollection<OBJ,EXC>::Clear();
00239     }
00240 
00241     /// \brief
00242     /// Removes the specified item from the collection. Throws an invalid argument exception if the item does not exist within the collection.
00243     /// 
00244     /// \param value 
00245     /// Input value
00246     /// 
00247     /// \return
00248     /// Returns nothing
00249     /// 
00250     virtual void Remove(const OBJ* value)
00251     {
00252     // Remove the item from the map.
00253         if ( mpNameMap ) 
00254             RemoveMap( value );
00255 
00256     // Remove it from the list
00257         FdoCollection<OBJ,EXC>::Remove(value);
00258     }
00259 
00260     /// \brief
00261     /// Removes the specified item from the collection. Throws an invalid argument exception if the item does not exist within the collection.
00262     /// 
00263     /// \param index 
00264     /// Input index
00265     /// 
00266     /// \return
00267     /// Returns nothing
00268     /// 
00269     virtual void RemoveAt(FdoInt32 index)
00270     {
00271     // Remove the item from the map.
00272         if ( mpNameMap ) 
00273             RemoveMapAt(index);
00274 
00275     // Remove it from the list
00276         FdoCollection<OBJ,EXC>::RemoveAt(index);
00277     }
00278 
00279     /// \brief
00280     /// Returns true if the collection contains the specified item, false otherwise.
00281     /// 
00282     /// \param value 
00283     /// Input value
00284     /// 
00285     /// \return
00286     /// Returns true if the collection contains the specified item, false otherwise
00287     /// 
00288     virtual bool Contains(const OBJ* value) const
00289     {
00290     // trigger the building of a map when the collection reaches the threshold size.
00291         ((FdoNamedCollection<OBJ,EXC>*) this)->InitMap();
00292 
00293         if (mpNameMap )
00294         {
00295     // If map is built, use it since it is faster. 
00296             FdoPtr <FdoIDisposable> temp = GetMap (((OBJ*)value)->GetName());
00297             bool ret = (temp != NULL);
00298             return (ret);
00299         }
00300         else // Otherwise, linear search
00301         {
00302             FdoString * valueName = ((OBJ*)value)->GetName();
00303             FdoInt32 count = FdoCollection<OBJ, EXC>::GetCount();
00304             bool ret = false;
00305             for (FdoInt32 i = 0; !ret && i < count; i++)
00306             {
00307                 FdoPtr<OBJ> item = GetItem(i);
00308                 FdoString * itemName = item->GetName();
00309                 ret = (Compare(itemName, valueName)==0);
00310             }
00311             return ret;
00312         }
00313     }
00314 
00315     /// \brief
00316     /// Returns true if the collection contains the specified item, false otherwise.
00317     /// 
00318     /// \param name 
00319     /// Input the item name
00320     /// 
00321     /// \return
00322     /// Returns true if the collection contains the specified item, false otherwise
00323     /// 
00324     virtual bool Contains(FdoString* name) const
00325     {
00326         OBJ* item = FindItem(name);
00327         bool found = (item != NULL);
00328 
00329         FDO_SAFE_RELEASE(item);
00330 
00331         return(found);
00332     }
00333 
00334     /// \brief
00335     /// Returns the index of the specified item in the collection or -1 if the item does not exist.
00336     /// 
00337     /// \param value 
00338     /// Input value
00339     /// 
00340     /// \return
00341     /// Returns the index of the specified item in the collection or -1 if the item does not exist
00342     /// 
00343     virtual FdoInt32 IndexOf(const OBJ* value) const
00344     {
00345         return(FdoCollection<OBJ,EXC>::IndexOf(value));
00346     }
00347 
00348     /// \brief
00349     /// Returns the index of the specified item (by name) in the collection or -1 if the item does not exist.
00350     /// 
00351     /// \param name 
00352     /// Input the item name
00353     /// 
00354     /// \return
00355     /// Returns the index of the specified item in the collection or -1 if the item does not exist
00356     /// 
00357     virtual FdoInt32 IndexOf(FdoString* name) const
00358     {
00359         if (name == NULL)
00360             throw EXC::Create(FdoException::NLSGetMessage(FDO_NLSID(FDO_137_NAMED_COLLECTION_INDEX_NAME_ERROR),
00361                                                           L"FdoNamedCollection::IndexOf"));
00362 
00363         FdoInt32    size = FdoCollection <OBJ, EXC>::GetCount();
00364         for (FdoInt32 i = 0; i < size; i++)
00365         {
00366             FdoPtr<OBJ> pitem = FdoNamedCollection<OBJ, EXC>::GetItem(i);
00367             if (pitem != NULL && pitem->GetName() != (FdoString*) NULL && Compare(name, pitem->GetName()) == 0) {
00368                 return i;
00369             }
00370         }
00371 
00372         return(-1);
00373     }
00374 
00375 protected:
00376     FdoNamedCollection( bool caseSensitive = true )
00377     {
00378         mbCaseSensitive = caseSensitive;
00379         mpNameMap = NULL;
00380     }
00381 
00382     virtual ~FdoNamedCollection(void)
00383     {
00384         if (mpNameMap ) 
00385             delete mpNameMap;
00386     }
00387 
00388 /// \cond DOXYGEN-IGNORE
00389     int Compare( FdoString* str1, FdoString* str2 ) const
00390     {
00391         if ( mbCaseSensitive )
00392             return ( wcscmp(str1, str2) );
00393 
00394     // Try case-insensitive comparison.
00395 #ifdef _WIN32
00396         return ( _wcsicmp(str1, str2) );
00397 #else
00398         return ( wcscasecmp(str1, str2) );
00399 #endif
00400     }
00401 
00402     void CheckDuplicate( OBJ* item, FdoInt32 index )
00403     {
00404         FdoPtr<OBJ> foundItem1 = FindItem( item->GetName() );
00405         FdoPtr<OBJ> foundItem2;
00406 
00407         if ( index >= 0 ) {
00408             foundItem2 = GetItem(index);
00409         }
00410 
00411         if ( (foundItem1 !=NULL) && (foundItem1.p != foundItem2.p ) ) { 
00412             throw EXC::Create(
00413                 FdoException::NLSGetMessage(
00414                     FDO_NLSID(FDO_45_ITEMINCOLLECTION),
00415                     (FdoString*) item->GetName()
00416                 )
00417             );
00418         }
00419     }
00420 /// \endcond
00421 
00422 private:
00423     void InitMap()
00424     {
00425     // Build the map if not already built and the collection has hit the threshold
00426         if ( !mpNameMap && ( FdoCollection <OBJ, EXC>::GetCount() > FDO_COLL_MAP_THRESHOLD ) ) {
00427             mpNameMap = new std::map<FdoStringP,OBJ*>();
00428 
00429     // Put all the current elements into the map
00430             for ( FdoInt32 i = (FdoCollection <OBJ, EXC>::GetCount() - 1); i >= 0; i-- ) 
00431                 InsertMap( FdoPtr<OBJ>(GetItem(i)) );
00432         }
00433     }
00434 
00435     void InsertMap( OBJ* value ) const
00436     {
00437     // Add an element to the map. Elements are keyed by name, which may or may not be case sensitive.
00438     // Case insensitive names are stored in lower case.
00439         if ( mbCaseSensitive ) 
00440             mpNameMap->insert( std::pair<FdoStringP,OBJ*> ( FdoStringP(value->GetName(),true), value ) );
00441         else
00442             mpNameMap->insert( std::pair<FdoStringP,OBJ*> ( FdoStringP(value->GetName(),true).Lower(), value ) );            
00443     }
00444 
00445     // Remove the element at the specified index, from the map
00446     void RemoveMapAt( FdoInt32 index )
00447     {
00448     // Remove the old item from the map 
00449         OBJ* pItem = FdoCollection<OBJ,EXC>::GetItem(index);
00450 
00451         if ( pItem ) {
00452             RemoveMap( pItem );
00453             pItem->Release();
00454         }
00455     }
00456 
00457     // Remove the given element from the map.
00458     void RemoveMap( const OBJ* value )
00459     {
00460     // handle the current case sensitivity of the element name.
00461         if ( mbCaseSensitive ) 
00462             mpNameMap->erase( FdoStringP( ((OBJ*)value)->GetName() ) );
00463         else
00464             mpNameMap->erase( FdoStringP( ((OBJ*)value)->GetName() ).Lower() );            
00465     }
00466 
00467     // Get the named element from the map. Returns NULL if element not in map.
00468     OBJ* GetMap( const wchar_t* name ) const
00469     {
00470         OBJ* pItem = NULL;
00471 
00472         typename std::map <FdoStringP,OBJ*> :: const_iterator iter;
00473 
00474         if ( mbCaseSensitive )
00475             iter = mpNameMap->find( FdoStringP(name) );
00476         else 
00477     // Case sensitive names are stored in lower case.
00478             iter = mpNameMap->find( FdoStringP(name).Lower() );
00479 
00480         if ( iter != mpNameMap->end() ) { 
00481             pItem = (OBJ*) iter->second;
00482             FDO_SAFE_ADDREF(pItem);
00483         }
00484 
00485         return( pItem );
00486     }
00487 
00488     bool mbCaseSensitive;
00489 
00490     // A map keyed by name, to help performance of large collections.
00491     std::map<FdoStringP,OBJ*> *mpNameMap;
00492 };
00493 
00494 #endif
00495 
00496 

Comments or suggestions? Send us feedback.