#include <Storage_Schema.ixx>
#include <Storage.hxx>
#include <Storage_BucketOfPersistent.hxx>
#include <Storage_InternalData.hxx>
#include <Storage_TypedCallBack.hxx>
#include <TColStd_HSequenceOfAsciiString.hxx>
#include <Storage_MapOfAsciiString.hxx>

#include <Storage_HPArray.hxx>
#include <Storage_HSeqOfRoot.hxx>
#include <Storage_Root.hxx>
#include <Storage_DataMapIteratorOfMapOfCallBack.hxx>
#include <Storage_DefaultCallBack.hxx>
#include <Storage_HArrayOfCallBack.hxx>

#include <Standard_ErrorHandler.hxx>
#include <Storage_StreamModeError.hxx>
#include <Storage_StreamFormatError.hxx>
#include <Storage_StreamWriteError.hxx>
#include <Storage_StreamReadError.hxx>
#include <Storage_StreamUnknownTypeError.hxx>
#include <Storage_StreamTypeMismatchError.hxx>
#include <Storage_StreamExtCharParityError.hxx>

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if defined(HAVE_TIME_H) || defined(WNT)
# include <time.h>
#endif

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

#include <locale.h>
#include <stdio.h>

extern Standard_Address StandardCSFDB_Reallocate
                         (Standard_Address&,
                          const Standard_Size,
                          const Standard_Size);

// IMPLEMENTATION BucketOfPersistent
//
Storage_Bucket::~Storage_Bucket()
{
  StandardCSFDB_Free((Standard_Address&)mySpace);
  mySpace = 0L;
  mySpaceSize = 0;
  Clear();
}

//=======================================================================
//function : Clear
//purpose  : 
//=======================================================================

void Storage_Bucket::Clear()
{
  myCurrentSpace = -1;
}

//=======================================================================
//function : Append
//purpose  : 
//=======================================================================

void Storage_Bucket::Append(Standard_Persistent *sp)
{
  myCurrentSpace++;
  mySpace[myCurrentSpace] = sp;
}

//=======================================================================
//function : Value
//purpose  : 
//=======================================================================

Standard_Persistent* Storage_Bucket::Value
                         (const Standard_Integer theIndex) const
{
  return mySpace[theIndex];
}

//=======================================================================
//function : Storage_BucketOfPersistent
//purpose  : 
//=======================================================================

Storage_BucketOfPersistent::Storage_BucketOfPersistent
                         (const Standard_Integer theBucketSize,
                          const Standard_Integer theBucketNumber)
: myNumberOfBucket(1),myNumberOfBucketAllocated(theBucketNumber),myBucketSize
                         (theBucketSize)
{
  myBuckets =  (Storage_Bucket**)StandardCSFDB_Allocate
                         (sizeof(Storage_Bucket*) * theBucketNumber);
  myBuckets[0] = new Storage_Bucket(myBucketSize);
  myCurrentBucket = myBuckets[0];
  myLength = 0;
  myCurrentBucketNumber = 0;
}

//=======================================================================
//function : Clear
//purpose  : 
//=======================================================================

void Storage_BucketOfPersistent::Clear()
{
  if (myBuckets) {
    Standard_Integer i;

    for (i = 1; i < myNumberOfBucket; i++) delete myBuckets[i];
    myNumberOfBucket = 1;
    myCurrentBucket = myBuckets[0];
    myCurrentBucket->Clear();
    myCurrentBucketNumber = 0;
    myLength = 0;
  }
}

Storage_BucketOfPersistent::~Storage_BucketOfPersistent()
{
  Clear();
  delete myBuckets[0];
  StandardCSFDB_Free((Standard_Address&)myBuckets);
  myBuckets = 0L;
}

//=======================================================================
//function : Value
//purpose  : 
//=======================================================================

Standard_Persistent* Storage_BucketOfPersistent::Value
                         (const Standard_Integer theIndex)
{
  Standard_Integer theInd,theCurrentBucketNumber,tecurrentind = theIndex - 1;
  theCurrentBucketNumber = tecurrentind / myBucketSize;
  theInd = tecurrentind - (myBucketSize * theCurrentBucketNumber);

  return myBuckets[theCurrentBucketNumber]->mySpace[theInd];

}

//=======================================================================
//function : Append
//purpose  : 
//=======================================================================

void Storage_BucketOfPersistent::Append(const Handle(Standard_Persistent)& sp)
{
  myCurrentBucket->myCurrentSpace++;

  if (myCurrentBucket->myCurrentSpace != myBucketSize) {
    myLength++;
    myCurrentBucket->mySpace[myCurrentBucket->myCurrentSpace] = sp.operator->();
    return;
  }

  myCurrentBucket->myCurrentSpace--;
  myNumberOfBucket++;
  myCurrentBucketNumber++;

  if (myNumberOfBucket > myNumberOfBucketAllocated) {
    Standard_Size e = sizeof(Storage_Bucket*) * myNumberOfBucketAllocated;
    myBuckets =  (Storage_Bucket**)StandardCSFDB_Reallocate((Standard_Address&)myBuckets,e,e * 2);
    myNumberOfBucketAllocated *= 2;
  }

  myBuckets[myCurrentBucketNumber] = new Storage_Bucket(myBucketSize);
  myCurrentBucket = myBuckets[myCurrentBucketNumber];
  myCurrentBucket->myCurrentSpace++;
  myLength++;
  myCurrentBucket->mySpace[myCurrentBucket->myCurrentSpace] = sp.operator->();
}

