/*!
 * \file    OMS_Object.hpp
 * \author  IvanS, MarkusS, PeterG
 * \brief   Base classes for persistent objects
 */
/*

    ========== licence begin  GPL
    Copyright (c) 2002-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



*/


#ifndef __OMS_OBJECT_HPP
#define __OMS_OBJECT_HPP

#include "Oms/OMS_Handle.hpp"
#include "Oms/OMS_TypedOid.hpp"
#include "Oms/OMS_AbstractObject.hpp"
#include "Oms/OMS_ObjByClsIter.hpp"
#include "Oms/OMS_KeyRangeIter.hpp"


#ifdef	LIVECACHE_INTGUIDS
#define LIVECACHE_OBJECT_TMPLPAR(T, guid)	<T, guid>
#define LIVECACHE_KEYEDOBJECT_TMPLPAR(T, key, guid)	<T, key, guid>
#else
#define LIVECACHE_OBJECT_TMPLPAR(T, guid)	<T>
#define LIVECACHE_KEYEDOBJECT_TMPLPAR(T, key, guid)	<T, key>
#endif

#ifdef WIN32
// disable warning
#pragma warning(disable: 4624)
#endif

/*------------------------------------------------------------------------------------*/
/// Template for persistent objects
/*!
** The OmsObject<T> template defines the structure and methods of a persistent
** C++ class T.
** Any class becomes persistent when it derives itself from the OmsObject
** template.
*/
template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
class OmsObject : public OmsAbstractObject
{
  friend class OmsHandle;
  friend class OmsOid<T2>;
public:
#ifndef	LIVECACHE_INTGUIDS
  /*! Every persistent class has a GUID, which identifies the class uniquely with respect to the 
  **  liveCache. A GUID generated by means of the GUIDGEN tool must be assigned to the 
  **  guid. This GUID is stored by the liveCache in the catalog, and is used to assign a name 
  **  for the class container.
  */
  static const GUID guid;
#endif

  /// New operator
  inline void* operator new ( size_t sz, OmsHandle&ah, OmsSchemaHandle, OmsContainerNo);
  inline void* operator new ( size_t sz, OmsHandle& ah, OmsRegEnum e);
  inline void* operator new ( size_t sz, OmsHandle& ah, OmsRegEnum e, size_t rsz);

  /// Delete operator
  void operator delete ( void* p, OmsHandle&ah, OmsSchemaHandle, OmsContainerNo) {}
  void operator delete ( void* p, OmsHandle& ah, OmsRegEnum e) {}
  void operator delete ( void* p, OmsHandle& ah, OmsRegEnum e, size_t rsz) {}

  /*------------------------------------------------------------------------------------*/
  /// Get object ID.
  /*!
  ** Provides the OID of the instance.
  */
  inline const OmsOid<T2> omsGetOid() const;

  /*------------------------------------------------------------------------------------*/
  /// Dereference the object for update
  /*!
  ** Notifies the OMS that changes to the instance are intended. As a result, the invoker 
  ** receives a pointer, by means of which the instance can be manipulated.  
  ** The Oms requires this note to store information in case the current subtransaction is 
  ** reset. 
  **
  ** \param h current handle
  ** \param doLock If true, then a lock is also requested for the instance.
  **
  ** \see \ref example_omsForUpdPtr 
  */
  inline T2* omsForUpdPtr(OmsHandle& h, bool doLock=true) const; 

  ///
  inline bool  omsHistoryInUse(OmsHandle&) const;

