- Main Page
- Related Pages
- Modules
- Classes
- Files

# 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

Please send us your comment about this page