//=======================================================================
//function : Storage_BucketIterator
//purpose  : 
//=======================================================================

Storage_BucketIterator::Storage_BucketIterator
                         (Storage_BucketOfPersistent* aBucketManager)
{
  if (aBucketManager) {
    myBucket             = aBucketManager;
    myCurrentBucket      = myBucket->myBuckets[0];
    myBucketNumber       = aBucketManager->myNumberOfBucket;
    myCurrentBucketIndex = 0;
    myCurrentIndex       = 0;
    myMoreObject         = Standard_True;
  }
  else myMoreObject         = Standard_False;
}

//=======================================================================
//function : Reset
//purpose  : 
//=======================================================================

void Storage_BucketIterator::Reset()
{
  if (myBucket) {
    myCurrentBucket = myBucket->myBuckets[0];
    myBucketNumber  = myBucket->myNumberOfBucket;
    myCurrentIndex  = 0;
    myCurrentBucketIndex = 0;
    myMoreObject = Standard_True;
  }
  else myMoreObject         = Standard_False;
}

//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void Storage_BucketIterator::Init(Storage_BucketOfPersistent* aBucketManager)
{
  if (aBucketManager) {
    myBucket        = aBucketManager;
    myCurrentBucket = myBucket->myBuckets[0];
    myBucketNumber  = aBucketManager->myNumberOfBucket;
    myCurrentIndex  = 0;
    myCurrentBucketIndex = 0;
    myMoreObject = Standard_True;
  }
  else myMoreObject         = Standard_False;
}

//=======================================================================
//function : Next
//purpose  : 
//=======================================================================

void Storage_BucketIterator::Next()
{
  if (!myMoreObject) return;

  if (myCurrentIndex < myCurrentBucket->myCurrentSpace) {
    myCurrentIndex++;
  }
  else {
    myCurrentIndex = 0;
    myCurrentBucketIndex++;
    if (myCurrentBucketIndex < myBucketNumber) {
      myCurrentBucket = myBucket->myBuckets[myCurrentBucketIndex];
    }
    else {
      myMoreObject = Standard_False;
    }
  }
}

//=======================================================================
//function : Storage_Schema
//purpose  : USER API -- --------------------------------------------------------------
//           IMPLEMENTATION BucketOfPersistent
//=======================================================================

Storage_Schema::Storage_Schema()
{
  Clear();
  ResetDefaultCallBack();
  myCallBackState = Standard_False;
  myNestedState = Standard_False;
}

//=======================================================================
//function : SetVersion
//purpose  : returns version of the schema
//=======================================================================

void Storage_Schema::SetVersion(const TCollection_AsciiString& aVersion)
{
  myVersion = aVersion;
}

//=======================================================================
//function : Version
//purpose  : returns the version of the schema
//=======================================================================

TCollection_AsciiString Storage_Schema::Version() const
{
  return myVersion;
}

//=======================================================================
//function : SetName
//purpose  : set the schema's name
//=======================================================================

void Storage_Schema::SetName(const TCollection_AsciiString& aSchemaName)
{
  myName = aSchemaName;
}

//=======================================================================
//function : Name
//purpose  : returns the schema's name
//=======================================================================

TCollection_AsciiString Storage_Schema::Name() const
{
  return myName;
}

//=======================================================================
//function : Write
//purpose  : write 
//Arguments:
//           s: driver to write
//           raises  if  the  stream  is  not  opened  in  VSWrite  or
//           VSReadWrite
//=======================================================================