  /*--------------------------------------------------------------------------------------*/
  /// Deletes all the instances from a class container.
  /*! 
  ** Deletes all the instances of the persistent class in the specified container and schema. 
  ** The method carries out this task considerably faster than a comparable iterator with 
  ** multiple omsDelete calls. Unlike with an iterator of this type, however, the virtual 
  ** omsCleanUp method is not executed here. 
  ** The method fails if a lock within the consistent view cannot be obtained. The application 
  ** must ensure, therefore, that exclusive access to the container is provided. An 
  ** OmsLockObject, for example, can be used for this purpose.
  **
  ** \param h handle,
  ** \param sh schema of the objects which should be deleted,
  ** \param ContainerNo  container number of the objects which should be deleted.
  **
  ** \see \ref example_omsDeleteAll
  */
  static void  omsDeleteAll ( OmsHandle &h, OmsSchemaHandle sh, OmsContainerNo ContainerNo )
  { OmsAbstractObject::omsDeleteAll( h, guid, sh, ContainerNo ); }

  /*--------------------------------------------------------------------------------------*/
  ///  Provides an iterator for a sequential scan across all the instances of the class. 
  /*! 
  ** Provides an iterator, which yields all instances of T stored in the containers 
  ** identified by schema and containerNo.
  */
  static OmsObjByClsIter<T2> omsAllOids( OmsHandle&h, OmsSchemaHandle sh, OmsContainerNo ContainerNo, int maxBufferSize = 20)
  { 
    OmsObjByClsIterBase iter = OmsAbstractObject::omsAllOids( h, guid, sh, ContainerNo, maxBufferSize );
    return *REINTERPRET_CAST (OmsObjByClsIter<T2>*, &iter);
  }

  /*--------------------------------------------------------------------------------------*/
  /// Checks whether object, specified by oid, belongs to an instance of type OmsObject<T>
  /*!
  ** Provides true if an instance of OmsObject<T> is identified by oid; otherwise, false.
  **
  ** \param h current handle
  ** \param oid oid to be checked
  ** \return true if object, specified by oid, belongs to current instance of OmsObject<T>
  */
  static bool omsOidIsInstanceOf(OmsHandle &h, const OmsExternalOid &oid) 
  { return T2::omsCheckOid(h, reinterpret_cast<const OmsObjectId&>(oid)); }

