// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

//    abv 09.04.99 S4136: eliminate parameter step.readaccept.void
//    sln 04,10.2001. BUC61003. Prevent exception which may occur during reading of complex entity
//    (if entity's items are not in alphabetical order)

#include <Interface_Check.hxx>
#include <Interface_HArray1OfHAsciiString.hxx>
#include <Interface_Macros.hxx>
#include <Interface_ParamList.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <Standard_Transient.hxx>
#include <Standard_Type.hxx>
#include <StepData_ESDescr.hxx>
#include <StepData_FieldList.hxx>
#include <StepData_PDescr.hxx>
#include <StepData_SelectArrReal.hxx>
#include <StepData_SelectInt.hxx>
#include <StepData_SelectMember.hxx>
#include <StepData_SelectNamed.hxx>
#include <StepData_SelectReal.hxx>
#include <StepData_SelectType.hxx>
#include <StepData_StepReaderData.hxx>
#include <NCollection_IncAllocator.hxx>
#include <TCollection_AsciiString.hxx>
#include <TCollection_ExtendedString.hxx>
#include <NCollection_UtfIterator.hxx>
#include <TCollection_HAsciiString.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <TColStd_HArray1OfInteger.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_HArray1OfTransient.hxx>
#include <TColStd_HSequenceOfReal.hxx>
#include <TColStd_IndexedMapOfInteger.hxx>
#include <TColStd_SequenceOfInteger.hxx>
#include <StepData_UndefinedEntity.hxx>
#include <Resource_Unicode.hxx>

#include <stdio.h>
IMPLEMENT_STANDARD_RTTIEXT(StepData_StepReaderData, Interface_FileReaderData)

// Le Header est constitue d entites analogues dans leur principe a celles
// du Data, a ceci pres qu elles sont sans identifieur, et ne peuvent ni
// referencer, ni etre referencees (que ce soit avec Header ou avec Data)
// Ainsi, dans StepReaderData, le Header est constitue des "thenbhead" 1res Entites
//  #########################################################################
//  ....   Creation et Acces de base aux donnees atomiques du fichier    ....
typedef TCollection_HAsciiString String;
static char                      txtmes[200]; // plus commode que redeclarer partout

static Standard_Boolean initstr = Standard_False;
#define Maxlst 64
// static TCollection_AsciiString subl[Maxlst];          // Maxlst : minimum 10

static Standard_Integer acceptvoid = 0;

// ----------  Fonctions Utilitaires  ----------

//! Convert unsigned character to hexadecimal system,
//! if character hasn't representation in this system, returns 0.
static Standard_Integer convertCharacterTo16bit(const Standard_ExtCharacter theCharacter)
{
  switch (theCharacter)
  {
    case '0':
      return 0;
    case '1':
      return 1;
    case '2':
      return 2;
    case '3':
      return 3;
    case '4':
      return 4;
    case '5':
      return 5;
    case '6':
      return 6;
    case '7':
      return 7;
    case '8':
      return 8;
    case '9':
      return 9;
    case 'A':
    case 'a':
      return 10;
    case 'B':
    case 'b':
      return 11;
    case 'C':
    case 'c':
      return 12;
    case 'D':
    case 'd':
      return 13;
    case 'E':
    case 'e':
      return 14;
    case 'F':
    case 'f':
      return 15;
    default:
      return 0;
  }
}

//=================================================================================================

void StepData_StepReaderData::cleanText(const Handle(TCollection_HAsciiString)& theVal) const
{
  if (theVal->Length() == 2)
  {
    theVal->Clear();
    return;
  }
  TCollection_ExtendedString aResString;
  const Standard_Boolean     toConversion = mySourceCodePage != Resource_FormatType_NoConversion;
  Resource_Unicode::ConvertFormatToUnicode(mySourceCodePage, theVal->ToCString() + 1, aResString);
  Standard_Integer           aResStringSize = aResString.Length() - 1; // skip the last apostrophe
  TCollection_ExtendedString aTempExtString;  // string for characters within control directives
  Standard_Integer           aSetCharInd = 1; // index to set value to result string
                                              // clang-format off
  Resource_FormatType aLocalFormatType = Resource_FormatType_iso8859_1; // a code page for a "\S\" control directive
                                              // clang-format on
  for (Standard_Integer aStringInd = 1; aStringInd <= aResStringSize; ++aStringInd)
  {
    const Standard_ExtCharacter aChar = aResString.Value(aStringInd);
    aSetCharInd                       = aStringInd;
    if (aChar == '\\' && aStringInd <= aResStringSize - 3) // can contains the control directive
    {
      Standard_Boolean            isConverted  = Standard_False;
      const Standard_ExtCharacter aDirChar     = aResString.Value(aStringInd + 1);
      const Standard_Boolean      isSecSlash   = aResString.Value(aStringInd + 2) == '\\';
      const Standard_Boolean      isThirdSlash = aResString.Value(aStringInd + 3) == '\\';
      // Encoding ISO 8859 characters within a string;
      // ("\P{N}\") control directive;
      // indicates code page for ("\S\") control directive;
      // {N}: "A", "B", "C", "D", "E", "F", "G", "H", "I";
      // "A" identifies ISO 8859-1; "B" identifies ISO 8859-2, etc.
      if (aDirChar == 'P' && isThirdSlash)
      {
        const Standard_Character aPageId =
          UpperCase(static_cast<Standard_Character>(aResString.Value(aStringInd + 2) & 255));
        if (aPageId >= 'A' && aPageId <= 'I')
        {
          aLocalFormatType = (Resource_FormatType)(Resource_FormatType_iso8859_1 + (aPageId - 'A'));
        }
        else
        {
          thecheck->AddWarning(
            "String control directive \\P*\\ with an unsupported symbol in place of *");
        }
        isConverted = Standard_True;
        aStringInd += 3;
      }
      // Encoding ISO 8859 characters within a string;
      // ("\S\") control directive;
      // converts followed a LATIN CODEPOINT character.
      else if (aDirChar == 'S' && isSecSlash)
      {
        Standard_Character aResChar =
          static_cast<Standard_Character>(aResString.Value(aStringInd + 3) | 0x80);
        const char aStrForCovert[2] = {aResChar, '\0'};
        Resource_Unicode::ConvertFormatToUnicode(aLocalFormatType, aStrForCovert, aTempExtString);
        isConverted = Standard_True;
        aStringInd += 3;
      }
      // Encoding U+0000 to U+00FF in a string
      // ("\X\") control directive;
      // converts followed two hexadecimal character.
      else if (aDirChar == 'X' && aStringInd <= aResStringSize - 4 && isSecSlash)
      {
        Standard_Character aResChar =
          (char)convertCharacterTo16bit(aResString.Value(aStringInd + 3));
        aResChar =
          (aResChar << 4) | (char)convertCharacterTo16bit(aResString.Value(aStringInd + 4));
        const char aStrForConvert[2] = {aResChar, '\0'};
        // clang-format off
        aTempExtString = TCollection_ExtendedString(aStrForConvert, Standard_False); // pass through without conversion
        // clang-format on
        isConverted = Standard_True;
        aStringInd += 4;
      }
      // Encoding ISO 10646 characters within a string
      // ("\X{N}\") control directive;
      // {N}: "0", "2", "4";
      // "\X2\" or "\X4\" converts followed a hexadecimal character sequence;
      // "\X0\" indicate the end of the "\X2\" or "\X4\".
      else if (aDirChar == 'X' && isThirdSlash)
      {
        Standard_Integer aFirstInd = aStringInd + 3;
        Standard_Integer aLastInd  = aStringInd;
        Standard_Boolean isClosed  = Standard_False;
        // find the end of the "\X2\" or "\X4\" by an external "aStringInd"
        for (; aStringInd <= aResStringSize && !isClosed; ++aStringInd)
        {
          if (aResStringSize - aStringInd > 2 && aResString.Value(aStringInd) == '\\'
              && aResString.Value(aStringInd + 1) == 'X' && aResString.Value(aStringInd + 2) == '0'
              && aResString.Value(aStringInd + 3) == '\\')
          {
            aLastInd   = aStringInd - 1;
            aStringInd = aStringInd + 2;
            isClosed   = Standard_True;
          }
        }
        if (!isClosed) // "\X0\" not exists
        {
          aLastInd = aStringInd = aResStringSize;
        }
        const Standard_Integer aStrLen = aLastInd - aFirstInd;
        // "\X2\" control directive;
        // followed by multiples of four or three hexadecimal characters.
        // Encoding in UTF-16
        if (aResString.Value(aFirstInd - 1) == '2' && aResStringSize - aFirstInd > 3)
        {
          Standard_Integer anIterStep = (aStrLen % 4 == 0) ? 4 : 3;
          if (aStrLen % anIterStep)
          {
            aTempExtString.AssignCat('?');
            thecheck->AddWarning(
              "String control directive \\X2\\ is followed by number of digits not multiple of 4");
          }
          else
          {
            Standard_Utf16Char aUtfCharacter = '\0';
            for (Standard_Integer aCharInd = 1; aCharInd <= aStrLen; ++aCharInd)
            {
              aUtfCharacter |= convertCharacterTo16bit(aResString.Value(aCharInd + aFirstInd));
              if (aCharInd % anIterStep == 0)
              {
                aTempExtString.AssignCat(aUtfCharacter);
                aUtfCharacter = '\0';
              }
              aUtfCharacter = aUtfCharacter << 4;
            }
          }
        }
        // "\X4\" control directive;
        // followed by multiples of eight hexadecimal characters.
        // Encoding in UTF-32
        else if (aResString.Value(aFirstInd - 1) == '4' && aResStringSize - aFirstInd > 7)
        {
          if (aStrLen % 8)
          {
            aTempExtString.AssignCat('?');
            thecheck->AddWarning(
              "String control directive \\X4\\ is followed by number of digits not multiple of 8");
          }
          else
          {
            Standard_Utf32Char aUtfCharacter[2] = {'\0', '\0'};
            for (Standard_Integer aCharInd = 1; aCharInd <= aStrLen; ++aCharInd)
            {
              aUtfCharacter[0] |= convertCharacterTo16bit(aResString.Value(aCharInd + aFirstInd));
              if (aCharInd % 8 == 0)
              {
                NCollection_Utf32Iter aUtfIter(aUtfCharacter);
                Standard_Utf16Char    aStringBuffer[3];
                Standard_Utf16Char*   aUtfPntr = aUtfIter.GetUtf16(aStringBuffer);
                *aUtfPntr++                    = '\0';
                TCollection_ExtendedString aUtfString(aStringBuffer);
                aTempExtString.AssignCat(aUtfString);
                aUtfCharacter[0] = '\0';
              }
              aUtfCharacter[0] = aUtfCharacter[0] << 4;
            }
          }
        }
        isConverted = Standard_True;
      }
      if (isConverted) // find the control directive
      {
        if (toConversion) // else skip moving
        {
          // clang-format off
          aResStringSize -= aStringInd - aSetCharInd - aTempExtString.Length() + 1; // change the string size to remove unused symbols
          // clang-format on
          aResString.SetValue(aSetCharInd, aTempExtString);
          aSetCharInd += aTempExtString.Length(); // move to the new position
          aResString.SetValue(aSetCharInd, aResString.ToExtString() + aStringInd);
          aStringInd = aSetCharInd - 1;
          aResString.Trunc(aResStringSize);
          ;
        }
        aTempExtString.Clear();
        continue;
      }
    }
    if (aStringInd <= aResStringSize - 1)
    {
      const Standard_ExtCharacter aCharNext = aResString.Value(aStringInd + 1);
      if (aCharNext == aChar && (aChar == '\'' || aChar == '\\'))
      {
        // clang-format off
        aResString.SetValue(aSetCharInd, aResString.ToExtString() + aStringInd); // move the string,removing one symbol
        // clang-format on
        aResStringSize--; // change the string size to remove unused symbol
        aResString.Trunc(aResStringSize);
      }
      else if (aChar == '\\')
      {
        const Standard_Boolean isDirective =
          aStringInd <= aResStringSize - 2 && aResString.Value(aStringInd + 2) == '\\';
        if (isDirective)
        {
          if (aCharNext == 'N')
          {
            aResString.SetValue(aSetCharInd++, '\n');
            // clang-format off
            aResString.SetValue(aSetCharInd, aResString.ToExtString() + aStringInd + 2); // move the string,removing two symbols
            aResStringSize-=2; // change the string size to remove unused symbols
            aResString.Trunc(aResStringSize);
            continue;
          }
          else if (aCharNext == 'T')
          {
            aResString.SetValue(aSetCharInd++, '\t');
            aResString.SetValue(aSetCharInd, aResString.ToExtString() + aStringInd + 2); // move the string,removing two symbols
            // clang-format on
            aResStringSize -= 2; // change the string size to remove unused symbols
            aResString.Trunc(aResStringSize);
            continue;
          }
        }
      }
    }
    if (aChar == '\n' || aChar == '\r')
    {
      aResString.SetValue(aSetCharInd, aResString.ToExtString() + aStringInd);
      aResStringSize--;
      aResString.Trunc(aResStringSize);
      aStringInd--;
    }
  }
  theVal->Clear();
  aResString.Trunc(aResStringSize); // trunc the last apostrophe
  TCollection_AsciiString aTmpString(aResString, 0);
  theVal->AssignCat(aTmpString.ToCString());
}