void Storage_Schema::Write
                         (Storage_BaseDriver& f,
                          const Handle(Storage_Data)& aData) const
{
 if (aData.IsNull()) return;

  // add all the persistent to write...
  //
  Standard_Integer                 posfrom,posto;
  Handle(Standard_Persistent)      p;
  Handle(Storage_HSeqOfRoot)       plist;
  TCollection_AsciiString          errorContext("AddPersistent");
  Storage_Schema::ISetCurrentData(aData);

  Handle(Storage_InternalData) iData = aData->InternalData();

  aData->Clear();
  aData->ClearErrorStatus();

  plist = aData->Roots();

  for (posto = 1; posto <= plist->Length(); posto++) {
    PersistentToAdd(plist->Value(posto)->Object());
  }

  for (posto = 1; posto <= plist->Length(); posto++) {
    AddTypeSelection(plist->Value(posto)->Object());
  }

  for (posfrom = plist->Length() + 1; posfrom <= iData->myPtoA.Length(); posfrom++) {
    AddTypeSelection(iData->myPtoA.Value(posfrom));
  }

  // ...and now we write
  //
  Standard_Integer            i,
                              len;

 aData->HeaderData()->SetCreationDate(ICreationDate());
 aData->HeaderData()->SetStorageVersion(Storage::Version());
 aData->HeaderData()->SetNumberOfObjects(iData->myPtoA.Length());
 aData->HeaderData()->SetSchemaName(myName);
 aData->HeaderData()->SetSchemaVersion(myVersion);

  if ((f.OpenMode() == Storage_VSWrite) || (f.OpenMode() == Storage_VSReadWrite)) {
    try {
      OCC_CATCH_SIGNALS
      errorContext = "BeginWriteInfoSection";
      f.BeginWriteInfoSection();
      errorContext = "WriteInfo";
      f.WriteInfo(aData->NumberOfObjects(),
                  aData->StorageVersion(),
                  aData->CreationDate(),
                  aData->SchemaName(),
                  aData->SchemaVersion(),
                  aData->ApplicationName(),
                  aData->ApplicationVersion(),
                  aData->DataType(),
                  aData->UserInfo());
      errorContext = "EndWriteInfoSection";
      f.EndWriteInfoSection();

      errorContext = "BeginWriteCommentSection";
      f.BeginWriteCommentSection();
      errorContext = "WriteComment";
      f.WriteComment(aData->Comments());
      errorContext = "EndWriteCommentSection";
      f.EndWriteCommentSection();

      Handle(TColStd_HSequenceOfAsciiString) tlist;

      tlist = aData->Types();

      errorContext = "BeginWriteTypeSection";
      f.BeginWriteTypeSection();
      len = aData->NumberOfTypes();

      Handle(Storage_HArrayOfCallBack) WFunc = new Storage_HArrayOfCallBack(1,len);

      f.SetTypeSectionSize(len);

      Storage_DataMapIteratorOfMapOfCallBack cbit(iData->myTypeBinding);
      Handle(Storage_TypedCallBack) atcallBack;

      for (; cbit.More(); cbit.Next()) {
        atcallBack = cbit.Value();
        WFunc->SetValue(atcallBack->Index(),atcallBack->CallBack());
      }

      errorContext = "WriteTypeInformations";
      for (i = 1; i <= len; i++) {
        f.WriteTypeInformations(i,tlist->Value(i).ToCString());
      }

      errorContext = "EndWriteTypeSection";
      f.EndWriteTypeSection();

      errorContext = "BeginWriteRootSection";
      f.BeginWriteRootSection();
      f.SetRootSectionSize(plist->Length());

      errorContext = "WriteRoot";
      for (i = 1; i <= plist->Length(); i++) {
        f.WriteRoot(plist->Value(i)->Name(),i,plist->Value(i)->Type());
      }

      errorContext = "EndWriteRootSection";
      f.EndWriteRootSection();

      errorContext = "BeginWriteRefSection";
      f.BeginWriteRefSection();
      f.SetRefSectionSize(iData->myObjId - 1);
      errorContext = "WriteReferenceType";

      Storage_BucketIterator bit(&iData->myPtoA);

      while(bit.More()) {
        p = bit.Value();
        if (!p.IsNull()) f.WriteReferenceType(p->_refnum,p->_typenum);
        bit.Next();
      }

      errorContext = "EndWriteRefSection";
      f.EndWriteRefSection();

      errorContext = "BeginWriteDataSection";
      f.BeginWriteDataSection();

      Handle(Storage_Schema) me = this;

      errorContext = "Write";

      bit.Reset();

      while(bit.More()) {
        p = bit.Value();
        if (!p.IsNull()) {
          WFunc->Value(p->_typenum)->Write(p,f,me);
          p->_typenum = 0;
        }
        bit.Next();
      }

      errorContext = "EndWriteDataSection";
      f.EndWriteDataSection();
    }
    catch(Storage_StreamWriteError) {
      aData->SetErrorStatus(Storage_VSWriteError);
      aData->SetErrorStatusExtension(errorContext);
    }
  }
  else {
    aData->SetErrorStatus(Storage_VSModeError);
    aData->SetErrorStatusExtension("OpenMode");
  }

  iData->Clear();
  Clear();
}

//=======================================================================
//function : Read
//purpose  : ...and read a Storage file
//Arguments:
//           s: driver to read
//=======================================================================