  /*--------------------------------------------------------------------------------------*/
  /// Registers the class in the OMS.
  /*!
  ** Before a container is accessed for the first time, it must be registered. Registration 
  ** defines the schema to which a container belongs and the size of the instances stored in it. 
  ** The method takes the properties of the class to which the instances belong from an 
  ** instance of the class, which is generated by means of a special new operator of the class. 
  ** To use this new operator, each persistent class requires a default constructor.  
  ** During registration, the liveCache checks whether the container already exists. If this is 
  ** not the case, it is generated implicitly. 
  ** More than one container can be created for the instances of a class. This is expedient if 
  ** the instances can be grouped according to different criteria, and these criteria are used to 
  ** execute scan operations. omsRegClass must then be invoked with the corresponding 
  ** container number for each container of the class. 
  ** Each time the liveCache is started, a container must be registered before it is accessed for 
  ** the first time. Multiple registrations of a container are not critical. 
  ** The macro OMS_REG_CLASS facilitates registering the class.
  **
  ** \param h current handle,
  ** \param className name of the class to be registered,
  ** \param size size of the object to be registered,
  ** \param pObj pointer to an instance of the class to be registered,
  ** \param sh schema in which the class should be registered,
  ** \param containerNo  number of the container in which the class should be registered.
  ** 
  ** \see \ref example_omsRegClass
  */
  static void omsRegClass( OmsHandle &h, const char* className, 
    size_t size, OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)* pObj, 
    OmsSchemaHandle sh, OmsContainerNo containerNo)
  { OmsAbstractObject::omsRegClass( h, guid, className, size, sizeof(T2), NULL, pObj, sh, containerNo ); }

  /*--------------------------------------------------------------------------------------*/
  /// Registers the class in the OMS.
  /*!
  ** Real derivations in the C++ sense cannot be used when persistent classes are defined. 
  ** This is mainly due to the fact that the OMS is implemented exclusively with C++ means 
  ** and without pre-compiler.  
  ** Each persistent class must have exactly one virtual method table. This means that 
  ** multiple inheritance is excluded. 
  ** To allow the advantages of inheritance to be leveraged despite this, the OMS offers a 
  ** method for simulating inheritance with the help of templates and registration. This 
  ** variant of omsRegClass is used to inform the OMS that the class to be registered must be 
  ** understood as a derivation of the class identified by BaseClassGuid. The meaning of the 
  ** other parameters of the method can be derived from the description of the first variant of 
  ** omsRegClass. 
  ** The macro OMS_REG_CLASS_INH facilitates this type of registration.
  **
  ** \param h current handle,
  ** \param className name of the class to be registered,
  ** \param size size of the object to be registered,
  ** \param baseClassGuid guid of the base class,
  ** \param pObj pointer to an instance of the class to be registered,
  ** \param sh schema in which the class should be registered,
  ** \param containerNo  number of the container in which the class should be registered.
  ** 
  ** \see \ref example_omsRegClassInh
  */
  static void omsRegClass( OmsHandle& h, const char* className, 
    size_t size, const ClassIDRef baseClassGuid, OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)* pObj, 
    OmsSchemaHandle sh, OmsContainerNo containerNo)
  { 
#ifdef LIVECACHE_INTGUIDS
    OmsAbstractObject::omsRegClass( h, guid, className, size, sizeof(T2), baseClassGuid, pObj, sh, containerNo ); 
#else
    OmsAbstractObject::omsRegClass( h, guid, className, size, sizeof(T2), &baseClassGuid, pObj, sh, containerNo ); 
#endif
  }

  /*--------------------------------------------------------------------------------------*/  
  /// .
  static bool omsCheckOid(OmsHandle &h, const OmsObjectId &id)
  { return h.omsCheckOid(id, guid); }

  /*--------------------------------------------------------------------------------------*/
  /// Returns the guid of the corresponding class.
  static const ClassIDRef omsGetGuid()
  { return guid; }

  /*--------------------------------------------------------------------------------------*/
  /// .
  static const OmsObjectId& omsCastOid(OmsHandle &h, const OmsObjectId &from)
  { return h.omsCastOid(guid, from); }

#ifdef	SAPDB_LVCSIM_EXTRAS
/// /name Extras for liveCache simulator
//@{
#ifdef	SAPDB_LVCSIM_DEPRECATED
  static inline void omsRegClass(OmsHandle&h, size_t sz, const char*cls_name, const OmsAbstractObject*pobj, OmsSchemaHandle sh, OmsContainerNo cno )
  { OmsAbstractObject::omsRegClass(h, guid, sz, sizeof(T2), cls_name, 0, pobj, sh, cno ); }
  static inline void omsRegClass(OmsHandle&h, size_t sz, const char*cls_name, const ClassIDRef base_class, const OmsAbstractObject*pobj, OmsSchemaHandle sh, OmsContainerNo cno )
#ifdef LIVECACHE_INTGUIDS 
  { OmsAbstractObject::omsRegClass(h, guid, sz, sizeof(T2), cls_name, base_class, pobj, sh, cno ); }
#else
  { OmsAbstractObject::omsRegClass(h, guid, sz, sizeof(T2), cls_name, &base_class, pobj, sh, cno ); }
#endif
#endif
//@}
#endif

protected:
  OmsObject() {}

#if defined(WIN32) && defined(I386) && !defined(BIT64)
#pragma warning(disable: 4624)  // for .Net
private:
#endif

	~OmsObject() {} // derived class must not be destructed

private:
  static const T2* omsDeRef( const OmsOid<T2>&aoid, OmsHandle&ah )   
  { 
    return (T2*)OmsAbstractObject::omsDeRef( aoid, ah, guid );
  }
  static T2* omsDeRefForUpd( const OmsOid<T2>&aoid, OmsHandle&ah, bool doLock )
  { 
    return (T2*)OmsAbstractObject::omsDeRefForUpd( aoid, ah, guid, doLock );
  }
  OmsObject( const OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)& );       // no copy
  void operator= ( const OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)& ); // no assignment
};



