text: //==================================================================================
  // Copyright (c) 2016 , Advanced Micro Devices, Inc.  All rights reserved.
  //
  /// \author AMD Developer Tools Team
  /// \file gsCallsHistoryLogger.cpp
  ///
  //==================================================================================
  
  //------------------------------ gsCallsHistoryLogger.cpp ------------------------------
  
  // Infra:
  // Infra:
  #include <AMDTOSWrappers/Include/osTime.h>
  #include <AMDTBaseTools/Include/gtAssert.h>
  #include <AMDTOSWrappers/Include/osApplication.h>
  #include <AMDTServerUtilities/Include/suStringConstants.h>
  #include <AMDTServerUtilities/Include/suGlobalVariables.h>
  
  // Local:
  #include <src/gsStringConstants.h>
  #include <src/gsOpenGLMonitor.h>
  #include <src/gsCallsHistoryLogger.h>
  
  // HTML texture thumbnail sizes:
  #define GS_TEX_THUMBNAIL_WIDTH 50
  #define GS_TEX_THUMBNAIL_HEIGHT 50
  #define GS_TEX_THUMBNAIL_BORDER 1
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::gsCallsHistoryLogger
  // Description: Constructor
  // Arguments: contextId - The id of the render context who's calls history is logged.
  // Author:      Yaki Tebeka
  // Date:        4/11/2009
  // ---------------------------------------------------------------------------
  gsCallsHistoryLogger::gsCallsHistoryLogger(int contextId, apMonitoredFunctionId contextCreationFunc, const gtVector<gtString>* pContextAttribs)
      : suCallsHistoryLogger(apContextID(AP_OPENGL_CONTEXT, contextId), contextCreationFunc, suMaxLoggedOpenGLCallsPerContext(), GS_STR_RenderContextCallsHistoryLoggerMessagesLabelFormat, false)
  {
      if (nullptr != pContextAttribs)
      {
          parseContextAttribs(*pContextAttribs);
      }
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::~gsCallsHistoryLogger
  // Description: Destructor
  // Author:      Yaki Tebeka
  // Date:        4/11/2009
  // ---------------------------------------------------------------------------
  gsCallsHistoryLogger::~gsCallsHistoryLogger()
  {
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::onFrameTerminatorCall
  // Description:
  //  Is called when a monitored function, defined as a frame terminator is called.
  //  Clears the calls history log.
  //
  // Author:      Yaki Tebeka
  // Date:        8/11/2009
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::onFrameTerminatorCall()
  {
      // Clear the log:
      clearLog();
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::onContextDeletion
  // Description: Is called when the context that I represent is deleted.
  // Author:      Yaki Tebeka
  // Date:        14/11/2005
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::onContextDeletion()
  {
      // Output a "context deleted" message into the log file:
      outputContextDeletionMessage();
  
      // Close the physical log file:
      closeHTMLLogFile();
  }
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::calculateHTMLLogFilePath
  // Description: Calculates and outputs the HTML log file path.
  // Author:      Yaki Tebeka
  // Date:        18/8/2004
  // Implementation notes:
  //   The log file path is:
  //   <_logFilesDirectoryPath>\<Application name><Context><X><Log><Data and time>.txt
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::calculateHTMLLogFilePath(osFilePath& textLogFilePath) const
  {
      // Build the log file name:
      gtString logFileName;
      logFileName.appendFormattedString(GS_STR_callsLogFilePath, _contextId._contextId);
  
      // Set the log file path:
      textLogFilePath = suCurrentSessionLogFilesDirectory();
      textLogFilePath.setFileName(logFileName);
  
      gtString fileExtension;
      apFileTypeToFileExtensionString(AP_HTML_FILE, fileExtension);
      textLogFilePath.setFileExtension(fileExtension);
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::getHTMLLogFileHeader
  // Description:
  //   Outputs a string that will be used as the generated HTML log file's header
  // Author:      Yaki Tebeka
  // Date:        4/11/2009
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::getHTMLLogFileHeader(gtString& htmlLogFileHeader) const
  {
      htmlLogFileHeader.makeEmpty();
  
      // Get the debugged application name:
      gtString applicationName;
      osGetCurrentApplicationName(applicationName);
  
      // Print the HTML header:
      htmlLogFileHeader += L"<HTML>\n";
      htmlLogFileHeader += L"<head>\n";
      htmlLogFileHeader += L"   <title>OpenGL calls log - ";
      htmlLogFileHeader += applicationName;
      htmlLogFileHeader += L" ";
      gtString contextIdText;
      contextIdText.appendFormattedString(L" - Context %d", _contextId._contextId);
      htmlLogFileHeader += contextIdText;
      htmlLogFileHeader += L" - generated by CodeXL</title>\n";
      htmlLogFileHeader += L"</head>\n\n";
      htmlLogFileHeader += L"<BODY style=\"font: 12px/16px Courier, Verdana, sans-serif; background-color: EFEFEF;\">\n";
      htmlLogFileHeader += L"<h3>\n";
  
      htmlLogFileHeader += L"////////////////////////////////////////////////////////////<br>\n";
      htmlLogFileHeader += L"// This File contain an OpenGL calls log<br>\n";
  
      htmlLogFileHeader += L"// Application: ";
      htmlLogFileHeader += applicationName;
      htmlLogFileHeader += L"<br>\n";
  
      gtString dateAsString;
      _logCreationTime.dateAsString(dateAsString, osTime::WINDOWS_STYLE, osTime::LOCAL);
      htmlLogFileHeader += L"// Generation date: ";
      htmlLogFileHeader += dateAsString;
      htmlLogFileHeader += L"<br>\n";
  
      gtString timeAsString;
      _logCreationTime.timeAsString(timeAsString, osTime::WINDOWS_STYLE, osTime::LOCAL);
      htmlLogFileHeader += L"// Generation time: ";
      htmlLogFileHeader += timeAsString;
      htmlLogFileHeader += L"<br>\n";
  
      gtString contextText = L"// OpenGL Context id:";
      contextText.appendFormattedString(L" %d<br>\n", _contextId._contextId);
      contextText.append(L"// Context created with ").append(apMonitoredFunctionsManager::instance().monitoredFunctionName(m_contextCreationFunc)).append(L"<br>\n");
      contextText.append(m_contextCreationAttribsString);
      htmlLogFileHeader += contextText;
  
      htmlLogFileHeader += L"//<br>\n";
      htmlLogFileHeader += L"// Generated by CodeXL - OpenGL and OpenCL Debugger, Profiler and Memory Analyzer<br>\n";
      htmlLogFileHeader += L"// <A HREF=\"http://gpuopen.com/\" TARGET=\"_blank\">http://gpuopen.com/</A><br>\n";
      htmlLogFileHeader += L"////////////////////////////////////////////////////////////<br>\n";
      htmlLogFileHeader += L"</h3>\n";
      htmlLogFileHeader += L"<br>\n\n";
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::getHTMLLogFileFooter
  // Description:
  //   Outputs a string that will be used as the generated HTML log file's footer
  // Author:      Yaki Tebeka
  // Date:        4/11/2009
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::getHTMLLogFileFooter(gtString& htmlLogFileFooter) const
  {
      htmlLogFileFooter = L"\n</BODY>\n";
      htmlLogFileFooter += L"</HTML>\n";
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::outputContextDeletionMessage
  // Description: Outputs a context deletion message into the text log file.
  // Author:      Yaki Tebeka
  // Date:        14/11/2005
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::outputContextDeletionMessage()
  {
      // only print if the text log file is active.
      bool isHTMLFileOpen = isHTMLLogFileOpen();
  
      if (isHTMLFileOpen)
      {
          gtString contextDeletionMessage(GS_STR_contextWasDeletedHTMLLog);
          printToHTMLLogFile(contextDeletionMessage);
      }
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::getPseudoArgumentHTMLLogSection
  // Description: Inputs a pseudo argument and output an HTML section that represents it.
  // Arguments: pseudoParam - The pseudo argument to be logged.
  //            htmlLogFileSection - The HTML section that represents the pseudo argument
  // Author:      Yaki Tebeka
  // Date:        26/1/2005
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::getPseudoArgumentHTMLLogSection(const apPseudoParameter& pseudoArgument, gtString& htmlLogFileSection)
  {
      // Handle the pseudo param according to its type:
      osTransferableObjectType pseudoArgType = pseudoArgument.type();
  
      switch (pseudoArgType)
      {
          case OS_TOBJ_ID_ASSOCIATED_TEXTURE_NAMES_PSEUDO_PARAMETER:
          {
              getAssociatedTextureHTMLLogSection((const apAssociatedTextureNamesPseudoParameter&)pseudoArgument, htmlLogFileSection);
              break;
          }
  
          case OS_TOBJ_ID_ASSOCIATED_PROGRAM_NAME_PSEUDO_PARAMETER:
          {
              getAssociatedProgramHTMLLogSection((const apAssociatedProgramNamePseudoParameter&)pseudoArgument, htmlLogFileSection);
              break;
          }
  
          case OS_TOBJ_ID_ASSOCIATED_SHADER_NAME_PSEUDO_PARAMETER:
          {
              getAssociatedShaderHTMLLogSection((const apAssociatedShaderNamePseudoParameter&)pseudoArgument, htmlLogFileSection);
              break;
          }
  
          default:
          {
              // We shouldn't reach here!
              GT_ASSERT(false);
          }
      }
  }
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::reportMemoryAllocationFailure
  // Description: Reports to my debugger about memory allocation failure.
  // Author:      Yaki Tebeka
  // Date:        31/1/2009
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::reportMemoryAllocationFailure()
  {
      // If we are not in glBegin-glEnd block:
      bool isInGLBeginEndBlock = isInOpenGLBeginEndBlock();
  
      if (!isInGLBeginEndBlock)
      {
          // Get the amount of function calls:
          int functionCallsAmount = amountOfFunctionCalls();
  
          // Calculate the report string:
          gtString errorDescription = _loggerMessagesLabel;
          bool allocFailureOccur = allocationFailureOccur();
  
          if (allocFailureOccur)
          {
              errorDescription.appendFormattedString(SU_STR_notEnoughMemoryForLoggingFunctions, functionCallsAmount);
          }
          else
          {
              errorDescription.appendFormattedString(SU_STR_maxLoggedFunctionsAmountReached, functionCallsAmount);
          }
  
          // Report an error to the debugger:
          gsOpenGLMonitor& theOpenGLMonitor = gsOpenGLMonitor::instance();
          theOpenGLMonitor.reportDetectedError(AP_MAX_LOGGED_FUNCTIONS_EXCEEDED_ERROR, errorDescription, apMonitoredFunctionsAmount);
      }
  
      // Perform base class actions:
      suCallsHistoryLogger::reportMemoryAllocationFailure();
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::getAssociatedTextureHTMLLogSection
  // Description: Stores an associated texture pseudo argument into
  //              htmlLogFileSection.
  // Arguments:   assciatedTexture - The associated texture pseudo argument.
  // Author:      Yaki Tebeka
  // Date:        26/1/2005
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::getAssociatedTextureHTMLLogSection(const apAssociatedTextureNamesPseudoParameter& assciatedTexture,
                                                                gtString& htmlLogFileSection)
  {
      // Get the names of textures associated with the current function call:
      const gtVector<GLuint>& associatedTextureNames = assciatedTexture.associatedTextureNames();
      int amountOfAssociatedTextures = (int)associatedTextureNames.size();
  
      // Get an instance of the openGL monitor
      const gsOpenGLMonitor& theOpenGLMonitor = gsOpenGLMonitor::instance();
  
      // Get the appropriate render context monitor:
      const gsRenderContextMonitor* pRenderContextMon = theOpenGLMonitor.renderContextMonitor(_contextId._contextId);
  
      if (pRenderContextMon)
      {
          for (int i = 0; i < amountOfAssociatedTextures; i++)
          {
              // Get the current texture details:
              GLuint textureID = associatedTextureNames[i];
              const gsTexturesMonitor* texturesMon = pRenderContextMon->texturesMonitor();
              GT_IF_WITH_ASSERT(texturesMon != NULL)
              {
                  gsGLTexture* pTextureDetails = texturesMon->getTextureObjectDetails(textureID);
  
                  if (pTextureDetails)
                  {
                      // Get the texture type
                      apTextureType textureType = pTextureDetails->textureType();
  
                      // Get current texture image saving format
                      apFileType fileType = suLoggedTexturesFileType();
  
                      // On 3D Textures we always use tiff format
                      if ((textureType == AP_3D_TEXTURE) || (textureType == AP_1D_ARRAY_TEXTURE) || (textureType == AP_2D_ARRAY_TEXTURE))
                      {
                          fileType = AP_TIFF_FILE;
                      }
  
                      // Get image format extension (as string)
                      gtString fileExtension;
                      bool rc1 = apFileTypeToFileExtensionString(fileType, fileExtension);
                      GT_IF_WITH_ASSERT(rc1)
                      {
                          // Ask the texture monitor to generate the name for current texture object
                          bool rc2 = texturesMon->generateTextureElementsPaths(pTextureDetails);
                          GT_IF_WITH_ASSERT(rc2)
                          {
                              // Get the amount of texture data files:
                              int amountOfFiles = pTextureDetails->amountOfTextureDataFiles();
  
                              if (amountOfFiles == 0)
                              {
                                  // Add a "no texture" printout to the print buffer:
                                  htmlLogFileSection.appendFormattedString(GS_STR_noTextureImagePreviewAvailable, _contextId._contextId, textureID);
                              }
                              else
                              {
                                  // Print the texture context and name:
                                  htmlLogFileSection.appendFormattedString(GS_STR_beginTexturePreview, _contextId._contextId, textureID);
  
                                  // Iterate the available texture data files:
                                  for (int j = 0; j < amountOfFiles; j++)
                                  {
                                      // Add comma separator between texture images:
                                      if (j != 0)
                                      {
                                          htmlLogFileSection += L", ";
                                      }
  
                                      // Get the current texture data file path:
                                      gtString textureFileName;
                                      const osFilePath* pTextureDataFilePath = nullptr;
                                      bool rc0 = pTextureDetails->getTextureDataFilePath(pTextureDataFilePath, j);
                                      GT_IF_WITH_ASSERT(rc0 && (nullptr != pTextureDataFilePath))
                                      {
                                          bool rc3 = pTextureDataFilePath->getFileName(textureFileName);
                                          GT_ASSERT(rc3);
                                      }
  
                                      if (!textureFileName.isEmpty())
                                      {
                                          // Translate the file path into a relative URL:
                                          gtString fileRelativeURL = L"./";
                                          fileRelativeURL += textureFileName;
                                          fileRelativeURL += osFilePath::osExtensionSeparator;
                                          fileRelativeURL += fileExtension;
  
                                          // Add the current texture image printout to the print buffer:
                                          htmlLogFileSection.appendFormattedString(GS_STR_texturePreview, fileRelativeURL.asCharArray(), fileRelativeURL.asCharArray(), _contextId._contextId, textureID, _contextId._contextId, textureID, GS_TEX_THUMBNAIL_WIDTH, GS_TEX_THUMBNAIL_HEIGHT, GS_TEX_THUMBNAIL_BORDER);
                                      }
                                      else
                                      {
                                          htmlLogFileSection += GS_STR_textureNoPreviewAvailable;
                                      }
                                  }
  
                                  // Close the texture printout block:
                                  htmlLogFileSection += GS_STR_endTexturePreview;
                              }
                          }
                      }
                  }
              }
          }
      }
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::getAssociatedProgramHTMLLogSection
  // Description: Stores an associated program pseudo argument into
  //              htmlLogFileSection.
  // Arguments:   associatedProgram - The associated program pseudo argument.
  // Author:      Yaki Tebeka
  // Date:        22/5/2005
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::getAssociatedProgramHTMLLogSection(const apAssociatedProgramNamePseudoParameter& associatedProgram,
                                                                gtString& htmlLogFileSection)
  {
      // Get the OpenGL name of the program associated with the current function call:
      GLuint associatedProgramName = associatedProgram.associatedProgramName();
  
      // Add an associated program printout to the print buffer:
      htmlLogFileSection.appendFormattedString(GS_STR_assiciatedProgramTextStart, _contextId._contextId, associatedProgramName);
  
      // Get the appropriate render context monitor:
      const gsOpenGLMonitor& theOpenGLMonitor = gsOpenGLMonitor::instance();
      const gsRenderContextMonitor* pRenderContextMon = theOpenGLMonitor.renderContextMonitor(_contextId._contextId);
  
      if (pRenderContextMon)
      {
          // Get the program object details:
          const gsProgramsAndShadersMonitor* programsAndShadersMon = pRenderContextMon->programsAndShadersMonitor();
          GT_IF_WITH_ASSERT(programsAndShadersMon != NULL)
          {
              const apGLProgram* pProgramObjDetails = programsAndShadersMon->programObjectDetails(associatedProgramName);
  
              if (pProgramObjDetails != NULL)
              {
                  // Iterate the program attached shaders:
                  const gtList<GLuint>& attachedShaders = pProgramObjDetails->shaderObjects();
                  gtList<GLuint>::const_iterator iter = attachedShaders.begin();
                  gtList<GLuint>::const_iterator endIter = attachedShaders.end();
                  bool isFirstShader = true;
  
                  while (iter != endIter)
                  {
                      // Add comma and colon if needed:
                      if (isFirstShader)
                      {
                          htmlLogFileSection += L": ";
                          isFirstShader = false;
                      }
                      else
                      {
                          htmlLogFileSection += L", ";
                      }
  
                      // Get the shader object name:
                      GLuint currShaderName = *iter;
  
                      // Get the shader object details:
                      const apGLShaderObject* pShaderObjDetails = programsAndShadersMon->shaderObjectDetails(currShaderName);
  
                      if (pShaderObjDetails)
                      {
                          // Get the shader source code file:
                          const osFilePath& shaderSourceCodePath = pShaderObjDetails->sourceCodeFilePath();
                          const gtString& shaderSourceCodePathAsString = shaderSourceCodePath.asString();
  
                          // If we don't have the shader source code file:
                          if (shaderSourceCodePathAsString.isEmpty())
                          {
                              // Add a "source code is not available" printout to the print buffer:
                              htmlLogFileSection.appendFormattedString(GS_STR_assiciatedProgramTextShaderNoSourceCode, currShaderName);
                          }
                          else
                          {
                              // Will get the source code file relative path:
                              gtString fileRelativeURL = L"N/A";
  
                              // Get the shader file path name and extension:
                              gtString fileName;
                              gtString fileExtension;
                              bool rc1 = shaderSourceCodePath.getFileName(fileName);
                              bool rc2 = shaderSourceCodePath.getFileExtension(fileExtension);
  
                              if (rc1 && rc2)
                              {
                                  // Translate the file path into a relative URL:
                                  fileRelativeURL = L"./";
                                  fileRelativeURL += fileName;
                                  fileRelativeURL += osFilePath::osExtensionSeparator;
                                  fileRelativeURL += fileExtension;
                              }
  
                              // Print the context and shader names:
                              htmlLogFileSection.appendFormattedString(GS_STR_assiciatedProgramTextShader,
                                                                       fileRelativeURL.asCharArray(),
                                                                       currShaderName);
                          }
                      }
  
                      // Next associated shader:
                      iter++;
                  }
              }
  
          }
          // End the program printed string:
          htmlLogFileSection += GS_STR_assiciatedProgramTextEnd;
      }
  }
  
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::getAssociatedShaderHTMLLogSection
  // Description: Stores an associated program pseudo argument into
  //              htmlLogFileSection.
  // Arguments:   associatedShader - The associated shader pseudo argument.
  // Author:      Yaki Tebeka
  // Date:        22/5/2005
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::getAssociatedShaderHTMLLogSection(const apAssociatedShaderNamePseudoParameter& associatedShader,
                                                               gtString& htmlLogFileSection)
  {
      // Get the OpenGL name of the shader associated with the current function call:
      GLuint associatedShaderName = associatedShader.associatedShaderName();
  
      // Get the appropriate render context monitor:
      const gsOpenGLMonitor& theOpenGLMonitor = gsOpenGLMonitor::instance();
      const gsRenderContextMonitor* pRenderContextMon = theOpenGLMonitor.renderContextMonitor(_contextId._contextId);
  
      if (pRenderContextMon)
      {
          // Get the shader object details:
          const gsProgramsAndShadersMonitor* programsAndShadersMon = pRenderContextMon->programsAndShadersMonitor();
          GT_IF_WITH_ASSERT(programsAndShadersMon != NULL)
          {
              const apGLShaderObject* pShaderObjDetails = programsAndShadersMon->shaderObjectDetails(associatedShaderName);
  
              if (pShaderObjDetails)
              {
                  // Get the shader source code file:
                  const osFilePath& shaderSourceCodePath = pShaderObjDetails->sourceCodeFilePath();
                  const gtString& shaderSourceCodePathAsString = shaderSourceCodePath.asString();
  
                  // If we don't have the shader source code file:
                  if (shaderSourceCodePathAsString.isEmpty())
                  {
                      // Add a "source code is not available" printout to the print buffer:
                      htmlLogFileSection.appendFormattedString(GS_STR_noShaderSourceCodeAvailable, _contextId._contextId, associatedShaderName);
                  }
                  else
                  {
                      // Will get the source code file relative path:
                      gtString fileRelativeURL = L"N/A";
  
                      // Get the shader file path name and extension:
                      gtString fileName;
                      gtString fileExtension;
                      bool rc1 = shaderSourceCodePath.getFileName(fileName);
                      bool rc2 = shaderSourceCodePath.getFileExtension(fileExtension);
  
                      if (rc1 && rc2)
                      {
                          // Translate the file path into a relative URL:
                          fileRelativeURL = L"./";
                          fileRelativeURL += fileName;
                          fileRelativeURL += osFilePath::osExtensionSeparator;
                          fileRelativeURL += fileExtension;
                      }
  
                      // Print the context and shader names:
                      htmlLogFileSection.appendFormattedString(GS_STR_shaderNameAndSourceCodeBegin, _contextId._contextId, fileRelativeURL.asCharArray(), associatedShaderName);
                  }
              }
          }
      }
  }
  
  // ---------------------------------------------------------------------------
  // Name:        gsCallsHistoryLogger::parseContextAttribs
  // Description: Creates a string to be used in the log header from a list of attributes
  // Author:      Uri Shomroni
  // Date:        27/7/2015
  // ---------------------------------------------------------------------------
  void gsCallsHistoryLogger::parseContextAttribs(const gtVector<gtString>& contextAttribs)
  {
      m_contextCreationAttribsString = L"// OpenGL Context attributes:<br>\n";
  
      int attribsCount = (int)contextAttribs.size();
  
      if (0 < attribsCount)
      {
          for (int i = 0; attribsCount > i; ++i)
          {
              const gtString& currentAttrib = contextAttribs[i];
              int attribLen = currentAttrib.length();
  
              if (60 > attribLen)
              {
                  m_contextCreationAttribsString.append(L"//    ").append(currentAttrib).append(L"<br>\n");
              }
              else
              {
                  gtString subStr;
  
                  for (int j = 0; attribLen > j; j += 60)
                  {
                      int line = (60 <= attribLen - j) ? 59 : attribLen - j - 1;
                      currentAttrib.getSubString(j, j + line, subStr);
                      m_contextCreationAttribsString.append(L"//    ").append(subStr).append(L"<br>\n");
                  }
              }
          }
      }
  }
  