Handle(Storage_Data) Storage_Schema::Read(Storage_BaseDriver& f) const
{
  Handle(Storage_Data)          dData = new Storage_Data;
  Storage_Error                errorCode;
static Standard_Boolean             result;
static Standard_Integer             len;
static Standard_Integer             i;
  i = 0 ;
  Handle(Standard_Persistent)  per;
  Handle(Storage_HArrayOfCallBack) theCallBack;

  Handle(Storage_InternalData) iData = dData->InternalData();
  Handle(Storage_TypeData)     tData = dData->TypeData();
  Handle(Storage_RootData)     rData = dData->RootData();
  Handle(Storage_HeaderData)   hData = dData->HeaderData();

  if ((f.OpenMode() == Storage_VSRead) || (f.OpenMode() == Storage_VSReadWrite)) {

    Storage_Schema::ISetCurrentData(dData);

    // IReadHeaderSection can set an error status
    //
    result = IReadHeaderSection(f,hData);

    if (result) {
      Handle(Storage_CallBack) accallBack;
      Standard_Integer            p;
      TCollection_AsciiString     typeName;

      iData->myReadArray = new Storage_HPArray(1,dData->NumberOfObjects());

      // IReadTypeSection can set an error status
      //
      result = IReadTypeSection(f,tData);

      if (result) {
        len = dData->NumberOfTypes();
        theCallBack = new Storage_HArrayOfCallBack(1,len);
        {
          try {
            OCC_CATCH_SIGNALS
            for (i = 1; i <= len; i++) {
              typeName = tData->Type(i);
              p = tData->Type(typeName);
              theCallBack->SetValue(p,CallBackSelection(typeName));
            }
          }
          catch(Storage_StreamUnknownTypeError) {
            result = Standard_False;
            dData->SetErrorStatus(Storage_VSUnknownType);
            dData->SetErrorStatusExtension(typeName);
          }
        }
      }
      else {
        dData->SetErrorStatus(tData->ErrorStatus());
        dData->SetErrorStatusExtension(tData->ErrorStatusExtension());
      }
    }
    else {
      dData->SetErrorStatus(hData->ErrorStatus());
      dData->SetErrorStatusExtension(hData->ErrorStatusExtension());
    }

    if (result) {
      result = IReadRootSection(f,rData);
      dData->SetErrorStatus(rData->ErrorStatus());
      if (!result) dData->SetErrorStatusExtension(rData->ErrorStatusExtension());
    }

    if (result) {
      Standard_Integer otype,oref;

      errorCode = f.BeginReadRefSection();

      if (errorCode == Storage_VSOk) {
        {
          try {
            OCC_CATCH_SIGNALS
            len = f.RefSectionSize();

            for (i = 1; i <= len; i++) {
              f.ReadReferenceType(oref,otype);
              iData->myReadArray->ChangeValue(oref) = theCallBack->Value(otype)->New();
              if (!iData->myReadArray->ChangeValue(oref).IsNull()) iData->myReadArray->ChangeValue(oref)->_typenum = otype;
            }
          }
          catch(Storage_StreamTypeMismatchError) {
            TCollection_AsciiString aOref = oref;
            result = Standard_False;
            dData->SetErrorStatus(Storage_VSTypeMismatch);
            dData->SetErrorStatusExtension(aOref);
          }
        }

        if (result) {
          errorCode = f.EndReadRefSection();
          result = (errorCode == Storage_VSOk);
          dData->SetErrorStatus(errorCode);
          if (!result) dData->SetErrorStatusExtension("EndReadRefSection");
        }
      }
      else {
        result = Standard_False;
        dData->SetErrorStatus(errorCode);
        dData->SetErrorStatusExtension("BeginReadRefSection");
      }
    }

    if (result) {
      errorCode = f.BeginReadDataSection();
      result = (errorCode == Storage_VSOk);
      dData->SetErrorStatus(errorCode);
      if (!result) dData->SetErrorStatusExtension("BeginReadDataSection");
    }

    if (result) {
      Handle(Storage_Schema) me = this;
      Handle(Storage_CallBack) rcback;

      {
        try {
          OCC_CATCH_SIGNALS
          for (i = 1; i <= dData->NumberOfObjects(); i++) {
            Handle(Standard_Persistent) pobj = iData->myReadArray->Value(i);
            if (!pobj.IsNull()) {
              rcback = theCallBack->Value(pobj->_typenum);
              rcback->Read(pobj,f,me);
              pobj->_typenum = 0;
            }
          }
        }
        catch(Storage_StreamTypeMismatchError) {
          result = Standard_False;
          dData->SetErrorStatus(Storage_VSTypeMismatch);
          dData->SetErrorStatusExtension(i-1);
        }
        catch(Storage_StreamFormatError) {
          result = Standard_False;
          dData->SetErrorStatus(Storage_VSFormatError);
          dData->SetErrorStatusExtension(i-1);
        }
        catch(Storage_StreamReadError) {
          result = Standard_False;
          dData->SetErrorStatus(Storage_VSFormatError);
          dData->SetErrorStatusExtension(i-1);
        }
      }

      if (result) {
        Handle(Storage_HSeqOfRoot) rlist = rData->Roots();
        Handle(Storage_Root)       rroot;

        for(i = 1; i <= dData->NumberOfRoots(); i++) {
          rroot = rlist->Value(i);
          rData->UpdateRoot(rroot->Name(),iData->myReadArray->Value(rroot->Reference()));
        }

        errorCode = f.EndReadDataSection();
        result = (errorCode == Storage_VSOk);
        dData->SetErrorStatus(errorCode);
        if (!result) dData->SetErrorStatusExtension("EndReadDataSection");
      }
    }
  }
  else {
    dData->SetErrorStatus(Storage_VSModeError);
    dData->SetErrorStatusExtension("OpenMode");
  }

  iData->Clear();
  Clear();

  return dData;
}

//=======================================================================
//function : ReadHeaderSection
//purpose  : read the header part of the stream
//Arguments:
//           s: driver to read
//=======================================================================

Handle(Storage_HeaderData) Storage_Schema::ReadHeaderSection
                         (Storage_BaseDriver& s) const
{
  Handle(Storage_HeaderData) result = new Storage_HeaderData;

  if ((s.OpenMode() == Storage_VSRead) || (s.OpenMode() == Storage_VSReadWrite)) {
    IReadHeaderSection(s,result);
  }
  else {
    result->SetErrorStatus(Storage_VSModeError);
    result->SetErrorStatusExtension("OpenMode");
  }

  return result;
}

//=======================================================================
//function : ReadTypeSection
//purpose  : fill the TypeData with the  names of the type used
//           in a stream
//Arguments:
//           s: driver to read
//=======================================================================

Handle(Storage_TypeData) Storage_Schema::ReadTypeSection
                         (Storage_BaseDriver& f) const
{
  Handle(Storage_TypeData) result = new Storage_TypeData;

  if ((f.OpenMode() == Storage_VSRead) || (f.OpenMode() == Storage_VSReadWrite)) {
    IReadTypeSection(f,result);
  }
  else {
    result->SetErrorStatus(Storage_VSModeError);
    result->SetErrorStatusExtension("OpenMode");
  }

  return result;
}

//=======================================================================
//function : ReadRootSection
//purpose  : read root part of the file
//Arguments:
//           s: driver to read
//=======================================================================

Handle(Storage_RootData) Storage_Schema::ReadRootSection
                         (Storage_BaseDriver& f) const
{
  Handle(Storage_RootData) result = new Storage_RootData;

  if ((f.OpenMode() == Storage_VSRead) || (f.OpenMode() == Storage_VSReadWrite)) {
    IReadRootSection(f,result);
  }
  else {
    result->SetErrorStatus(Storage_VSModeError);
    result->SetErrorStatusExtension("OpenMode");
  }

  return result;
}