/*------------------------------------------------------------------------------------*/
/// Template for persistent objects with key
/*!
**  The OmsKeyedObj template defines the structure and the methods of a persistent class 
**  \e Obj with a key of the type \e Key. It is derived from OmsObject and, thus, inherits 
**  the basic structure and methods of this object. 
**
** A key must be assigned to each instance of OmsKeyedObject in the new operator. The 
** key value must have the key attribute; in other words, when the new operator is executed 
** within a container, a second instance of the class with the same key value must not exist.
**
** The class Key must implement the methods omsKeyToBinary and omsBinaryToKey. The objects of
** type OmsKeyedObjects are organized as a B*-tree in the liveCache, where the keys are sorted
** using the memcmp semantics. As this sorting is not desired, the method omsKeyToBinary allows
** a user-defined transformation of the key before it is inserted into the B*-tree.  
** Accordingly the method omsBinaryToKey allows the transformation back from the internal
** representation of the key into the external format.
**
** \see \ref oms_OmsKeyedObjectAPI
** \see \ref example_omsBinaryToKey
** \see \ref example_omsKeyedObject
*/
template LIVECACHE_KEYEDOBJECT_TMPLPAR(class Obj, class Key, ClassID guid)
class OmsKeyedObject : public OmsObject LIVECACHE_OBJECT_TMPLPAR(Obj, guid)
{
public:
  /*------------------------------------------------------------------------------------*/
  /// Returns the key of the object
  /*!
  ** Provides the key of an instance of a persistent class derived from OmsKeyedObject.
  **
  ** \param h handle
  ** \param key contains after the call the key of the object
  */
  inline void  omsGetKey( OmsHandle &h, Key& key ) const;

  /*------------------------------------------------------------------------------------*/
  /// Operator new
  /*!
  ** Defines the new operator for persistent classes with a key. The persistent object is 
  ** created in the container identified by schema and containerNo. The key is assigned when 
  ** the object is created and cannot be changed subsequently. The key must be unique in the 
  ** container; in other words, there must not be two instances with the same key at any time. 
  ** This key constraint is guaranteed by the liveCache. 
  ** The instance can be accessed by means of its OID as well as by the key. 
  */
  void* operator new ( size_t sz, OmsHandle&ah, const Key&akey ,OmsSchemaHandle sh, OmsContainerNo ContainerNo)
  { return (void*) OmsAbstractObject::omsNewKeyedObject( ah, guid, (unsigned char*) &akey, sh, ContainerNo ); } 
  void* operator new ( size_t sz, OmsHandle& ah, OmsAbstractObject::OmsRegEnum e)
  { return (void*) OmsAbstractObject::omsNewRegistryObject( sz + sizeof(Key), ah, guid); }
  
  /// Operator delete
  void operator delete (void* p, OmsHandle&ah, const Key&akey ,OmsSchemaHandle sh, OmsContainerNo ContainerNo) {}
  void operator delete (void* p, OmsHandle& ah, OmsAbstractObject::OmsRegEnum e) {}

  /*------------------------------------------------------------------------------------*/
  /// Registers a class
  /*!
  ** Registers a container of a class derived by OmsKeyedObject. To do so, it uses the 
  ** omsRegClass method defined in the OmsHandle. The macro OMS_REG_CLASS 
  ** facilitates registration. 
  ** After the liveCache has been started, each container must be registered at least once by 
  ** means of OMS_REG_CLASS before an instance of the container is accessed for the first 
  ** time. Multiple calls of OMS_REG_CLASS are permissible.
  */
  static void  omsRegClass( OmsHandle&ah, const char* className, size_t sz, OmsObject LIVECACHE_OBJECT_TMPLPAR(Obj, guid)* pobj, OmsSchemaHandle sh, OmsContainerNo cno )
  { OmsAbstractObject::omsRegClassAndKey( ah, guid, className, sz, sizeof(Key), sizeof(Obj), pobj, sh, cno, false ); }
  
