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

Comments or suggestions? Send us feedback.