//  -------------  METHODES  -------------

//=================================================================================================

StepData_StepReaderData::StepData_StepReaderData(const Standard_Integer    nbheader,
                                                 const Standard_Integer    nbtotal,
                                                 const Standard_Integer    nbpar,
                                                 const Resource_FormatType theSourceCodePage)
    : Interface_FileReaderData(nbtotal, nbpar),
      theidents(1, nbtotal),
      thetypes(1, nbtotal),
      mySourceCodePage(theSourceCodePage) //, themults (1,nbtotal)
{
  //  char textnum[10];
  thenbscop = 0;
  thenbents = 0;
  thelastn  = 0;
  thenbhead = nbheader;
  // themults.Init(0);
  thecheck = new Interface_Check;
  if (initstr)
    return;
  // for (Standard_Integer i = 0; i < Maxlst; i ++) {
  //   sprintf(textnum,"$%d",i+1);
  //   subl[i].AssignCat(textnum);
  // }
  initstr = Standard_True;
}

//=================================================================================================

void StepData_StepReaderData::SetRecord(const Standard_Integer num,
                                        const Standard_CString ident,
                                        const Standard_CString type,
                                        const Standard_Integer /* nbpar */)
{
  Standard_Integer numlst;

  if (type[0] != '(')
    thenbents++; // total de termes propres du fichier

  thetypes.ChangeValue(num) = thenametypes.Add(TCollection_AsciiString(type));

  if (ident[0] == '$')
  {
    if (strlen(ident) > 2)
      numlst = atoi(&ident[1]);
    else
      numlst = ident[1] - 48;
    if (thelastn < numlst)
      thelastn = numlst; // plus fort n0 de sous-liste
    theidents.SetValue(num, -2 - numlst);
  }
  else if (ident[0] == '#')
  {
    numlst = atoi(&ident[1]);
    theidents.SetValue(num, numlst);
    if (numlst == 0 && num > thenbhead)
    {
      //    Header, ou bien Type Complexe ...
      //    Si Type Complexe, retrouver Type Precedent (on considere que c est rare)
      //    On chaine le type precedent sur le suivant
      //    VERIFICATION que les types sont en ordre alphabetique
      for (Standard_Integer prev = num - 1; prev > thenbhead; prev--)
      {
        if (theidents(prev) >= 0)
        {

          // themults.SetValue(prev,num);
          themults.Bind(prev, num);
          if (thenametypes.FindKey(thetypes.Value(num))
                .IsLess(thenametypes.FindKey(thetypes.Value(prev))))
          {
            //  Warning: components in complex entity are not in alphabetical order.
            TCollection_AsciiString errm("Complex Type incorrect : ");
            errm.AssignCat(thenametypes.FindKey(thetypes.Value(prev)));
            errm.AssignCat(" / ");
            errm.AssignCat(thenametypes.FindKey(thetypes.Value(num)));
            errm.AssignCat(" ... ");
            while (theidents(prev) <= 0)
            {
              prev--;
              if (prev <= 0)
                break;
            }

            Message_Messenger::StreamBuffer sout = Message::SendTrace();
            sout << "  ***  Incorrect record " << num << " (on " << NbRecords() << " -> "
                 << num * 100 / NbRecords() << " % in File)  ***";
            if (prev > 0)
              sout << "  Ident #" << theidents(prev);
            sout << "\n" << errm << std::endl;
            thecheck->AddWarning(errm.ToCString(), "Complex Type incorrect : ");
          }
          break;
        }
      }
    }
  }
  else if (!strcmp(ident, "SCOPE"))
  {
    theidents.SetValue(num, -1); // SCOPE
    thenbscop++;
  }
  else if (!strcmp(ident, "ENDSCOPE"))
    theidents.SetValue(num, -2); // ENDSCOPE
  //      Reste 0

  // InitParams(num);
}

//=================================================================================================

void StepData_StepReaderData::AddStepParam(const Standard_Integer    num,
                                           const Standard_CString    aval,
                                           const Interface_ParamType atype,
                                           const Standard_Integer    nument)
{
  if (atype == Interface_ParamSub)
  {
    Standard_Integer numid = 0;
    if (aval[2] != '\0')
    {
      numid = atoi(&aval[1]);
      //      if (numid <= Maxlst) Interface_FileReaderData::AddParam
      //	(num,subl[numid-1].ToCString(),atype,numid);
      Interface_FileReaderData::AddParam(num, aval, atype, numid);
    }
    else
    {
      char* numlstchar = (char*)(aval + 1);
      numid            = (*numlstchar) - 48; // -48 ('0') -1 (adresse [] depuis 0)
      //      Interface_FileReaderData::AddParam (num,subl[numid].ToCString(),atype,numid);
      Interface_FileReaderData::AddParam(num, aval, atype, numid);
    }
  }
  else if (atype == Interface_ParamIdent)
  {
    Standard_Integer numid = atoi(&aval[1]);
    Interface_FileReaderData::AddParam(num, aval, atype, numid);
  }
  else
  {
    Interface_FileReaderData::AddParam(num, aval, atype, nument);
  }

  //  Interface_FileReaderData::AddParam (num,parval,atype,numid);
}

//=================================================================================================

const TCollection_AsciiString& StepData_StepReaderData::RecordType(const Standard_Integer num) const
{
  return thenametypes.FindKey(thetypes.Value(num));
}

//=================================================================================================

Standard_CString StepData_StepReaderData::CType(const Standard_Integer num) const
{
  return thenametypes.FindKey(thetypes.Value(num)).ToCString();
}

//=================================================================================================

Standard_Integer StepData_StepReaderData::RecordIdent(const Standard_Integer num) const
{
  return theidents(num);
}

//  ########################################################################
//  ....       Aides a la lecture des parametres, adaptees a STEP       ....

//=================================================================================================