  /*------------------------------------------------------------------------------------*/
  /// Registers a class
  /*!
  ** Registers a container of a class derived by OmsKeyedObject. To do so, it uses the 
  ** omsRegClass method defined in the OmsHandle. The macro OMS_REG_CLASS_WITH_PARTITIONED_KEY 
  ** facilitates registration. 
  ** After the liveCache has been started, each container must be registered at least once by 
  ** means of OMS_REG_CLASS before an instance of the container is accessed for the first 
  ** time. Multiple calls of OMS_REG_CLASS are permissible.
  ** 
  ** Using this method it is possible to partition the keys (partitionedKey = true). In this case
  ** instead of a single tree, several trees are used to store the keys. When inserting a new object
  ** the tree in which the object has to be stored is determined using a hash function. This distribution
  ** on several trees should prevent bottlenecks caused by concurrent operations on the tree. 
  ** Currently the keys are always distributed among 16 trees. 
  ** 
  ** \attention
  ** Iterating on objects which are stored with partitioned keys might lead to performance degradation,
  ** as the different trees must be merged during the iteration. Partitioned keys are even worse if
  ** the running direction of the iterator is changed during a single run.
  **
  ** \param partitionedKey Should several B*-trees be used to store the keys?
  */
  static void  omsRegClass(
    OmsHandle&ah, 
    const char* className, 
    size_t sz, 
    OmsObject LIVECACHE_OBJECT_TMPLPAR(Obj, guid)* pobj, 
    OmsSchemaHandle sh, 
    OmsContainerNo cno,
    bool partitionedKey)
  { 
    OmsAbstractObject::omsRegClassAndKey( ah, guid, 
      className, sz, sizeof(Key), sizeof(Obj), pobj, sh, cno, partitionedKey ); 
  }


#ifdef	SAPDB_LVCSIM_EXTRAS
  // extras for liveCache simulator
#ifdef	SAPDB_LVCSIM_DEPRECATED
  static inline void  omsRegClass(OmsHandle &ah, size_t sz, const char*cls_name, OmsAbstractObject*pobj, OmsSchemaHandle sh, OmsContainerNo cno)
  { OmsAbstractObject::omsRegClassAndKey(ah, guid, sz, sizeof(Obj), sizeof(Key), cls_name, 0, pobj, sh, cno); }
  static inline void  omsRegClass(OmsHandle &ah, const char*cls_name, size_t sz, const ClassIDRef base_class, OmsAbstractObject*pobj, OmsSchemaHandle sh, OmsContainerNo cno)
#ifdef LIVECACHE_INTGUIDS
  { OmsAbstractObject::omsRegClassAndKey(ah, guid, sz, sizeof(Obj), sizeof(Key), cls_name, base_class, pobj, sh, cno); }
#else
  { OmsAbstractObject::omsRegClassAndKey(ah, guid, sz, sizeof(Obj), sizeof(Key), cls_name, &base_class, pobj, sh, cno); }
#endif
#endif
#endif

  /*------------------------------------------------------------------------------------*/
  /// Dereferences the instance with the specified key.
  /*!
  ** Copies an instance of a class derived from OmsKeyedObject (identified by the key) from 
  ** the container identified by schema and cno to the current context cache, and provides a 
  ** pointer back to this. If there is no instance identified by key in the consistent view in the 
  ** container, the NULL pointer is provided. The pointer provides only read access to the 
  ** instance; changes cannot be carried out. To allow changes to be carried out, the pointer 
  ** must be converted to an update pointer by invoking omsForUpdPtr.
  */
  static const Obj* omsKeyAccess( OmsHandle&h, const Key&key, OmsSchemaHandle shdl, OmsContainerNo cno )  
  { return (Obj*)OmsAbstractObject::omsDeRefKey( (unsigned char*)(&key), h, guid, shdl, cno ); }
  