//=======================================================================
//function : SchemaKnownTypes
//purpose  : returns the known types of a schema
//=======================================================================

const TColStd_SequenceOfAsciiString& Storage_Schema::SchemaKnownTypes() const
{
  static TColStd_SequenceOfAsciiString aSeq;
  return aSeq;
}

//=======================================================================
//function : GetAllSchemaKnownTypes
//purpose  : returns the all known types  of a schema and their
//           nested schemes.
//PTV      : add get of all known type for inheritance of schemas
//=======================================================================

Handle(TColStd_HSequenceOfAsciiString) Storage_Schema::
                          GetAllSchemaKnownTypes() const
{
  Handle(TColStd_HSequenceOfAsciiString) aSeqOfType = new TColStd_HSequenceOfAsciiString;
  const TColStd_SequenceOfAsciiString& alocalTypeList = SchemaKnownTypes();

  for (Standard_Integer k = 1; k <= alocalTypeList.Length(); k++)
    aSeqOfType->Append(alocalTypeList.Value(k));

  // get nested schemas
  Handle(Storage_HArrayOfSchema) aNestedSchemas = NestedSchemas();
  if (!aNestedSchemas.IsNull())
  {
    for (Standard_Integer i = aNestedSchemas->Lower(); i <= aNestedSchemas->Upper(); i++)
    {
      Handle(Storage_Schema) aSchema = aNestedSchemas->Value(i);
      if (aSchema.IsNull())
        continue;

      Handle(TColStd_HSequenceOfAsciiString) typeList = aSchema->GetAllSchemaKnownTypes();
      for (Standard_Integer j = 1; j <= typeList->Length(); j++)
        aSeqOfType->Append(typeList->Value(j));
    }
  }

  return aSeqOfType;
}

//=======================================================================
//function : HasUnknownType
//purpose  : indicates whether  the  are  types  in  the driver
//           which are not known from  the schema and for which
//           no callbacks have been set. The unknown types can
//           be read in <theUnknownTypes>.
//=======================================================================

Standard_Boolean Storage_Schema::HasUnknownType
                         (Storage_BaseDriver& f,
                          TColStd_SequenceOfAsciiString& theUnknownTypes) const
{
  Standard_Boolean result = Standard_False;
  Handle(TColStd_HSequenceOfAsciiString) typeList = GetAllSchemaKnownTypes();

  Handle(Storage_TypeData) tData;

  tData = ReadTypeSection(f);

  result = (tData->ErrorStatus() != Storage_VSOk);

  if (!result) {
    Standard_Integer i;
    Storage_MapOfAsciiString names;

    for (i = 1; i <= typeList->Length(); i++) {
      names.Add(typeList->Value(i));
    }

    Handle(TColStd_HSequenceOfAsciiString) flist = tData->Types();

    for (i = 1; i <= flist->Length(); i++) {
      if (!names.Contains(flist->Value(i))) {
        theUnknownTypes.Append(flist->Value(i));
        result = Standard_True;
      }
    }
  }

  return result;
}

//=======================================================================
//function : SetNestedSchemas
//purpose  : 
//=======================================================================

void Storage_Schema::SetNestedSchemas
                         (const Handle(Storage_HArrayOfSchema)& theSchemas)
{
  myArrayOfSchema = theSchemas;
}

//=======================================================================
//function : ClearNestedSchemas
//purpose  : 
//=======================================================================

void Storage_Schema::ClearNestedSchemas()
{
  myArrayOfSchema.Nullify();
}

//=======================================================================
//function : NestedSchemas
//purpose  : 
//=======================================================================

Handle(Storage_HArrayOfSchema) Storage_Schema::NestedSchemas() const
{
  return myArrayOfSchema;
}

//=======================================================================
//function : AddReadUnknownTypeCallBack
//purpose  : add two functions to the callback list
//=======================================================================

void Storage_Schema::AddReadUnknownTypeCallBack
                         (const TCollection_AsciiString& aTypeName,
                          const Handle(Storage_CallBack)& aCallBack)
{
  if (!aCallBack.IsNull()) {
     Handle(Storage_TypedCallBack) aTCallBack = new Storage_TypedCallBack(aTypeName,aCallBack);

     myCallBack.Bind(aTypeName,aTCallBack);
  }
}

//=======================================================================
//function : RemoveReadUnknownTypeCallBack
//purpose  : remove a callback for a type
//=======================================================================

void Storage_Schema::RemoveReadUnknownTypeCallBack
                         (const TCollection_AsciiString& aTypeName)
{
  if (myCallBack.IsBound(aTypeName)) {
    myCallBack.UnBind(aTypeName);
  }
}

//=======================================================================
//function : InstalledCallBackList
//purpose  : returns  a  list  of   type  name  with  installed
//           callback.
//=======================================================================

Handle(TColStd_HSequenceOfAsciiString) Storage_Schema::
                          InstalledCallBackList() const
{
  Storage_DataMapIteratorOfMapOfCallBack it(myCallBack);
  Handle(TColStd_HSequenceOfAsciiString) result = new TColStd_HSequenceOfAsciiString;

  for (; it.More(); it.Next()) {
    result->Append(it.Key());
  }

  return result;
}

//=======================================================================
//function : ClearCallBackList
//purpose  : clear all callback from schema instance.
//=======================================================================

