/*!
 * \file    OMS_AbstractObject.cpp
 * \author  MarkusSi
 * \brief   Defines Functions used by SQL-Class and OMS
 */
/*

    ========== 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



*/

#include "Oms/OMS_AbstractObject.hpp"
#include "Oms/OMS_Handle.hpp"
#include "Oms/OMS_Trace.hpp"
#include "Oms/OMS_Session.hpp"
#include "Oms/OMS_ObjectContainer.hpp"

#define FOR_UPD_CO10       true

static inline OmsObjectContainerPtr omsGetContainer(const OmsAbstractObject* pObj)
{
  if (NULL == pObj) 
    throw DbpError (DbpError::RTE_ERROR, e_nil_pointer, __MY_FILE__, __LINE__);
  return (OmsObjectContainerPtr) ((unsigned char*) pObj - OmsObjectContainer::headerSize());
}

//
// Implementation of OmsAbstractObject
//

OmsAbstractObject::OmsAbstractObject() {}

/*----------------------------------------------------------------------*/

OmsObjByClsIterBase OmsAbstractObject::omsAllOids(OmsHandle       &h, 
                                                  const ClassIDRef guid, 
                                                  OmsSchemaHandle  Schema, 
                                                  OmsContainerNo   ContainerNo,
                                                  int              maxBufferSize)
{
  OMS_TRACE(omsTrInterface, h.m_pSession->m_lcSink, "OmsAbstractObject::omsAllOids : " << guid << "," << Schema << "," << ContainerNo); 
  return h.omsAllOids (guid, Schema, ContainerNo, maxBufferSize);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsBinaryToKey(void* pSource, void* pKey) const
{
  throw DbpError(-9000, "invalid call of OmsAbstractObject::omsBinaryToKey");
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsCleanUp (OmsHandle&) {}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsDelete (OmsHandle& Handle)
{
  OMS_CHECK_EXCEPTION(Handle);

  const char* msg = "OmsAbstractObject::omsDelete";

  Handle.m_pSession->AssertNotReadOnly(msg);
  Handle.m_pSession->IncDelete ();
  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, msg << " : " << pObjContainer->m_oid);

  // Check that container is not dropped already  PTS 1127338
  OMS_ClassIdEntry *pInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  if (pObjContainer->DeletedFlag()) {
    Handle.m_pSession->ThrowDBError (e_object_not_found, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }
  if (!pObjContainer->LockedFlag() && !Handle.m_pSession->IsLocked(pObjContainer->m_oid))
  {
    Handle.m_pSession->ThrowDBError (e_object_not_locked, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }
  omsCleanUp (Handle);
  pObjContainer->MarkDeleted();
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsDeleteAll(OmsHandle& h, const ClassIDRef guid, OmsSchemaHandle Schema, OmsContainerNo ContainerNo) 
{
  const char* msg = "OmsAbstractObject::omsDeleteAll";

  OMS_TRACE(omsTrInterface, h.m_pSession->m_lcSink, msg << " : "
    << guid << "," << Schema << "," << ContainerNo);
  h.omsDeleteAll (guid, Schema, ContainerNo);
}

/*----------------------------------------------------------------------*/

const OmsAbstractObject* OmsAbstractObject::omsDeRef (const OmsObjectId& oid, OmsHandle& Handle, const ClassIDRef guid)
{
  const char *msg = "OmsAbstractObject::omsDeRef";

  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsDeRef : " << oid << ", " << guid); 
  OMS_CHECK_EXCEPTION(Handle);

#if defined(TEST_INLINE)
  OmsObjectContainerPtr p = Handle.m_pSession->m_context->FindObjInContext(&oid);

  if (NULL != p) {
    if (p->DeletedFlag()) {
      p = NULL;
    }
  }
  else {
    if (oid) {
      p = Handle.m_pSession->m_context->GetObjFromLiveCacheBase(oid, OMS_Context::NoLock);
    }
    else {
      return NULL;
    }
  }
  if (NULL != p) {
    Handle.m_pSession->m_context->CheckCompatibility(guid, p);
    return &p->m_pobj;
  }
  else {
    Handle.m_pSession->ThrowDBError (e_object_not_found, "OmsAbstractObject::omsDeRef", oid, __MY_FILE__, __LINE__);
  }
  return NULL;
#else
  if (!oid) {
    return NULL;
  }
  else {
    return REINTERPRET_CAST(OmsAbstractObject*, Handle.m_pSession->DeRef (oid, guid, !FOR_UPD_CO10, false));
  }
#endif
}

/*----------------------------------------------------------------------*/

OmsAbstractObject* OmsAbstractObject::omsDeRefForUpd (const OmsObjectId& oid, OmsHandle& Handle, const ClassIDRef guid, bool do_lock)
{
  const char *msg = "OmsAbstractObject::omsDeRefForUpd";

  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsDeRefForUpd : " << oid 
    << ", " << guid << ", do_lock : " << do_lock); 
  OMS_CHECK_EXCEPTION(Handle);


  if (oid) {
    return REINTERPRET_CAST(OmsAbstractObject*, Handle.m_pSession->DeRef(oid, guid, FOR_UPD_CO10, do_lock));
  }
  else {
    return NULL;
  }
}

/*----------------------------------------------------------------------*/

const OmsAbstractObject* OmsAbstractObject::omsDeRefKey (unsigned char*  key,
                                                         OmsHandle&      Handle, 
                                                         const ClassIDRef guid, 
                                                         OmsSchemaHandle h,
                                                         OmsContainerNo  ContainerNo) 
{
  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsDeRefKey : " << guid
    << "CNo : " << ContainerNo << " Key : " << OMS_UnsignedCharBuffer(key, Handle.m_pSession->GetClassInfo(guid)->GetKeyLen()));
  OMS_CHECK_EXCEPTION(Handle);
  return REINTERPRET_CAST(OmsAbstractObject*, Handle.m_pSession->DeRefViaKey(key, guid, !FOR_UPD_CO10, false, h, ContainerNo));
}

/*----------------------------------------------------------------------*/

OmsAbstractObject* OmsAbstractObject::omsDeRefKeyForUpd (unsigned char*  key,
                                                         OmsHandle&      Handle, 
                                                         const ClassIDRef guid, 
                                                         bool            do_lock,
                                                         OmsSchemaHandle h,
                                                         OmsContainerNo  ContainerNo)
{
  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsDeRefKeyForUpd : " << guid
    << "CNo : " << ContainerNo << " Key : " << OMS_UnsignedCharBuffer(key, Handle.m_pSession->GetClassInfo(guid)->GetKeyLen())
    << " do_lock : " << do_lock);
  OMS_CHECK_EXCEPTION(Handle);

  return REINTERPRET_CAST(OmsAbstractObject*, Handle.m_pSession->DeRefViaKey(key, guid, FOR_UPD_CO10, do_lock, h, ContainerNo));
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsDump (OmsHandle& Handle, const char* flags) const
{
  OmsObjectId oid = omsOid();
  Handle.omsTracef ("OID : %d.%d (vers %d) %s", 
    oid.getPno(), oid.getPagePos(), oid.getGeneration(), flags); 
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsFlush (OmsHandle& Handle)
{
  const char* msg = "OmsAbstractObject::omsFlush";

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  // Check that container is not dropped already  PTS 1127338
  OMS_ClassIdEntry *pInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  Handle.m_pSession->m_context->FlushObj(pObjContainer);
}

/*----------------------------------------------------------------------*/

const OmsObjectId& OmsAbstractObject::omsGetOid() const
{
  return omsOid();
}

/*----------------------------------------------------------------------*/

OmsAbstractObject* OmsAbstractObject::omsForUpdPtr(OmsHandle& h, bool doLock) const
{
  const char* msg = "OmsAbstractObject::omsForUpdPtr";

  OMS_CHECK_EXCEPTION(h);

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  h.m_pSession->AssertNotReadOnly(msg); 

  // Check that container is not dropped already // PTS 1127338
  OMS_ClassIdEntry *pInfo = pObjContainer->GetContainerInfoNoCheck(h.m_pSession->CurrentContext());
  if (pInfo->GetContainerInfoPtr()->IsDropped()){
    h.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  if (doLock) {
    if (!(pObjContainer->LockedFlag()) && (!h.m_pSession->InVersion())) {
      ((OmsAbstractObject*) this)->omsLock(h);
    }
  }
  h.m_pSession->InsertBeforeImage (pObjContainer);
  return (OmsAbstractObject*) this;
}

/*----------------------------------------------------------------------*/

OmsSchemaHandle OmsAbstractObject::omsGetSchemaHandle(OmsHandle& Handle) const
{
  const char* msg = "OmsAbstractObject::omsGetSchemaHandle";

  OMS_CHECK_EXCEPTION(Handle);

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  // Check that container is not dropped already  PTS 1127338
  //OMS_ClassIdEntry* pContainerInfo = pObjContainer->GetContainerInfo(Handle.m_pSession->CurrentContext());
  OMS_ClassIdEntry *pContainerInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pContainerInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }  
  
  return pContainerInfo->GetSchema();
}

/*----------------------------------------------------------------------*/

bool OmsAbstractObject::omsHistoryInUse(OmsHandle& h) const
{
  const char* msg = "OmsAbstractObject::omsHistoryInUse";

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  OMS_TRACE(omsTrInterface, h.m_pSession->m_lcSink, "OmsAbstractObject::omsHistoryInUse : " << pObjContainer->m_oid);
  OMS_CHECK_EXCEPTION(h);

  // Check that container is not dropped already  PTS 1127338
  OMS_ClassIdEntry *pInfo = pObjContainer->GetContainerInfoNoCheck(h.m_pSession->CurrentContext());
  if (pInfo->GetContainerInfoPtr()->IsDropped()){
    h.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  return h.m_pSession->HistoryInUse(pObjContainer->m_oid);
}

/*----------------------------------------------------------------------*/

bool OmsAbstractObject::omsIsLocked (OmsHandle& Handle) const
{
  const char* msg = "OmsAbstractObject::omsIsLocked";

  _TRACE_METHOD_ENTRY(&Handle,"OmsAbstractObject::omsIsLocked");

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  // Check that container is not dropped already  PTS 1127338
  OMS_ClassIdEntry *pInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  return Handle.m_pSession->IsLocked(pObjContainer->m_oid);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsKey(OmsHandle& Handle, unsigned char* key) const
{
  const char *msg = "OmsAbstractObject::omsKey";

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  // Check that container is not dropped already // PTS 1127338
  //OMS_ClassIdEntry* pContainerInfo = pObjContainer->GetContainerInfo (Handle.m_pSession->CurrentContext());
  OMS_ClassIdEntry *pContainerInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pContainerInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  this->omsBinaryToKey(pContainerInfo->GetKeyPtr(pObjContainer), key);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsKeyToBinary(const void* pKey, void* pDest) const
{
  throw DbpError (-9000, "invalid call of OmsAbstractObject::omsKeyToBinary");
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsLock (OmsHandle& h)
{
  OMS_CHECK_EXCEPTION(h);

  const char* msg = "OmsAbstractObject::omsLock"; 

  h.m_pSession->AssertNotReadOnly(msg);  

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  OMS_TRACE(omsTrInterface, h.m_pSession->m_lcSink, msg << " : " << pObjContainer->m_oid);

  // Check that container is not dropped already // PTS 1127338
  OMS_ClassIdEntry *pContainerInfo = pObjContainer->GetContainerInfoNoCheck(h.m_pSession->CurrentContext());
  if (pContainerInfo->GetContainerInfoPtr()->IsDropped()){
    h.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  if ((pObjContainer->LockedFlag()) || (h.m_pSession->InVersion())) {
    // instance already locked
    return;
  }
  h.m_pSession->LockObj(pObjContainer);
}

/*----------------------------------------------------------------------*/

bool OmsAbstractObject::omsTryLock (OmsHandle& h)
{
  OMS_CHECK_EXCEPTION(h);

  const char* msg = "OmsAbstractObject::omsTryLock"; 

  h.m_pSession->AssertNotReadOnly(msg);  

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  OMS_TRACE(omsTrInterface, h.m_pSession->m_lcSink, msg << " : " << pObjContainer->m_oid);

  // Check that container is not dropped already // PTS 1127338
  OMS_ClassIdEntry *pContainerInfo = pObjContainer->GetContainerInfoNoCheck(h.m_pSession->CurrentContext());
  if (pContainerInfo->GetContainerInfoPtr()->IsDropped()){
    h.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  if ((pObjContainer->LockedFlag()) || (h.m_pSession->InVersion())) {
    // instance already locked
    return true;
  }
  return h.m_pSession->TryLockObj(pObjContainer);
}

/*----------------------------------------------------------------------*/

OmsAbstractObject* OmsAbstractObject::omsNewObject (OmsHandle&      Handle, 
                                                    const ClassIDRef guid, 
                                                    OmsSchemaHandle h, 
                                                    OmsContainerNo  ContainerNo )
{
  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsNewObject : " << guid 
    << "CNo : " << ContainerNo);

  return Handle.omsNewObject ( guid, h, ContainerNo ); 
}

/*----------------------------------------------------------------------*/

OmsAbstractObject* OmsAbstractObject::omsNewRegistryObject(size_t sz, OmsHandle& Handle, const ClassIDRef guid)
{
  return Handle.omsNewRegistryObject (sz, guid);
}

/*----------------------------------------------------------------------*/

OmsAbstractObject* OmsAbstractObject::omsNewKeyedObject (OmsHandle&           Handle, 
                                                         const ClassIDRef     guid, 
                                                         const unsigned char* key,
                                                         OmsSchemaHandle      h,
                                                         OmsContainerNo       ContainerNo )
{
  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsNewKeyedObject : " << guid
    << " CNo : " << ContainerNo << " Key : " << OMS_UnsignedCharBuffer(key, Handle.m_pSession->GetClassInfo(guid)->GetKeyLen()));

  return Handle.omsNewKeyedObject ( guid, key, h, ContainerNo ); 
}

/*----------------------------------------------------------------------*/

const OmsObjectId& OmsAbstractObject::omsOid() const
{
  return omsGetContainer(this)->m_oid;
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsRegClass (OmsHandle&         Handle,
                                     const ClassIDRef   Guid, 
                                     const char*        ClassName, 
                                     size_t             PersistentSize, 
                                     size_t             ObjectSize,
                                     const ClassIDPtr   pBaseClass,
                                     OmsAbstractObject* Obj,
                                     OmsSchemaHandle    h,
                                     OmsContainerNo     ContainerNo,
                                     size_t             arrayByteSize) 
{
  Handle.omsRegClass (h, Guid, ClassName, PersistentSize, ObjectSize, pBaseClass,
    Obj, ContainerNo, arrayByteSize);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsRegClassAndKey (OmsHandle&         Handle,
                                           const ClassIDRef   Guid,
                                           const char*        ClassName, 
                                           size_t             PersistentSize, 
                                           size_t             KeySize, 
                                           size_t             ObjectSize,
                                           OmsAbstractObject* Obj,
                                           OmsSchemaHandle    h,
                                           OmsContainerNo     ContainerNo,
                                           bool               partitionedKey)
{
  Handle.omsRegClassAndKey (h, Guid, ClassName, PersistentSize, KeySize, 
    ObjectSize, Obj, ContainerNo, partitionedKey);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsRelease(OmsHandle& Handle)
{
  const char* msg = "OmsAbstractObject::omsRelease";

  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsRelease : " << omsGetContainer(this)->m_oid);
  
  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  // Check that container is not dropped already  PTS 1127338
  OMS_ClassIdEntry *pInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  Handle.m_pSession->ReleaseObj(pObjContainer);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsStore (OmsHandle& Handle)
{
  OMS_TRACE(omsTrInterface, Handle.m_pSession->m_lcSink, "OmsAbstractObject::omsStore : " << omsGetContainer(this)->m_oid);
  OMS_CHECK_EXCEPTION(Handle);

  const char* msg = "OmsAbstractObject::omsStore"; 

  Handle.m_pSession->AssertNotReadOnly(msg);  

  Handle.m_pSession->IncStore();
  // omsVerify(Handle); PTS 1121779

  OmsObjectContainerPtr pObjContainer = omsGetContainer(this);

  // Check that container is not dropped already // PTS 1127338
  OMS_ClassIdEntry *pContainerInfo = pObjContainer->GetContainerInfoNoCheck(Handle.m_pSession->CurrentContext());
  if (pContainerInfo->GetContainerInfoPtr()->IsDropped()){
    Handle.m_pSession->ThrowDBError(e_container_dropped, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }

  _TRACE_OID(&Handle, pObjContainer->m_oid);
  if (!pObjContainer->LockedFlag() && !Handle.m_pSession->IsLocked(pObjContainer->m_oid))
  {
    Handle.m_pSession->ThrowDBError (e_object_not_locked, msg, pObjContainer->m_oid, __MY_FILE__, __LINE__);
  }
  if (!(pObjContainer->existBeforeImage (Handle.m_pSession->CurrentSubtransLevel(), Handle.m_pSession->InVersion()))) {
    Handle.m_pSession->ThrowDBError (e_missing_before_image, "missing before image", __MY_FILE__, __LINE__);
  }
  else {
    pObjContainer->MarkStored();
  }
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsVerify (OmsHandle& h) const {}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsSimDumpToHtmlShort(OmsHandle &Handle, OmsSimDumpCollector &str) const
{
  Handle.m_pSession->m_lcSink->SimCtlDumpToHtml(Handle, omsOid(), str);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsSimDumpToHtmlLong(OmsHandle &Handle, OmsSimDumpCollector &str) const
{
  omsSimDumpToHtmlShort(Handle, str);
}

/*----------------------------------------------------------------------*/

void OmsAbstractObject::omsSimSetObjectName(OmsHandle &Handle, const char *name)
{
  Handle.m_pSession->m_lcSink->SimCtlSetObjectName(Handle, omsOid(), name);
}

/*----------------------------------------------------------------------*/

const char* OmsAbstractObject::omsSimGetObjectName(OmsHandle &Handle) const
{
  const char *name;
  Handle.m_pSession->m_lcSink->SimCtlGetObjectName(Handle, omsOid(), &name);
  return name;
}

/*----------------------------------------------------------------------*/

const char *OmsAbstractObject::omsSimDumpMakeOidRef(OmsHandle &h, const OmsObjectId &oid, int flags)
{
  const char *ref;
  h.m_pSession->m_lcSink->SimCtlGetHTMLRef(h, oid, &ref, flags);
  return ref;
}

/*----------------------------------------------------------------------*/

const char *OmsAbstractObject::omsSimDumpMakeOidRef(const OmsObjectId &oid, int flags)
{
  return omsSimDumpMakeOidRef(*OmsHandle::omsGetOmsHandle(), oid, flags);
}