  /*------------------------------------------------------------------------------------*/
  /// Dereferences the instance with the specified key for update.
  /*!
  ** Copies an instance of a class derived from OmsKeyedObject (identified by the key) from 
  ** the container identified by schema and cno to the current context cache, and provides a 
  ** pointer back to this. If there is no instance identified by key in the consistent view in the 
  ** container, the NULL pointer is provided. The pointer can be used to apply changes.
  */
  static Obj* omsKeyAccessForUpd( OmsHandle&h, const Key&key, bool doLock, OmsSchemaHandle sh, OmsContainerNo cno )  
  { return (Obj*)OmsAbstractObject::omsDeRefKeyForUpd((unsigned char*)(&key), h, guid, doLock, sh, cno ); }
  
  /*------------------------------------------------------------------------------------*/
  /// Creates a key-iterator and positions the iterator onto the smallest element 
  /*!
  ** Creates a keyiterator, which returns objects lying in the interval [startKey, stopKey]
  ** and which belongs to the given schema handle and container number.
  ** After the creation the iterator is positioned onto the smallest object within the
  ** interval.
  ** The boundaries of the interval are inclusive. 
  **
  ** \param h handle
  ** \param sh schema handle 
  ** \param ContainerNo container number
  ** \param lowerKey lower boundary of the interval
  ** \param upperKey upper boundary of the interval
  ** \param maxBufferSize Size of the local buffer which is used in the oms-layer to buffer
  **        oids. This buffer reduced the communication overhead between the oms-layer and
  **        the kernel and therefore it should be increased/decreased dependend on the cost
  **        of the communication between these two layers. The buffer is only used for objects 
  **        created in the kernel; so it is not relevant for objects created in a version. 
  */
  static OmsKeyRangeIter<Obj> omsFirstKey(
    OmsHandle&      h, 
    OmsSchemaHandle sh, 
    OmsContainerNo  ContainerNo,
    const Key&      lowerKey,
    const Key&      upperKey,
    int             maxBufferSize = 20)
  { 
    // Ensure alignment PTS 1124030
    //unsigned char binStartKey[sizeof(Key)];
    //unsigned char binStopKey [sizeof(Key)];
    long binStartKeyAlign[sizeof(Key)/sizeof(long) + 1];
    long binStopKeyAlign [sizeof(Key)/sizeof(long) + 1];
    unsigned char* binStartKey = reinterpret_cast<unsigned char*>(&binStartKeyAlign[0]);
    unsigned char* binStopKey  = reinterpret_cast<unsigned char*>(&binStopKeyAlign[0]);

    lowerKey.omsKeyToBinary(&binStartKey[0]);
    upperKey.omsKeyToBinary(&binStopKey[0]);
    OmsObjByKeyIterBase iter = h.omsCreateKeyRangeIter( guid, sh, ContainerNo, 
       &binStartKey[0], &binStartKey[0], &binStopKey[0], OMS_ASCENDING, maxBufferSize);
    return *REINTERPRET_CAST (OmsKeyRangeIter<Obj>*, &iter);
  }