void Storage_Schema::ClearCallBackList()
{
  myCallBack.Clear();
}

//=======================================================================
//function : UseDefaultCallBack
//purpose  : install  a  callback  for  all  unknown  type. the
//           objects with unknown types  will be skipped. (look
//           SkipObject method in BaseDriver)
//=======================================================================

void Storage_Schema::UseDefaultCallBack()
{
  myCallBackState = Standard_True;
}

//=======================================================================
//function : DontUseDefaultCallBack
//purpose  : tells schema to uninstall the default callback.
//=======================================================================

void Storage_Schema::DontUseDefaultCallBack()
{
  myCallBackState = Standard_False;
}

//=======================================================================
//function : IsUsingDefaultCallBack
//purpose  : ask if the schema is using the default callback.
//=======================================================================

Standard_Boolean Storage_Schema::IsUsingDefaultCallBack() const
{
  return myCallBackState;
}

//=======================================================================
//function : SetDefaultCallBack
//purpose  : overload the  default  function  for build.(use to
//           set an  error  message  or  skip  an  object while
//           reading an unknown type).
//=======================================================================

void Storage_Schema::SetDefaultCallBack(const Handle(Storage_CallBack)& f)
{
  myDefaultCallBack = f;
}

//=======================================================================
//function : ResetDefaultCallBack
//purpose  : reset  the  default  function  defined  by Storage
//           package.
//=======================================================================

void Storage_Schema::ResetDefaultCallBack()
{
  myDefaultCallBack = new Storage_DefaultCallBack;
}

//=======================================================================
//function : DefaultCallBack
//purpose  : returns   the   read   function   used   when  the
//           UseDefaultCallBack() is set.
//=======================================================================

Handle(Storage_CallBack) Storage_Schema::DefaultCallBack() const
{
  return myDefaultCallBack;
}

//=======================================================================
//function : ResolveUnknownType
//purpose  : 
//=======================================================================

Handle(Storage_CallBack) Storage_Schema::ResolveUnknownType
                         (const TCollection_AsciiString&     aTypeName,
                          const Handle(Standard_Persistent)& p,
                          const Storage_SolveMode            aMode) const
{
  Handle(Storage_CallBack) theCallBack;

  if (!myArrayOfSchema.IsNull()) {
    Standard_Integer i;
    Standard_Boolean IsNotFound     = Standard_True;
    Standard_Boolean AlreadyMatched;

    for(i = myArrayOfSchema->Lower(); i <= myArrayOfSchema->Upper() && IsNotFound; i++) {
      Handle(Storage_Schema) aSchema = myArrayOfSchema->Value(i);

      if (!aSchema.IsNull()) {
        AlreadyMatched = aSchema->SetNested();
        if (!AlreadyMatched) {
          if (aMode == Storage_WriteSolve || aMode == Storage_ReadSolve) {
            theCallBack = aSchema->CallBackSelection(aTypeName);
          }
          else if (aMode == Storage_AddSolve) {
            theCallBack = aSchema->AddTypeSelection(p);
          }
          aSchema->UnsetNested();
          IsNotFound = theCallBack.IsNull();
        }
      }
    }
  }

  if (!myNestedState && theCallBack.IsNull()) {
    if (myCallBack.IsBound(aTypeName)) {
      theCallBack = myCallBack.Find(aTypeName)->CallBack();
    }
    else if (myCallBackState == Standard_True) {
      theCallBack = myDefaultCallBack;
    }
    else {
      Clear();
      Standard_SStream aMsg;

      aMsg << "Unknown type " << aTypeName << " in schema ";

      if (!myName.IsEmpty()) {
        aMsg << myName;
      }

      Storage_StreamUnknownTypeError::Raise(aMsg);
    }
  }

  return theCallBack;
}

//=======================================================================
//function : CallBackSelection
//purpose  : 
//=======================================================================

Handle(Storage_CallBack) Storage_Schema::CallBackSelection
                         (const TCollection_AsciiString&) const
{
  Handle(Storage_CallBack) theCallBack;

  return theCallBack;
}

//=======================================================================
//function : AddTypeSelection
//purpose  : 
//=======================================================================

Handle(Storage_CallBack)  Storage_Schema::AddTypeSelection
                         (const Handle(Standard_Persistent)&) const
{
  Handle(Storage_CallBack) theCallBack;

  return theCallBack;
}

//=======================================================================
//function : BindType
//purpose  : 
//=======================================================================

void Storage_Schema::BindType
                         (const TCollection_AsciiString& aTypeName,
                          const Handle(Storage_CallBack)& aCallBack) const
{
  if (!HasTypeBinding(aTypeName)) {
      Handle(Storage_InternalData) iData = Storage_Schema::ICurrentData()->InternalData();
      Handle(Storage_TypeData) tData = Storage_Schema::ICurrentData()->TypeData();
      Handle(Storage_TypedCallBack) c = new Storage_TypedCallBack(aTypeName,aCallBack);

      tData->AddType(aTypeName,iData->myTypeId);
      c->SetIndex(iData->myTypeId++);
      iData->myTypeBinding.Bind(aTypeName,c);
  }
}

//=======================================================================
//function : TypeBinding
//purpose  : 
//=======================================================================

Handle(Storage_CallBack) Storage_Schema::TypeBinding
                         (const TCollection_AsciiString& aTypeName) const
{
  Handle(Storage_CallBack) result;

  if (HasTypeBinding(aTypeName)) {
    Handle(Storage_InternalData) iData = Storage_Schema::ICurrentData()->InternalData();

    result =  iData->myTypeBinding.Find(aTypeName)->CallBack();
  }

  return result;
}