Standard_Integer StepData_StepReaderData::SubListNumber(const Standard_Integer num,
                                                        const Standard_Integer nump,
                                                        const Standard_Boolean aslast) const
{
  if (nump == 0 || nump > NbParams(num))
    return 0;
  const Interface_FileParameter& FP = Param(num, nump);
  if (FP.ParamType() != Interface_ParamSub)
    return 0;
  if (aslast)
  {
    if (nump != NbParams(num))
      return 0;
  }
  return FP.EntityNumber();
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::IsComplex(const Standard_Integer num) const
{
  // return (themults(num) != 0);
  return themults.IsBound(num);
}

//=================================================================================================

void StepData_StepReaderData::ComplexType(const Standard_Integer         num,
                                          TColStd_SequenceOfAsciiString& types) const
{
  if (theidents(num) < 0)
    return;
  for (Standard_Integer i = num; i > 0; i = NextForComplex(i))
  {
    types.Append(RecordType(i));
  }
}

//=================================================================================================

Standard_Integer StepData_StepReaderData::NextForComplex(const Standard_Integer num) const
{
  Standard_Integer next = 0;
  if (themults.IsBound(num))
    next = themults.Find(num);
  return next;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::NamedForComplex(const Standard_CString   name,
                                                          const Standard_Integer   num0,
                                                          Standard_Integer&        num,
                                                          Handle(Interface_Check)& ach) const
{
  // Standard_Boolean stat = Standard_True;
  Standard_Integer n = (num <= 0 ? num0 : NextForComplex(num));
  // sln 04,10.2001. BUC61003. if(n==0) the next  function is not called in order to avoid exception
  if ((n != 0) && (!strcmp(RecordType(n).ToCString(), name)))
  {
    num = n;
    return Standard_True;
  }

  if (n == 0)                            /*stat =*/
    NamedForComplex(name, num0, n, ach); // on a rembobine
                                         //  Pas dans l ordre alphabetique : boucler
  Handle(String) errmess = new String("Parameter n0.%d (%s) not a LIST");
  sprintf(txtmes, errmess->ToCString(), num0, name);
  for (n = num0; n > 0; n = NextForComplex(n))
  {
    if (!strcmp(RecordType(n).ToCString(), name))
    {
      num     = n;
      errmess = new String("Complex Record n0.%d, member type %s not in alphabetic order");
      sprintf(txtmes, errmess->ToCString(), num0, name);
      ach->AddWarning(txtmes, errmess->ToCString());
      return Standard_False;
    }
  }
  num     = 0;
  errmess = new String("Complex Record n0.%d, member type %s not found");
  sprintf(txtmes, errmess->ToCString(), num0, name);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::NamedForComplex(const Standard_CString   theName,
                                                          const Standard_CString   theShortName,
                                                          const Standard_Integer   num0,
                                                          Standard_Integer&        num,
                                                          Handle(Interface_Check)& ach) const
{
  Standard_Integer n = (num <= 0 ? num0 : NextForComplex(num));

  if ((n != 0)
      && (!strcmp(RecordType(n).ToCString(), theName)
          || !strcmp(RecordType(n).ToCString(), theShortName)))
  {
    num = n;
    return Standard_True;
  }

  // entities are not in alphabetical order
  Handle(String) errmess = new String("Parameter n0.%d (%s) not a LIST");
  sprintf(txtmes, errmess->ToCString(), num0, theName);
  for (n = num0; n > 0; n = NextForComplex(n))
  {
    if (!strcmp(RecordType(n).ToCString(), theName)
        || !strcmp(RecordType(n).ToCString(), theShortName))
    {
      num     = n;
      errmess = new String("Complex Record n0.%d, member type %s not in alphabetic order");
      sprintf(txtmes, errmess->ToCString(), num0, theName);
      ach->AddWarning(txtmes, errmess->ToCString());
      return Standard_False;
    }
  }
  num     = 0;
  errmess = new String("Complex Record n0.%d, member type %s not found");
  sprintf(txtmes, errmess->ToCString(), num0, theName);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##

//=================================================================================================

Standard_Boolean StepData_StepReaderData::CheckNbParams(const Standard_Integer   num,
                                                        const Standard_Integer   nbreq,
                                                        Handle(Interface_Check)& ach,
                                                        const Standard_CString   mess) const
{
  if (NbParams(num) == nbreq)
    return Standard_True;
  Handle(String) errmess;
  if (mess[0] == '\0')
    errmess = new String("Count of Parameters is not %d");
  else
    errmess = new String("Count of Parameters is not %d for %s");
  sprintf(txtmes, errmess->ToCString(), nbreq, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadSubList(const Standard_Integer   num,
                                                      const Standard_Integer   nump,
                                                      const Standard_CString   mess,
                                                      Handle(Interface_Check)& ach,
                                                      Standard_Integer&        numsub,
                                                      const Standard_Boolean   optional,
                                                      const Standard_Integer /* lenmin */,
                                                      const Standard_Integer /* lenmax */) const
{
  numsub = SubListNumber(num, nump, Standard_False);
  if (numsub > 0)
  {
    return (NbParams(numsub) > 0);
  }
  //  Si optionel indefini, on passe l eponge
  numsub                  = 0;
  Standard_Boolean isvoid = (Param(num, nump).ParamType() == Interface_ParamVoid);
  if (isvoid && optional)
    return Standard_False;

  Handle(String) errmess = new String("Parameter n0.%d (%s) not a LIST");
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (acceptvoid && isvoid)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
  {
    ach->AddFail(txtmes, errmess->ToCString());
    return Standard_False;
  }
  return Standard_True;
}

//  ...   Facilites pour LateBinding

//=================================================================================================

Standard_Integer StepData_StepReaderData::ReadSub(const Standard_Integer         numsub,
                                                  const Standard_CString         mess,
                                                  Handle(Interface_Check)&       ach,
                                                  const Handle(StepData_PDescr)& descr,
                                                  Handle(Standard_Transient)&    val) const
{
  Standard_Integer nbp = NbParams(numsub);
  if (nbp == 0)
    return 0; // liste vide = Handle Null
  const TCollection_AsciiString& rectyp = RecordType(numsub);
  if (nbp == 1 && rectyp.ToCString()[0] != '(')
  {
    //  c est un type avec un parametre -> SelectNamed
    //  cf ReadSelect mais ici, on est deja sur le contenu du parametre
    Handle(StepData_SelectNamed) sn = new StepData_SelectNamed;
    val                             = sn;
    sn->SetName(rectyp.ToCString());
    Handle(Standard_Transient) aSN = sn;
    if (ReadAny(numsub, 1, mess, ach, descr, aSN))
      return sn->Kind();
    else
      return 0;
  }

  //  cas courant : faire un HArray1 de ... de ... de quoi au fait
  const Interface_FileParameter&          FP0 = Param(numsub, 1);
  Interface_ParamType                     FT, FT0 = FP0.ParamType();
  Standard_CString                        str = FP0.CValue();
  Handle(TColStd_HArray1OfTransient)      htr;
  Handle(TColStd_HArray1OfInteger)        hin;
  Handle(TColStd_HArray1OfReal)           hre;
  Handle(Interface_HArray1OfHAsciiString) hst;
  Standard_Integer                        kod = 0;
  switch (FT0)
  {
    case Interface_ParamMisc:
      return -1;
    case Interface_ParamInteger:
      kod = 1;
      break;
    case Interface_ParamReal:
      kod = 5;
      break;
    case Interface_ParamIdent:
      kod = 7;
      break;
    case Interface_ParamVoid:
      kod = 0;
      break;
    case Interface_ParamText:
      kod = 6;
      break;
    case Interface_ParamEnum:
      kod = 4;
      break; // a confirmer(logical)
      /*      kod = 4;
        if ( str[0] == '.' && str[2] == '.' && str[3] == '\0' &&
        (str[1] == 'T' || str[1] == 'F' || str[1] == 'U') ) kod = 3;
        break; */ // svv #2
    case Interface_ParamLogical:
      return -1;
    case Interface_ParamSub:
      kod = 0;
      break;
    case Interface_ParamHexa:
      return -1;
    case Interface_ParamBinary:
      return -1;
    default:
      return -1;
  }
  if (kod == 1 || kod == 3)
  {
    hin = new TColStd_HArray1OfInteger(1, nbp);
    val = hin;
  }
  else if (kod == 5)
  {
    hre = new TColStd_HArray1OfReal(1, nbp);
    val = hre;
  }
  else if (kod == 6)
  {
    hst = new Interface_HArray1OfHAsciiString(1, nbp);
    val = hst;
  }
  else
  {
    htr = new TColStd_HArray1OfTransient(1, nbp);
    val = htr;
  }
  //  Attention : si type variable, faudra changer son fusil d epaule -> htr

  for (Standard_Integer ip = 1; ip <= nbp; ip++)
  {
    const Interface_FileParameter& FP = Param(numsub, ip);
    str                               = FP.CValue();
    FT                                = FP.ParamType();
    switch (kod)
    {
      case 1: {
        if (FT != Interface_ParamInteger)
        {
          kod = 0;
          break;
        }
        hin->SetValue(ip, atoi(str));
        break;
      }
      case 2:
      case 3: {
        if (FT != Interface_ParamEnum)
        {
          kod = 0;
          break;
        }
        if (!strcmp(str, ".F."))
          hin->SetValue(ip, 0);
        else if (!strcmp(str, ".T."))
          hin->SetValue(ip, 1);
        else if (!strcmp(str, ".U."))
          hin->SetValue(ip, 2);
        else
          kod = 0;
        break;
      }
      case 4: {
        if (FT != Interface_ParamEnum)
        {
          kod = 0;
          break;
        }
        Handle(StepData_SelectNamed) sn = new StepData_SelectNamed;
        sn->SetEnum(-1, str);
        htr->SetValue(ip, sn);
        break;
      }
      case 5: {
        if (FT != Interface_ParamReal)
        {
          kod = 0;
          break;
        }
        hre->SetValue(ip, Interface_FileReaderData::Fastof(str));
        break;
      }
      case 6: {
        if (FT != Interface_ParamText)
        {
          kod = 0;
          break;
        }
        Handle(TCollection_HAsciiString) txt = new TCollection_HAsciiString(str);
        cleanText(txt);
        hst->SetValue(ip, txt);
        break;
      }
      case 7: {
        Handle(Standard_Transient) ent = BoundEntity(FP.EntityNumber());
        htr->SetValue(ip, ent);
        break;
      }
      default:
        break;
    }
    //    Restent les autres cas ... tout est possible. cf le type du Param
    if (kod > 0)
      continue;
    //    Il faut passer au transient ...
    if (htr.IsNull())
    {
      htr = new TColStd_HArray1OfTransient(1, nbp);
      val = htr;
      Standard_Integer jp;
      if (!hin.IsNull())
      {
        for (jp = 1; jp < ip; jp++)
        {
          Handle(StepData_SelectInt) sin = new StepData_SelectInt;
          sin->SetInt(hin->Value(jp));
          htr->SetValue(jp, sin);
        }
      }
      if (!hre.IsNull())
      {
        for (jp = 1; jp < ip; jp++)
        {
          Handle(StepData_SelectReal) sre = new StepData_SelectReal;
          sre->SetReal(hre->Value(jp));
          htr->SetValue(jp, sre);
        }
      }
      if (!hst.IsNull())
      {
        for (jp = 1; jp < ip; jp++)
        {
          htr->SetValue(jp, hst->Value(jp));
        }
      }
    }
    //    A present, faut y aller : lire le champ et le mettre en place
    //    Ce qui suit ressemble fortement a ReadAny ...

    switch (FT)
    {
      case Interface_ParamMisc:
        break;
      case Interface_ParamInteger: {
        Handle(StepData_SelectInt) sin = new StepData_SelectInt;
        sin->SetInteger(atoi(str));
        htr->SetValue(ip, sin);
        break;
      }
      case Interface_ParamReal: {
        Handle(StepData_SelectReal) sre = new StepData_SelectReal;
        sre->SetReal(Interface_FileReaderData::Fastof(str));
        break;
        // htr->SetValue (ip,sre); break; svv #2: unreachable
      }
      case Interface_ParamIdent:
        htr->SetValue(ip, BoundEntity(FP.EntityNumber()));
        break;
      case Interface_ParamVoid:
        break;
      case Interface_ParamEnum: {
        Handle(StepData_SelectInt)   sin;
        Handle(StepData_SelectNamed) sna;
        Standard_Integer             logic = -1;
        // PTV 16.09.2000
        // set the default value of StepData_Logical
        StepData_Logical slog = StepData_LUnknown;
        if (str[0] == '.' && str[2] == '.' && str[3] == '\0')
        {
          if (str[1] == 'F')
          {
            slog  = StepData_LFalse;
            logic = 0;
          }
          else if (str[1] == 'T')
          {
            slog  = StepData_LTrue;
            logic = 1;
          }
          else if (str[1] == 'U')
          {
            slog  = StepData_LUnknown;
            logic = 2;
          }
        }
        if (logic >= 0)
        {
          sin = new StepData_SelectInt;
          sin->SetLogical(slog);
          htr->SetValue(ip, sin);
        }
        else
        {
          sna = new StepData_SelectNamed;
          sna->SetEnum(logic, str);
          htr->SetValue(ip, sna);
        }
        break;
      }
      case Interface_ParamLogical:
        break;
      case Interface_ParamText: {
        Handle(TCollection_HAsciiString) txt = new TCollection_HAsciiString(str);
        cleanText(txt);
        htr->SetValue(ip, txt);
        break;
      }
      case Interface_ParamSub: {
        Handle(Standard_Transient) sub;
        Standard_Integer           nent = FP.EntityNumber();
        Standard_Integer           kind = ReadSub(nent, mess, ach, descr, sub);
        if (kind < 0)
          break;
        htr->SetValue(ip, sub);
        break;
      }
      case Interface_ParamHexa:
        break;
      case Interface_ParamBinary:
        break;
      default:
        break;
    }
    return -1;
  }
  return 8; // pour Any
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadMember(const Standard_Integer         num,
                                                     const Standard_Integer         nump,
                                                     const Standard_CString         mess,
                                                     Handle(Interface_Check)&       ach,
                                                     Handle(StepData_SelectMember)& val) const
{
  Handle(Standard_Transient) v = val;
  Handle(StepData_PDescr)    nuldescr;
  if (v.IsNull())
  {
    return ReadAny(num, nump, mess, ach, nuldescr, v)
           && !(val = Handle(StepData_SelectMember)::DownCast(v)).IsNull();
  }
  Standard_Boolean res = ReadAny(num, nump, mess, ach, nuldescr, v);
  if (v == val)
    return res;
  //   changement -> refus
  Handle(String) errmess = new String("Parameter n0.%d (%s) : does not match SELECT clause");
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadField(const Standard_Integer         num,
                                                    const Standard_Integer         nump,
                                                    const Standard_CString         mess,
                                                    Handle(Interface_Check)&       ach,
                                                    const Handle(StepData_PDescr)& descr,
                                                    StepData_Field&                fild) const
{
  const Interface_FileParameter&   FP  = Param(num, nump);
  Standard_CString                 str = FP.CValue();
  Standard_Boolean                 OK  = Standard_True;
  Standard_Integer                 nent, kind;
  Handle(TCollection_HAsciiString) txt;
  Handle(Standard_Transient)       sub;
  Interface_ParamType              FT = FP.ParamType();
  switch (FT)
  {
    case Interface_ParamMisc:
      OK = Standard_False;
      break;
    case Interface_ParamInteger:
      fild.SetInteger(atoi(str));
      break;
    case Interface_ParamReal:
      fild.SetReal(Interface_FileReaderData::Fastof(str));
      break;
    case Interface_ParamIdent:
      nent = FP.EntityNumber();
      if (nent > 0)
        fild.SetEntity(BoundEntity(nent));
      break;
    case Interface_ParamVoid:
      break;
    case Interface_ParamText:
      txt = new TCollection_HAsciiString(str);
      cleanText(txt);
      fild.Set(txt);
      break;
    case Interface_ParamEnum:
      if (!strcmp(str, ".T."))
        fild.SetLogical(StepData_LTrue);
      else if (!strcmp(str, ".F."))
        fild.SetLogical(StepData_LFalse);
      else if (!strcmp(str, ".U."))
        fild.SetLogical(StepData_LUnknown);
      else
        fild.SetEnum(-1, str);
      break;
    case Interface_ParamLogical:
      OK = Standard_False;
      break;
    case Interface_ParamSub:
      nent = FP.EntityNumber();
      kind = ReadSub(nent, mess, ach, descr, sub);
      if (kind < 0)
        break;
      fild.Clear(kind);
      fild.Set(sub);
      break;
    case Interface_ParamHexa:
      OK = Standard_False;
      break;
    case Interface_ParamBinary:
      OK = Standard_False;
      break;
    default:
      OK = Standard_False;
      break;
  }

  if (!OK)
  {
    if (!strcmp(str, "*"))
      fild.SetDerived();
  }
  return Standard_True;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadList(const Standard_Integer          num,
                                                   Handle(Interface_Check)&        ach,
                                                   const Handle(StepData_ESDescr)& descr,
                                                   StepData_FieldList&             list) const
{
  // controler nbs egaux
  Standard_Integer i, nb = list.NbFields();
  if (!CheckNbParams(num, nb, ach, descr->TypeName()))
    return Standard_False;
  for (i = 1; i <= nb; i++)
  {
    Handle(StepData_PDescr) pde  = descr->Field(i);
    StepData_Field&         fild = list.CField(i);
    ReadField(num, i, pde->Name(), ach, pde, fild);
  }
  return Standard_True;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadAny(const Standard_Integer         num,
                                                  const Standard_Integer         nump,
                                                  const Standard_CString         mess,
                                                  Handle(Interface_Check)&       ach,
                                                  const Handle(StepData_PDescr)& descr,
                                                  Handle(Standard_Transient)&    val) const
{
  const Interface_FileParameter& FP  = Param(num, nump);
  Standard_CString               str = FP.CValue();
  Interface_ParamType            FT  = FP.ParamType();

  //    A present, faut y aller : lire le champ et le mettre en place
  switch (FT)
  {
    case Interface_ParamMisc:
      break;
    case Interface_ParamInteger: {
      if (!val.IsNull())
      {
        DeclareAndCast(StepData_SelectMember, sm, val);
        sm->SetReal(Interface_FileReaderData::Fastof(str));
        return Standard_True;
      }
      Handle(StepData_SelectInt) sin = new StepData_SelectInt;
      sin->SetInteger(atoi(str));
      val = sin;
      return Standard_True;
    }
    case Interface_ParamReal: {
      if (!val.IsNull())
      {
        DeclareAndCast(StepData_SelectMember, sm, val);
        sm->SetReal(Interface_FileReaderData::Fastof(str));
        return Standard_True;
      }
      Handle(StepData_SelectReal) sre = new StepData_SelectReal;
      sre->SetReal(Interface_FileReaderData::Fastof(str));
      val = sre;
      return Standard_True;
    }
    case Interface_ParamIdent: {
      Standard_Integer nent = FP.EntityNumber();
      if (nent > 0)
        val = BoundEntity(nent);
      return (!val.IsNull());
    }
    case Interface_ParamVoid:
      break;
    case Interface_ParamEnum: {
      Handle(StepData_SelectMember) sm;
      if (!val.IsNull())
        sm = GetCasted(StepData_SelectMember, val);
      Handle(StepData_SelectInt)   sin;
      Handle(StepData_SelectNamed) sna;
      Standard_Integer             logic = -1;

      // PTV 16.09.2000
      // set the default value of StepData_Logical
      StepData_Logical slog = StepData_LUnknown;
      if (str[0] == '.' && str[2] == '.' && str[3] == '\0')
      {
        if (str[1] == 'F')
        {
          slog  = StepData_LFalse;
          logic = 0;
        }
        else if (str[1] == 'T')
        {
          slog  = StepData_LTrue;
          logic = 1;
        }
        else if (str[1] == 'U')
        {
          slog  = StepData_LUnknown;
          logic = 2;
        }
      }
      if (logic >= 0)
      {
        if (!sm.IsNull())
          sm->SetLogical(slog);
        else
        {
          sin = new StepData_SelectInt;
          val = sin;
          sin->SetLogical(slog);
        }
      }
      else
      {
        if (!sm.IsNull())
          sm->SetEnum(logic, str);
        else
        {
          sna = new StepData_SelectNamed;
          val = sna; // Named sans nom...
          sna->SetEnum(logic, str);
        }
      } // -> Select general
      return Standard_True;
    }
    case Interface_ParamLogical:
      break;
    case Interface_ParamText: {
      Handle(TCollection_HAsciiString) txt = new TCollection_HAsciiString(str);
      cleanText(txt);

      // PDN May 2000: for reading SOURCE_ITEM (external references)
      if (!val.IsNull())
      {
        DeclareAndCast(StepData_SelectMember, sm, val);
        sm->SetString(txt->ToCString());
        return Standard_True;
      }

      val = txt;
      return Standard_True;
    }
    case Interface_ParamSub: {
      Standard_Integer numsub = SubListNumber(num, nump, Standard_False);
      Standard_Integer nbp    = NbParams(numsub);
      if (nbp == 0)
        return Standard_False; // liste vide = Handle Null
      const TCollection_AsciiString& rectyp = RecordType(numsub);
      if (nbp == 1 && rectyp.ToCString()[0] != '(')
      {
        //  SelectNamed because Field !!!
        // skl 15.01.2003 (for members with array of real)
        DeclareAndCast(StepData_SelectArrReal, sma, val);
        if (!sma.IsNull())
        {
          Standard_Integer numsub2 = SubListNumber(numsub, 1, Standard_False);
          Standard_Integer nbp2    = NbParams(numsub2);
          if (nbp2 > 1)
          {
            if (Param(numsub2, 1).ParamType() == Interface_ParamReal)
            {
              if (!sma->SetName(rectyp.ToCString()))
                return Standard_False;
              Handle(TColStd_HSequenceOfReal) aSeq = new TColStd_HSequenceOfReal;
              for (Standard_Integer i = 1; i <= nbp2; i++)
              {
                if (Param(numsub2, i).ParamType() != Interface_ParamReal)
                  continue;
                Handle(Standard_Transient) asr = new StepData_SelectReal;
                if (!ReadAny(numsub2, i, mess, ach, descr, asr))
                  continue;
                Handle(StepData_SelectReal) sm1 = Handle(StepData_SelectReal)::DownCast(asr);
                if (!sm1.IsNull())
                  aSeq->Append(sm1->Real());
              }
              Handle(TColStd_HArray1OfReal) anArr = new TColStd_HArray1OfReal(1, aSeq->Length());
              for (Standard_Integer nr = 1; nr <= aSeq->Length(); nr++)
              {
                anArr->SetValue(nr, aSeq->Value(nr));
              }
              sma->SetArrReal(anArr);
              return Standard_True;
            }
          }
        }
        DeclareAndCast(StepData_SelectMember, sm, val);
        if (sm.IsNull())
        {
          sm  = new StepData_SelectNamed;
          val = sm;
        }
        if (!sm->SetName(rectyp.ToCString()))
          return Standard_False; // loupe
        return ReadAny(numsub, 1, mess, ach, descr, val);
      }
    }
    default:
      break;
  }
  return Standard_False;
}

//  ....

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadXY(const Standard_Integer   num,
                                                 const Standard_Integer   nump,
                                                 const Standard_CString   mess,
                                                 Handle(Interface_Check)& ach,
                                                 Standard_Real&           X,
                                                 Standard_Real&           Y) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Integer numsub = SubListNumber(num, nump, Standard_False);
  if (numsub != 0)
  {
    if (NbParams(numsub) == 2)
    {
      const Interface_FileParameter& FPX = Param(numsub, 1);
      if (FPX.ParamType() == Interface_ParamReal)
        X = Interface_FileReaderData::Fastof(FPX.CValue());
      else
        errmess = new String("Parameter n0.%d (%s) : (X,Y) X not a Real");

      const Interface_FileParameter& FPY = Param(numsub, 2);
      if (FPY.ParamType() == Interface_ParamReal)
        Y = Interface_FileReaderData::Fastof(FPY.CValue());
      else
        errmess = new String("Parameter n0.%d (%s) : (X,Y) Y not a Real");
    }
    else
      errmess = new String("Parameter n0.%d (%s) : (X,Y) has not 2 params");
  }
  else
    errmess = new String("Parameter n0.%d (%s) : (X,Y) not a SubList");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadXYZ(const Standard_Integer   num,
                                                  const Standard_Integer   nump,
                                                  const Standard_CString   mess,
                                                  Handle(Interface_Check)& ach,
                                                  Standard_Real&           X,
                                                  Standard_Real&           Y,
                                                  Standard_Real&           Z) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Integer numsub = SubListNumber(num, nump, Standard_False);
  if (numsub != 0)
  {
    if (NbParams(numsub) == 3)
    {
      const Interface_FileParameter& FPX = Param(numsub, 1);
      if (FPX.ParamType() == Interface_ParamReal)
        X = Interface_FileReaderData::Fastof(FPX.CValue());
      else
        errmess = new String("Parameter n0.%d (%s) : (X,Y,Z) X not a Real");

      const Interface_FileParameter& FPY = Param(numsub, 2);
      if (FPY.ParamType() == Interface_ParamReal)
        Y = Interface_FileReaderData::Fastof(FPY.CValue());
      else
        errmess = new String("Parameter n0.%d (%s) : (X,Y,Z) Y not a Real");

      const Interface_FileParameter& FPZ = Param(numsub, 3);
      if (FPZ.ParamType() == Interface_ParamReal)
        Z = Interface_FileReaderData::Fastof(FPZ.CValue());
      else
        errmess = new String("Parameter n0.%d (%s) : (X,Y,Z) Z not a Real");
    }
    else
      errmess = new String("Parameter n0.%d (%s) : (X,Y,Z) has not 3 params");
  }
  else
    errmess = new String("Parameter n0.%d (%s) : (X,Y,Z) not a SubList");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadReal(const Standard_Integer   num,
                                                   const Standard_Integer   nump,
                                                   const Standard_CString   mess,
                                                   Handle(Interface_Check)& ach,
                                                   Standard_Real&           val) const
{
  Handle(String) errmess; // Null si pas d erreur
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamReal || FP.ParamType() == Interface_ParamInteger)
      val = Interface_FileReaderData::Fastof(FP.CValue());
    else
      errmess = new String("Parameter n0.%d (%s) not a Real");
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadEntity(const Standard_Integer       num,
                                                     const Standard_Integer       nump,
                                                     const Standard_CString       mess,
                                                     Handle(Interface_Check)&     ach,
                                                     const Handle(Standard_Type)& atype,
                                                     Handle(Standard_Transient)&  ent) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = Standard_False;
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP   = Param(num, nump);
    Standard_Integer               nent = FP.EntityNumber();
    if (FP.ParamType() == Interface_ParamIdent)
    {
      warn = (acceptvoid > 0);
      if (nent > 0)
      {
        Handle(Standard_Transient) entent = BoundEntity(nent);
        if (entent.IsNull() || !entent->IsKind(atype))
        {
          errmess = new String("Parameter n0.%d (%s) : Entity has illegal type");
          if (!entent.IsNull() && entent->IsKind(STANDARD_TYPE(StepData_UndefinedEntity)))
            ent = entent;
        }
        else
          ent = entent;
      }
      else
        errmess = new String("Parameter n0.%d (%s) : Unresolved reference");
    }
    else
    {
      if (acceptvoid && FP.ParamType() == Interface_ParamVoid)
        warn = Standard_True;
      errmess = new String("Parameter n0.%d (%s) not an Entity");
    }
  }
  else
  {
    warn    = (acceptvoid > 0);
    errmess = new String("Parameter n0.%d (%s) absent");
  }

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadEntity(const Standard_Integer   num,
                                                     const Standard_Integer   nump,
                                                     const Standard_CString   mess,
                                                     Handle(Interface_Check)& ach,
                                                     StepData_SelectType&     sel) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = Standard_False;
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP   = Param(num, nump);
    Standard_Integer               nent = FP.EntityNumber();
    if (FP.ParamType() == Interface_ParamIdent)
    {
      warn = (acceptvoid > 0);
      if (nent > 0)
      {
        Handle(Standard_Transient) entent = BoundEntity(nent);
        if (!sel.Matches(entent))
        {
          errmess = new String("Parameter n0.%d (%s) : Entity has illegal type");
          // fot not supported STEP entity
          if (!entent.IsNull() && entent->IsKind(STANDARD_TYPE(StepData_UndefinedEntity)))
            sel.SetValue(entent);
        }
        else
          sel.SetValue(entent);
      }
      else
        errmess = new String("Parameter n0.%d (%s) : Unresolved reference");
    }
    else if (FP.ParamType() == Interface_ParamVoid)
    {
      if (acceptvoid)
        warn = Standard_True;
      errmess = new String("Parameter n0.%d (%s) not an Entity");
    }
    else
    {
      // Cas restant : on s interesse en fait au SelectMember ...
      Handle(Standard_Transient) sm = sel.NewMember();
      // SelectMember qui assure ce role. Peut etre specialise
      if (!ReadAny(num, nump, mess, ach, sel.Description(), sm))
        errmess = new String("Parameter n0.%d (%s) : could not be read");
      if (!sel.Matches(sm))
        errmess = new String("Parameter n0.%d (%s) : illegal parameter type");
      else
        sel.SetValue(sm);
    }
  }
  else
  {
    warn    = (acceptvoid > 0);
    errmess = new String("Parameter n0.%d (%s) absent");
  }

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadInteger(const Standard_Integer   num,
                                                      const Standard_Integer   nump,
                                                      const Standard_CString   mess,
                                                      Handle(Interface_Check)& ach,
                                                      Standard_Integer&        val) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = Standard_False;
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamInteger)
      val = atoi(FP.CValue());
    else if (FP.ParamType() == Interface_ParamReal)
    {
      val =
        static_cast<Standard_Integer>(std::round(Interface_FileReaderData::Fastof(FP.CValue())));
      if (acceptvoid)
        warn = Standard_True;
      errmess = new String("Parameter n0.%d (%s) was rounded");
    }
    if (FP.ParamType() != Interface_ParamInteger && FP.ParamType() != Interface_ParamReal)
      errmess = new String("Parameter n0.%d (%s) not an Integer");
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadBoolean(const Standard_Integer   num,
                                                      const Standard_Integer   nump,
                                                      const Standard_CString   mess,
                                                      Handle(Interface_Check)& ach,
                                                      Standard_Boolean&        flag) const
{
  flag = Standard_True;
  Handle(String) errmess; // Null si pas d erreur
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamEnum)
    {
      Standard_CString txt = FP.CValue();
      if (!strcmp(txt, ".T."))
        flag = Standard_True;
      else if (!strcmp(txt, ".F."))
        flag = Standard_False;
      else
        errmess = new String("Parameter n0.%d (%s) : Incorrect Boolean Value. It was set to true");
    }
    else
      errmess = new String("Parameter n0.%d (%s) not a Boolean. It was set to true");
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent.It was set to true");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadLogical(const Standard_Integer   num,
                                                      const Standard_Integer   nump,
                                                      const Standard_CString   mess,
                                                      Handle(Interface_Check)& ach,
                                                      StepData_Logical&        flag) const
{
  Handle(String) errmess; // Null si pas d erreur
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamEnum)
    {
      Standard_CString txt = FP.CValue();
      if (!strcmp(txt, ".T."))
        flag = StepData_LTrue;
      else if (!strcmp(txt, ".F."))
        flag = StepData_LFalse;
      else if (!strcmp(txt, ".U."))
        flag = StepData_LUnknown;
      else
        errmess = new String("Parameter n0.%d (%s) : Incorrect Logical Value");
    }
    else
      errmess = new String("Parameter n0.%d (%s) not a Logical");
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadString(const Standard_Integer            num,
                                                     const Standard_Integer            nump,
                                                     const Standard_CString            mess,
                                                     Handle(Interface_Check)&          ach,
                                                     Handle(TCollection_HAsciiString)& val) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = Standard_False;
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamText)
    {
      /*Standard_CString anStr = FP.CValue();
      if(strlen(anStr) < 3)
        val = new TCollection_HAsciiString("");
      else {
        val = new TCollection_HAsciiString(FP.CValue());
        CleanText (val);
      }*/
      val = new TCollection_HAsciiString(FP.CValue());
      cleanText(val);
    }
    else
    {
      if (acceptvoid && FP.ParamType() == Interface_ParamVoid)
        warn = Standard_True;
      errmess = new String("Parameter n0.%d (%s) not a quoted String");
    }
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadEnumParam(const Standard_Integer   num,
                                                        const Standard_Integer   nump,
                                                        const Standard_CString   mess,
                                                        Handle(Interface_Check)& ach,
                                                        Standard_CString&        text) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = Standard_False;
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamEnum)
    {
      text = FP.CValue();
      warn = (acceptvoid > 0);
    }
    else if (FP.ParamType() == Interface_ParamVoid)
    {
      errmess = new String("Parameter n0.%d (%s) : Undefined Enumeration not allowed");
      warn    = (acceptvoid > 0);
    }
    else
      errmess = new String("Parameter n0.%d (%s) not an Enumeration");
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

void StepData_StepReaderData::FailEnumValue(const Standard_Integer /* num */,
                                            const Standard_Integer   nump,
                                            const Standard_CString   mess,
                                            Handle(Interface_Check)& ach) const
{
  Handle(String) errmess = new String("Parameter n0.%d (%s) : Incorrect Enumeration Value");
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadEnum(const Standard_Integer   num,
                                                   const Standard_Integer   nump,
                                                   const Standard_CString   mess,
                                                   Handle(Interface_Check)& ach,
                                                   const StepData_EnumTool& enumtool,
                                                   Standard_Integer&        val) const
{
  //  reprendre avec ReadEnumParam ?
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = Standard_False;
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() == Interface_ParamEnum)
    {
      val = enumtool.Value(FP.CValue());
      if (val >= 0)
        return Standard_True;
      else
        errmess = new String("Parameter n0.%d (%s) : Incorrect Enumeration Value");
      warn = (acceptvoid > 0);
    }
    else if (FP.ParamType() == Interface_ParamVoid)
    {
      val = enumtool.NullValue();
      if (val < 0)
        errmess = new String("Parameter n0.%d (%s) : Undefined Enumeration not allowed");
      warn = (acceptvoid > 0);
    }
    else
      errmess = new String("Parameter n0.%d (%s) not an Enumeration");
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::ReadTypedParam(const Standard_Integer   num,
                                                         const Standard_Integer   nump,
                                                         const Standard_Boolean   mustbetyped,
                                                         const Standard_CString   mess,
                                                         Handle(Interface_Check)& ach,
                                                         Standard_Integer&        numr,
                                                         Standard_Integer&        numrp,
                                                         TCollection_AsciiString& typ) const
{
  Handle(String) errmess; // Null si pas d erreur
  if (nump > 0 && nump <= NbParams(num))
  {
    const Interface_FileParameter& FP = Param(num, nump);
    if (FP.ParamType() != Interface_ParamSub)
    {
      //    Pas une sous-liste : OK si admis
      numr  = num;
      numrp = nump;
      typ.Clear();
      if (mustbetyped)
      {
        errmess = new String("Parameter n0.%d (%s) : single, not typed");
        sprintf(txtmes, errmess->ToCString(), nump, mess);
        ach->AddFail(txtmes, errmess->ToCString());
        return Standard_False;
      }
      return Standard_True;
    }
    numr  = FP.EntityNumber();
    numrp = 1;
    if (NbParams(numr) != 1)
      errmess = new String("Parameter n0.%d (%s) : SubList, not typed");
    typ = RecordType(numr);
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//=================================================================================================

Standard_Boolean StepData_StepReaderData::CheckDerived(const Standard_Integer   num,
                                                       const Standard_Integer   nump,
                                                       const Standard_CString   mess,
                                                       Handle(Interface_Check)& ach,
                                                       const Standard_Boolean   errstat) const
{
  Handle(String)   errmess; // Null si pas d erreur
  Standard_Boolean warn = !errstat;
  if (nump > 0 && nump <= NbParams(num))
  {
    if (!strcmp(Param(num, nump).CValue(), "*"))
      return Standard_True;
    else
      errmess = new String("Parameter n0.%d (%s) not Derived");
    if (acceptvoid)
      warn = Standard_True;
  }
  else
    errmess = new String("Parameter n0.%d (%s) absent");

  if (errmess.IsNull())
    return Standard_True;
  sprintf(txtmes, errmess->ToCString(), nump, mess);
  if (warn)
    ach->AddWarning(txtmes, errmess->ToCString());
  else
    ach->AddFail(txtmes, errmess->ToCString());
  return Standard_False;
}

//  #########################################################################
// ....     Methodes specifiques (demandees par FileReaderData)     .... //

//=================================================================================================

Standard_Integer StepData_StepReaderData::NbEntities() const // redefined
{
  return thenbents;
}

//=================================================================================================

Standard_Integer StepData_StepReaderData::FindNextRecord(const Standard_Integer num) const
{
  // retourne, sur un numero d enregistrement donne (par num), le suivant qui
  // definit une entite, ou 0 si c est fini :
  // passe le Header (nbhend premiers records) et
  // saute les enregistrements SCOPE et ENDSCOPE et les SOUS-LISTES

  if (num < 0)
    return 0;
  Standard_Integer num1 = num + 1;
  if (num == 0)
    num1 = thenbhead + 1;
  Standard_Integer max = NbRecords();

  while (num1 <= max)
  {
    if (theidents(num1) > 0)
      return num1;

    // SCOPE,ENDSCOPE et Sous-Liste ont un identifieur fictif: -1,-2 respectivement
    // et SUBLIST ont un negatif. Seule une vraie entite a un Ident positif
    num1++;
  }
  return 0;
}

//=================================================================================================

Standard_Integer StepData_StepReaderData::FindEntityNumber(const Standard_Integer num,
                                                           const Standard_Integer id) const
{
  //  Soit un "Id" : recherche dans les Parametres de type Ident de <num>,
  //  si un d eux designe #Id justement. Si oui, retourne son EntityNumber
  if (num == 0)
    return 0;
  Standard_Integer nb = NbParams(num);
  for (Standard_Integer i = 1; i <= nb; i++)
  {
    const Interface_FileParameter& FP = Param(num, i);
    if (FP.ParamType() != Interface_ParamIdent)
      continue;
    Standard_Integer ixp = atoi(&FP.CValue()[1]);
    if (ixp == id)
      return FP.EntityNumber();
  }
  return 0; // ici, pas trouve
}

//  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##
// ....         La fonction qui suit merite une attention speciale        ....

//  Cette methode precharge les EntityNumbers dans les Params : ils designent
//  les Entites proprement dites dans la liste lue par BoundEntity
//  Interet : adresse de meme les sous-listes (Num->no record dans le Direc)
//  resultat exploite par ParamEntity et ParamNumber

//  En l absence de SCOPE, ou si les "ident" sont strictement ordonnes, a coup
//  sur ils ne sont pas dupliques, on peut utiliser une IndexedMap en toute
//  confiance. Sinon, il faut balayer dans le fichier, mais avec les SCOPES
//  cela va beaucoup plus vite (s ils sont assez gros) : on s y retrouve.

// Pour la recherche par balayage, On opere en plusieurs etapes
// Avant toute chose, le chargement a deja fait une preparation : les idents
// (Entity, SubList) sont deja en entiers (rapidite de lecture), en particulier
// dans les EntityNumber : ainsi, on lit cet ident, on le traite, et on remet
// a la place un vrai numero de Record
//
// D abord, on passe le directory en table d entiers,  sous-listes expurgees
// en // , table inverse vers cette table, car les sous-listes peuvent par
// contre designer des objets ...

// Pour les sous-listes, on exploite leur mode de construction : elles sont
// enregistrees AVANT d etre referencees. Un tableau "subn" note donc pour
// chaque numero de sous-liste (relatif a une entite qui suit, et reference
// par elle ou une autre sous-liste qui suit egalement), son n0 de record
// REMARQUE : ceci marche aussi pour le Header, traite par l occasion

//=================================================================================================

void StepData_StepReaderData::SetEntityNumbers(const Standard_Boolean withmap)
{
  Message_Messenger::StreamBuffer sout = Message::SendTrace();
  //   Passe initiale : Resolution directe par Map
  //   si tout passe (pas de collision), OK. Sinon, autres passes a prevoir
  //   On resoud du meme coup les sous-listes
  Standard_Integer                 nbdirec = NbRecords();
  Handle(NCollection_IncAllocator) anAlloc =
    new NCollection_IncAllocator(NCollection_IncAllocator::THE_MINIMUM_BLOCK_SIZE);
  TColStd_Array1OfInteger subn(0, thelastn);

  Standard_Boolean            pbmap = Standard_False; // au moins un conflit
  Standard_Integer            nbmap = 0;
  TColStd_IndexedMapOfInteger imap(thenbents, anAlloc);
  TColStd_Array1OfInteger     indm(0, nbdirec); // Index Map -> Record Number (seulement si map)

  Standard_Integer num; // svv Jan11 2000 : porting on DEC
  for (num = 1; num <= nbdirec; num++)
  {
    Standard_Integer ident = theidents(num);
    if (ident > 0)
    { // Ident normal -> Map ?
      //  Map : si Recouvrement, l inhiber. Sinon, noter index
      Standard_Integer indmap = imap.Add(ident);
      if (indmap <= nbmap)
      {
        indmap       = imap.FindIndex(ident); // plus sur
        indm(indmap) = -1;                    // Map -> pb
        pbmap        = Standard_True;
        //  pbmap signifie qu une autre passe sera necessaire ...
      }
      else
      {
        nbmap        = indmap;
        indm(indmap) = num; // Map ->ident
      }
    }
  }

  for (num = 1; num <= nbdirec; num++)
  {
    Standard_Integer ident = theidents(num);
    if (ident < -2)
      subn(-(ident + 2)) = num; // toujours a jour ...

    Standard_Integer nba = NbParams(num);
    Standard_Integer nda = (num == 1 ? 0 : ParamFirstRank(num - 1));

    for (Standard_Integer na = nba; na > 0; na--)
    {
      //    On traite : les sous-listes (sf subn), les idents (si Map dit OK ...)
      Interface_FileParameter& FP = ChangeParameter(nda + na);
      //      Interface_FileParameter& FP = ChangeParam (num,na);
      Interface_ParamType letype = FP.ParamType();
      if (letype == Interface_ParamSub)
      {
        Standard_Integer numsub = FP.EntityNumber();
        if (numsub > thelastn)
        {
          Message::SendInfo() << "Bad Sub.N0, Record " << num << " Param " << na << ":$" << numsub
                              << std::endl;
          continue;
        }
        FP.SetEntityNumber(subn(numsub));
      }
      else if (letype == Interface_ParamIdent)
      {
        Standard_Integer id     = FP.EntityNumber();
        Standard_Integer indmap = imap.FindIndex(id);
        if (indmap > 0)
        { // la map a trouve
          Standard_Integer num0 = indm(indmap);
          if (num0 > 0)
            FP.SetEntityNumber(num0); // ET VOILA, on a resolu
          else
            FP.SetEntityNumber(-id); // CONFLIT -> faudra resoudre ...
        }
        else
        { // NON RESOLU, si pas pbmap, le dire
          if (pbmap)
          {
            FP.SetEntityNumber(-id);
            continue; // pbmap : on se retrouvera
          }
          char failmess[100];
          //  ...  Construire le Check  ...
          sprintf(failmess, "Unresolved Reference, Ent.Id.#%d Param.n0 %d (Id.#%d)", ident, na, id);
          thecheck->AddFail(failmess, "Unresolved Reference");
          //  ...  Et sortir message un peu plus complet
          sout << "*** ERR StepReaderData *** Entite #" << ident << "\n    Type:" << RecordType(num)
               << "  Param.n0 " << na << ": #" << id << " Not found" << std::endl;
        } // FIN  Mapping
      } // FIN  Traitement Reference
    } // FIN  Boucle Parametres
  } // FIN  Boucle Repertoires

  if (!pbmap)
  {
    return;
  }
  sout << " --  2nd pass required --";

  Standard_Integer                 nbseq = thenbents + 2 * thenbscop;
  TColStd_Array1OfInteger          inds(0, nbseq);   // n0 Record/Entite
  TColStd_Array1OfInteger          indi(0, nbseq);   // Idents/scopes
  TColStd_Array1OfInteger          indr(0, nbdirec); // inverse de nds
  Handle(TColStd_HArray1OfInteger) indx;             // pour EXPORT (silya)

  imap.Clear();
  anAlloc->Reset();
  Standard_Boolean iamap = withmap; // (par defaut True)
  nbmap                  = 0;

  TColStd_SequenceOfInteger scopile(anAlloc); // chainage des scopes note par pile
  Standard_Integer          nr = 0;
  for (num = 1; num <= nbdirec; num++)
  {
    Standard_Integer ident = theidents(num);
    if (ident < -2)
    {                     // SOUS-LISTE (cas le plus courant)
      indr(num) = nr + 1; // recherche basee sur nr (objet qui suit)
    }
    else if (ident >= 0)
    { // Ident normal
      nr++;
      inds(nr)  = num;
      indi(nr)  = ident;
      indr(num) = nr;
      if (ident > 0)
      { // et non (iamap && ident > 0)
        //  Map : si Recouvrement, l inhiber. Sinon, noter index
        Standard_Integer indmap = imap.Add(ident);
        if (indmap <= nbmap)
        {
          Standard_Boolean errorscope = Standard_False;
          indmap                      = imap.FindIndex(ident); // plus sur
          pbmap                       = Standard_True;
          if (thenbscop == 0)
            errorscope = Standard_True;
          //  Numeros identiques alors quilnya pas de SCOPE ? ERREUR !
          //  (Bien sur, silya des SCOPES, on passe au travers, mais bon...)
          else
          {
            //  Silya des SCOPES, tachons d y voir de plus pres pour signaler un probleme
            //  Erreur si MEME groupe SCOPE
            //  ATTENTION, on recherche, non dans tous les records, mais dans les records
            //    CHAINES, cf nr et non num (pas de sous-liste, chainage scope-endscope)
            Standard_Integer fromscope = nr;
            Standard_Integer toscope   = indm(indmap);
            if (toscope < 0)
              toscope = -toscope;
            for (;;)
            {
              fromscope--; // iteration de base
              if (fromscope <= toscope)
              {
                errorscope = Standard_True; // BANG, on est dessus
                break;
              }
              Standard_Integer idtest = indi(fromscope);
              if (idtest >= 0)
                continue; // le suivant (enfin, le precedent)
              if (idtest == -1)
                break; // pas meme niveau, donc c est OK
              if (idtest == -3)
              {
                fromscope = inds(fromscope);
                if (fromscope < toscope)
                  break; // on sort, pas en meme niveau
              }
            }
          }
          if (errorscope)
          {
            //  On est dedans : le signaler
            char ligne[80];
            sprintf(ligne, "Ident defined SEVERAL TIMES : #%d", ident);
            thecheck->AddFail(ligne, "Ident defined SEVERAL TIMES : #%d");
            sout << "StepReaderData : SetEntityNumbers, " << ligne << std::endl;
          }
          if (indm(indmap) > 0)
            indm(indmap) = -indm(indmap); // Pas pour Map
                                          //  Cas Normal pour la Map
        }
        else
        {
          nbmap        = indmap;
          indm(indmap) = nr; // Map ->(indm)->inds
        }
      }
    }
    else if (ident == -1)
    { // SCOPE
      nr++;
      inds(nr)  = num;
      indi(nr)  = -1;
      indr(num) = 0;
      scopile.Append(nr);
    }
    else if (ident == -2)
    {                                          // ENDSCOPE
      Standard_Integer nscop = scopile.Last(); // chainage SCOPE-ENDSCOPE
      scopile.Remove(scopile.Length());
      nr++;
      inds(nr)    = nscop;
      indi(nr)    = -3;
      indr(num)   = 0;
      inds(nscop) = nr;
      if (NbParams(num) > 0)
      {
        //  EXPORT : traitement special greffe sur celui de SCOPE (sans le perturber)
        if (indx.IsNull())
        {
          indx = new TColStd_HArray1OfInteger(0, nbseq);
          for (Standard_Integer ixp = 0; ixp <= nbseq; ixp++)
            indx->ChangeValue(ixp) = 0;
        }
        indx->ChangeValue(nr)    = num;
        indx->ChangeValue(nscop) = num;
      }
    }
    else if (ident == 0)
    { // HEADER
      indr(num) = 0;
    }
  }

  //  ..    Resolution des EXPORT, silyena et silya besoin    ..
  //  Pour chaque valeur de EXPORT qui n a pas ete resolue par la MAP,
  //  determiner sa position locale par recherche en arriere depuis ENDSCOPE
  if ((!iamap || pbmap) && !indx.IsNull())
  {
    for (nr = 0; nr <= nbseq; nr++)
    {
      if (indx->Value(nr) == 0 && indi(nr) != -3)
        continue; // ENDSCOPE + EXPORT
      num                  = indx->Value(nr);
      Standard_Integer nba = NbParams(num);
      for (Standard_Integer na = 1; na <= nba; na++)
      {
        Interface_FileParameter& FP = ChangeParam(num, na);
        if (FP.ParamType() != Interface_ParamIdent)
          continue;
        Standard_Integer id = -FP.EntityNumber();
        if (id < 0)
          continue; // deja resolu en tete
                    /*	if (imap.Contains(id)) {            et voila
                        FP.SetEntityNumber(indm(imap.FindIndex(id)));
                        continue;
                      }    */

        //  Recherche du Id demande : si EXPORT imbrique, deja resolu mais il faut
        //  regarder ! (inutile par contre d aller y voir : c est deja fait, car
        //  un EXPORT imbrique a ete traite AVANT celui qui imbrique)
        Standard_Integer n0 = nr - 1;
        if (indi(n0) == -3)
          n0--; // si on suit juste un ENDSCOPE
        while (n0 > 0)
        {
          Standard_Integer irec = indi(n0);
          if (irec == id)
          { // trouve
            FP.SetEntityNumber(inds(n0));
            break;
          }
          if (irec == -1)
            break; // SCOPE : fin de ce SCOPE/ENDSCOPE
          if (irec == -3)
          {
            //  gare a EXPORT : si un EXPORT detient Id, noter son Numero deja calcule
            //  Attention : Id a lire depuis CValue  car EntityNumber deja resolu
            Standard_Integer nok = FindEntityNumber(indx->Value(n0), id);
            if (nok > 0)
            {
              FP.SetEntityNumber(nok);
              break;
            }
            n0 = inds(n0); // ENDSCOPE ou EXPORT infructueux : le sauter
          } // fin traitement sur un ENDSCOPE ou EXPORT
          n0--;
        } // fin resolution d un Parametre EXPORT
      } // fin resolution de la liste d un EXPORT
    } // fin bouclage sur les EXPORT
  }

  //  Exploitation de la table : bouclage porte sur la table

  //  Traitement des sous-listes : se fait dans la foulee, par gestion d une pile
  //  basee sur la constitution des sous-listes
  Standard_Integer                 maxsubpil = 30; // pile simulee avec un Array : tres fort
  Handle(TColStd_HArray1OfInteger) subpile   =     // ... gagne de la memoire ...
    new TColStd_HArray1OfInteger(1, maxsubpil);
  Standard_Integer nbsubpil = 0; // ... et tellement plus rapide !

  for (num = 1; num <= nbdirec; num++)
  {
    nr = indr(num);
    if (nr == 0)
      continue; //    pas un objet ou une sous-liste
    Standard_Integer nba = NbParams(num);
    for (Standard_Integer na = nba; na > 0; na--)
    {
      //  On lit depuis la fin : cela permet de traiter les sous-listes dans la foulee
      //  Sinon, on devrait noter qu il y a eu des sous-listes et reprendre ensuite

      Interface_FileParameter& FP     = ChangeParam(num, na);
      Interface_ParamType      letype = FP.ParamType();
      if (letype == Interface_ParamSub)
      {
        //  parametre type sous-liste : numero de la sous-liste lu par depilement
        FP.SetEntityNumber(subpile->Value(nbsubpil));
        nbsubpil--; //	subpile->Remove(nbsubpil);
      }
      else if (letype == Interface_ParamIdent)
      {
        //  parametre type ident (reference une entite) : chercher ident demande
        Standard_Integer id = -FP.EntityNumber();
        if (id < 0)
          continue; // deja resolu en tete

        // Voila : on va chercher id dans ndi; algorithme de balayage
        Standard_Integer pass, sens, nok, n0, irec;
        pass = sens = nok = 0;
        if (!iamap)
          pass = 1; // si map non disponible
        while (pass < 3)
        {
          pass++;
          //    MAP disponible
          if (pass == 1)
          { // MAP DISPONIBLE
            Standard_Integer indmap = imap.FindIndex(id);
            if (indmap > 0)
            { // la map a trouve
              nok = indm(indmap);
              if (nok < 0)
                continue; // CONFLIT -> faut resoudre ...
              break;
            }
            else
              continue;
          }
          //    1re Passe : REMONTEE -> Debut fichier
          if (sens == 0 && nr > 1)
          {
            n0 = nr - 1;
            if (indi(n0) == -3)
              n0--; // si on suit juste un ENDSCOPE
            while (n0 > 0)
            {
              irec = indi(n0);
              if (irec == id)
              { // trouve
                nok = n0;
                break;
              }
              //    ENDSCOPE : Attention a EXPORT sinon sauter
              if (irec == -3)
              {
                if (indx.IsNull())
                  n0 = inds(n0);
                else
                {
                  //    EXPORT, il faut regarder
                  nok = FindEntityNumber(indx->Value(n0), id);
                  if (nok > 0)
                    break;
                  n0 = inds(n0); // ENDSCOPE : le sauter
                }
              }
              n0--;
            }
            //    2me Passe : DESCENTE -> Fin fichier
          }
          else if (nr < nbseq)
          { // descente -> fin fichier
            n0 = nr + 1;
            while (n0 <= nbseq)
            {
              irec = indi(n0);
              if (irec == id)
              { // trouve
                nok = n0;
                break;
              }
              //    SCOPE : Attention a EXPORT sinon sauter
              if (irec == -1)
              {
                if (indx.IsNull())
                  n0 = inds(n0);
                else
                {
                  //    EXPORT, il faut regarder
                  nok = FindEntityNumber(indx->Value(n0), id);
                  if (nok > 0)
                    break;
                  n0 = inds(n0); // SCOPE : le sauter
                }
              }
              n0++;
            }
          }
          if (nok > 0)
            break;
          sens = 1 - sens; // passe suivante
        }
        // ici on a nok, numero trouve
        if (nok > 0)
        {
          Standard_Integer num0 = inds(nok);
          FP.SetEntityNumber(num0); // ET VOILA, on a resolu

          // pas trouve : le signaler
        }
        else
        {
          //  Alimenter le Check ...  Pour cela, determiner n0 Entite et Ident
          char             failmess[100];
          Standard_Integer nument = 0;
          Standard_Integer n0ent; // svv Jan11 2000 : porting on DEC
          for (n0ent = 1; n0ent <= nr; n0ent++)
          {
            if (indi(n0ent) > 0)
              nument++;
          }
          Standard_Integer ident = RecordIdent(num);
          if (ident < 0)
          {
            for (n0ent = num + 1; n0ent <= nbdirec; n0ent++)
            {
              ident = RecordIdent(n0ent);
              if (ident > 0)
                break;
            }
          }
          //  ...  Construire le Check  ...
          sprintf(failmess,
                  "Unresolved Reference, Ent.n0 %d (Id.#%d) Param.n0 %d (Id.#%d)",
                  nument,
                  ident,
                  na,
                  id);
          thecheck->AddFail(failmess, "Unresolved Reference");

          //  ...  Et sortir message un peu plus complet
          sout << "*** ERR StepReaderData *** Entite " << nument << ", a " << (nr * 100) / nbseq
               << "% de DATA : #" << ident << "\n    Type:" << RecordType(num) << "  Param.n0 "
               << na << ": #" << id << " Not found" << std::endl;

          FP.SetEntityNumber(0); // -> Reference non resolue
        }
      }
    }
    //  Si ce record est lui-meme une sous-liste, empiler !
    if (inds(nr) != num)
    {
      if (nbsubpil >= maxsubpil)
      {
        maxsubpil                                  = maxsubpil + 30;
        Handle(TColStd_HArray1OfInteger) newsubpil = new TColStd_HArray1OfInteger(1, maxsubpil);
        for (Standard_Integer bidpil = 1; bidpil <= maxsubpil - 30; bidpil++)
          newsubpil->SetValue(bidpil, subpile->Value(bidpil));
        subpile = newsubpil;
      }
      nbsubpil++;
      subpile->SetValue(nbsubpil, num); // Append(num);
    }
  }
}

//  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##  ##
//  ....             Gestion du Header : Preparation, lecture             ....

//=================================================================================================

Standard_Integer StepData_StepReaderData::FindNextHeaderRecord(const Standard_Integer num) const
{
  // retourne, sur un numero d enregistrement donne (par num), le suivant qui
  // definit une entite, ou 0 si c est fini :
  // Opere comme FindNextRecord mais ne balaie que le Header

  if (num < 0)
    return 0;
  Standard_Integer num1 = num + 1;
  Standard_Integer max  = thenbhead;

  while (num1 <= max)
  {
    // SCOPE,ENDSCOPE et Sous-Liste ont un identifieur negatif
    // Ne retenir que les Idents positifs ou nuls (nul : pas d Ident dans Header)
    if (RecordIdent(num1) >= 0)
      return num1;
    num1++;
  }
  return 0;
}

//=================================================================================================

void StepData_StepReaderData::PrepareHeader()
{
  // Resolution des references : ne concerne que les sous-listes
  //  deja faite par SetEntityNumbers donc pas de souci a se faire

  /*
  // Algorithme repris et adapte de SetEntityNumbers
  //  Traitement des sous-listes : se fait dans la foulee, par gestion d une pile
  //  basee sur la constitution des sous-listes
    TColStd_SequenceOfInteger subpile;
    Standard_Integer nbsubpil = 0;     // profondeur de pile mais plus rapide ...

    for (Standard_Integer num = 1 ; num <= thenbhead ; num ++) {
      Standard_Integer nba = NbParams(num) ;
      for (Standard_Integer na = nba ; na > 0 ; na --) {
  ..  On lit depuis la fin : cela permet de traiter les sous-listes dans la foulee
  ..  Sinon, on devrait noter qu il y a eu des sous-listes et reprendre ensuite

        Interface_FileParameter& FP = ChangeParam(num,na);
        Interface_ParamType letype = FP.ParamType();
        if (letype == Interface_ParamSub) {
  ..  parametre type sous-liste : numero de la sous-liste lu par depilement
          FP.SetEntityNumber(subpile.Last());
  .. ..        SetParam(num,na,FP);
    subpile.Remove(nbsubpil);
          nbsubpil --;
        }
      }
  .. Si c est une sous-liste, empiler
      if (RecordIdent(num) < -2) {
        subpile.Append(num);
        nbsubpil ++;
      }
    }
  */
}

//=================================================================================================

const Handle(Interface_Check) StepData_StepReaderData::GlobalCheck() const
{
  return thecheck;
}