  /*------------------------------------------------------------------------------------*/
  /// Creates a key-iterator and positions the iterator onto the largest element 
  /*!
  ** Creates a keyiterator, which returns objects lying in the interval [startKey, stopKey]
  ** and which belongs to the given schema handle and container number.
  ** After the creation the iterator is positioned onto the smallest object within the
  ** interval.
  ** The boundaries of the interval are inclusive. 
  **
  ** \param h handle
  ** \param sh schema handle 
  ** \param ContainerNo container number
  ** \param lowerKey lower boundary of the interval
  ** \param upperKey upper boundary of the interval
  ** \param maxBufferSize Size of the local buffer which is used in the oms-layer to buffer
  **        oids. This buffer reduced the communication overhead between the oms-layer and
  **        the kernel and therefore it should be increased/decreased dependend on the cost
  **        of the communication between these two layers. The buffer is only used for objects 
  **        created in the kernel; so it is not relevant for objects created in a version. 
  **
  ** \since PTS 1119480
  */
  static OmsKeyRangeIter<Obj> omsLastKey( 
    OmsHandle&      h, 
    OmsSchemaHandle sh, 
    OmsContainerNo  ContainerNo,
    const Key&      lowerKey,
    const Key&      upperKey,
    int             maxBufferSize = 20)
  { 
    // Ensure alignment PTS 1124030
    //unsigned char binStartKey[sizeof(Key)];
    //unsigned char binStopKey [sizeof(Key)];
    long binStartKeyAlign[sizeof(Key)/sizeof(long) + 1];
    long binStopKeyAlign [sizeof(Key)/sizeof(long) + 1];
    unsigned char* binStartKey = reinterpret_cast<unsigned char*>(&binStartKeyAlign[0]);
    unsigned char* binStopKey  = reinterpret_cast<unsigned char*>(&binStopKeyAlign[0]);

    lowerKey.omsKeyToBinary(&binStartKey[0]);
    upperKey.omsKeyToBinary(&binStopKey[0]);
    OmsObjByKeyIterBase iter = h.omsCreateKeyRangeIter( guid, sh, ContainerNo,
        &binStopKey[0], &binStartKey[0], &binStopKey[0], OMS_DESCENDING, maxBufferSize);
    return *REINTERPRET_CAST (OmsKeyRangeIter<Obj>*, &iter);
  }

protected:
  virtual void  omsKeyToBinary(const void* pKey, void* pDest) const 
  {
    REINTERPRET_CAST(const Key*, pKey)->omsKeyToBinary(pDest);
  }
  virtual void omsBinaryToKey(void* pSource, void* pKey) const 
  {
    REINTERPRET_CAST(Key*, pKey)->omsBinaryToKey(pSource);
  }
};

template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
inline 
const OmsOid<T2> OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)::omsGetOid() const 
{ 
  return *(OmsOid<T2>*)(&omsOid());
}

template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
inline
bool OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)::omsHistoryInUse(OmsHandle& h) const
{
  return OmsAbstractObject::omsHistoryInUse(h);
}

template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
inline 
T2* OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)::omsForUpdPtr(OmsHandle& h, bool doLock) const
{
  return (T2*) OmsAbstractObject::omsForUpdPtr(h, doLock); 
}

template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
inline 
void* OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)::operator new ( size_t sz, OmsHandle&ah, OmsSchemaHandle sh, OmsContainerNo cno )
{
  return (void*) OmsAbstractObject::omsNewObject( ah, guid, sh, cno );
}

template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
inline 
void* OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)::operator new ( size_t sz, OmsHandle& ah, OmsRegEnum e ) 
{
  return (void*) OmsAbstractObject::omsNewRegistryObject( sz, ah, guid ); 
}

template LIVECACHE_OBJECT_TMPLPAR(class T2, ClassID guid)
inline 
void* OmsObject LIVECACHE_OBJECT_TMPLPAR(T2, guid)::operator new ( size_t sz, OmsHandle& ah, OmsRegEnum e, size_t rsz ) 
{
  return (void*) OmsAbstractObject::omsNewRegistryObject( rsz, ah, guid ); 
}

template LIVECACHE_KEYEDOBJECT_TMPLPAR(class Obj, class Key, ClassID guid)
inline
void OmsKeyedObject LIVECACHE_KEYEDOBJECT_TMPLPAR(Obj, Key, guid)::omsGetKey( OmsHandle&ah, Key& k ) const
{ 
  omsKey ( ah, REINTERPRET_CAST(unsigned char*, &k) );
}

#endif  // __OMS_OBJECT_HPP