//=======================================================================
//function : ReadPersistentReference
//purpose  : 
//=======================================================================

void Storage_Schema::ReadPersistentReference
                         (Handle(Standard_Persistent)& sp,
                          Storage_BaseDriver&          f)
{
  Standard_Integer ref;

  f.GetReference(ref);

  if (ref != 0) {
    Handle(Storage_InternalData) iData = Storage_Schema::ICurrentData()->InternalData();

    sp = iData->myReadArray->Value(ref);
  }
  else {
    sp.Nullify();
  }
}

//=======================================================================
//function : AddPersistent
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::AddPersistent
                         (const Handle(Standard_Persistent)& sp,
                          const Standard_CString tName) const
{
  Standard_Boolean result = Standard_False;

  if (!sp.IsNull()) {
    Handle(Storage_InternalData)     iData = Storage_Schema::ICurrentData()->InternalData();

    if (sp->_typenum == 0) {
      Standard_Integer         aTypenum;
      static TCollection_AsciiString  aTypeName;
      aTypeName = tName;
      Handle(Storage_TypeData) tData = Storage_Schema::ICurrentData()->TypeData();

      aTypenum = iData->myTypeBinding.Find(aTypeName)->Index();

      sp->_typenum = aTypenum;
      sp->_refnum = iData->myObjId++;

      result = Standard_True;
    }
  }

  return result;
}

//=======================================================================
//function : PersistentToAdd
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::PersistentToAdd
                         (const Handle(Standard_Persistent)& sp) const
{
 Standard_Boolean result = Standard_False;

  if (!sp.IsNull()) {
    Handle(Storage_InternalData) di = Storage_Schema::ICurrentData()->InternalData();

    if (sp->_typenum == 0 && sp->_refnum != -1) {
      result = Standard_True;
      sp->_refnum = -1;
      di->myPtoA.Append(sp);
    }
  }

  return result;
}

//=======================================================================
//function : Clear
//purpose  : 
//=======================================================================

void Storage_Schema::Clear() const
{
  Storage_Schema::ICurrentData().Nullify();
}

//=======================================================================
//function : IReadHeaderSection
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::IReadHeaderSection
                         (Storage_BaseDriver& f,
                          const Handle(Storage_HeaderData)& iData) const
{
  Standard_Boolean                 result = Standard_False;
  Storage_Error                    errorCode;
  TCollection_AsciiString          uinfo,mStorageVersion,mDate,mSchemaName,mSchemaVersion,mApplicationVersion;
  TCollection_ExtendedString       mApplicationName,mDataType;
  TColStd_SequenceOfAsciiString    mUserInfo;
  TColStd_SequenceOfExtendedString mComment;
  Standard_Integer                 mNBObj;

  errorCode = f.BeginReadInfoSection();

  if (errorCode == Storage_VSOk) {
    {
      try {
        OCC_CATCH_SIGNALS
        f.ReadInfo(mNBObj,
                   mStorageVersion,
                   mDate,
                   mSchemaName,
                   mSchemaVersion,
                   mApplicationName,
                   mApplicationVersion,
                   mDataType,
                   mUserInfo);
      }
      catch(Storage_StreamTypeMismatchError) {
        iData->SetErrorStatus(Storage_VSTypeMismatch);
        iData->SetErrorStatusExtension("ReadInfo");
        return Standard_False;
      }
      catch(Storage_StreamExtCharParityError) {
        iData->SetErrorStatus(Storage_VSExtCharParityError);
        iData->SetErrorStatusExtension("ReadInfo");
        return Standard_False;
      }
    }


    errorCode = f.EndReadInfoSection();

    iData->SetErrorStatus(errorCode);

    result = (errorCode == Storage_VSOk);

    if (result) {
      Standard_Integer i;

      iData->SetNumberOfObjects(mNBObj);
      iData->SetStorageVersion(mStorageVersion);
      iData->SetCreationDate(mDate);
      iData->SetSchemaName(mSchemaName);
      iData->SetSchemaVersion(mSchemaVersion);
      iData->SetApplicationName(mApplicationName);
      iData->SetApplicationVersion(mApplicationVersion);
      iData->SetDataType(mDataType);

      for (i = 1; i <= mUserInfo.Length(); i++) {
        iData->AddToUserInfo(mUserInfo.Value(i));
      }

      errorCode = f.BeginReadCommentSection();

      if (errorCode == Storage_VSOk) {
        {
          {
            try {
              OCC_CATCH_SIGNALS
              f.ReadComment(mComment);
            }
            catch(Storage_StreamTypeMismatchError) {
              iData->SetErrorStatus(Storage_VSTypeMismatch);
              iData->SetErrorStatusExtension("ReadComment");
              return Standard_False;
            }
            catch(Storage_StreamExtCharParityError) {
              iData->SetErrorStatus(Storage_VSExtCharParityError);
              iData->SetErrorStatusExtension("ReadComment");
              return Standard_False;
            }
          }
        }

        errorCode = f.EndReadCommentSection();
        iData->SetErrorStatus(errorCode);
        iData->SetErrorStatusExtension("EndReadCommentSection");
        result = (errorCode == Storage_VSOk);

        if (result) {
          for (i = 1; i <= mComment.Length(); i++) {
            iData->AddToComments(mComment.Value(i));
          }
        }
      }
      else {
        result = Standard_False;
        iData->SetErrorStatus(errorCode);
        iData->SetErrorStatusExtension("BeginReadCommentSection");
     }
    }
    else {
      iData->SetErrorStatusExtension("EndReadInfoSection");
    }
  }
  else {
    iData->SetErrorStatus(errorCode);
    iData->SetErrorStatusExtension("BeginReadInfoSection");
  }

  return result;
}

//=======================================================================
//function : IReadTypeSection
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::IReadTypeSection
                         (Storage_BaseDriver& f,
                          const Handle(Storage_TypeData)& tData) const
{
static Standard_Boolean         result;
  TCollection_AsciiString  typeName;
  Standard_Integer         typeNum;

  Storage_Error      errorCode;
  Standard_Integer len,i;

  result = Standard_False;
  errorCode = f.BeginReadTypeSection();

  if (errorCode == Storage_VSOk) {
    try {
      OCC_CATCH_SIGNALS
      len = f.TypeSectionSize();

      for (i = 1; i <= len; i++) {
        f.ReadTypeInformations(typeNum,typeName);
        tData->AddType(typeName,typeNum);
      }
      result = Standard_True;
    }
    catch(Storage_StreamTypeMismatchError) {
      tData->SetErrorStatus(Storage_VSTypeMismatch);
      tData->SetErrorStatusExtension("ReadTypeInformations");
      return Standard_False;
    }

    if (result) {
      errorCode = f.EndReadTypeSection();
      result = (errorCode == Storage_VSOk);

      tData->SetErrorStatus(errorCode);
      if (!result) tData->SetErrorStatusExtension("EndReadTypeSection");
    }
  }
  else {
    tData->SetErrorStatus(errorCode);
    tData->SetErrorStatusExtension("BeginReadTypeSection");
  }

  return result;
}

//=======================================================================
//function : IReadRootSection
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::IReadRootSection
                         (Storage_BaseDriver& f,
                          const Handle(Storage_RootData)& rData) const
{
static Standard_Boolean            result;
  Standard_Integer            len,i,ref;
  Storage_Error               errorCode;
  Handle(Standard_Persistent) p;
  Handle(Storage_Root)        aRoot;

  result = Standard_False;
  errorCode = f.BeginReadRootSection();

  if (errorCode == Storage_VSOk) {
    TCollection_AsciiString rootName,typeName;

    try {
      OCC_CATCH_SIGNALS
      len = f.RootSectionSize();

      for (i = 1; i <= len; i++) {
        f.ReadRoot(rootName,ref,typeName);
        aRoot = new Storage_Root(rootName,p);
        aRoot->SetReference(ref);
        aRoot->SetType(typeName);
        rData->AddRoot(aRoot);
      }
      result = Standard_True;
    }
    catch(Storage_StreamTypeMismatchError) {
      result = Standard_False;
      rData->SetErrorStatus(Storage_VSTypeMismatch);
      rData->SetErrorStatusExtension("ReadRoot");
    }

    if (result) {
      errorCode = f.EndReadRootSection();
      result = (errorCode == Storage_VSOk);

      rData->SetErrorStatus(errorCode);
      if (!result) rData->SetErrorStatusExtension("EndReadRootSection");
    }
  }
  else {
    rData->SetErrorStatus(errorCode);
    rData->SetErrorStatusExtension("BeginReadRootSection");
  }

  return result;
}

//=======================================================================
//function : ISetCurrentData
//purpose  : 
//=======================================================================

void Storage_Schema::ISetCurrentData(const Handle(Storage_Data)& dData)
{
  Storage_Schema::ICurrentData() = dData;
}

//=======================================================================
//function : ICurrentData
//purpose  : 
//=======================================================================

Handle(Storage_Data)& Storage_Schema::ICurrentData()
{
  static Handle(Storage_Data) _Storage_CData;
  return _Storage_CData;
}

#define SLENGTH 80

//=======================================================================
//function : ICreationDate
//purpose  : 
//=======================================================================

TCollection_AsciiString Storage_Schema::ICreationDate()
{
 // on sauvegarde l'ancien LC_NUMERIC
  char *oldnum,*plocal ;
  plocal =   setlocale(LC_NUMERIC, NULL);
  oldnum = new char[strlen(plocal)+1] ;
  strcpy(oldnum,plocal);


  char nowstr[SLENGTH];
  time_t nowbin;
  struct tm *nowstruct;

  (void)setlocale(LC_ALL, "");

  if (time(&nowbin) == (time_t) - 1)
    cerr << "Storage ERROR : Could not get time of day from time()" << endl;

  nowstruct = localtime(&nowbin);

  if (strftime(nowstr, SLENGTH, "%m/%d/%Y", nowstruct) == (size_t) 0)
    cerr << "Storage ERROR : Could not get string from strftime()" << endl;

  TCollection_AsciiString t(nowstr);

  // on remet le LC_NUMERIC a la precedente valeur
  setlocale(LC_NUMERIC, oldnum);
  delete[] oldnum;

  return t;
}

//=======================================================================
//function : SetNested
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::SetNested()
{
  Standard_Boolean result = myNestedState;

  myNestedState = Standard_True;

  return result;
}

//=======================================================================
//function : IsNested
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::IsNested() const
{
  return myNestedState;
}

//=======================================================================
//function : UnsetNested
//purpose  : 
//=======================================================================

Standard_Boolean Storage_Schema::UnsetNested()
{
  Standard_Boolean result = myNestedState;

  myNestedState = Standard_False;

  return result;
}
