]> OCCT Git - occt.git/commitdiff
Data Exchange, DE_Wrapper - Implement Stream support (#663)
authorPasukhin Dmitry <dpasukhi@opencascade.com>
Fri, 8 Aug 2025 11:05:27 +0000 (12:05 +0100)
committerGitHub <noreply@github.com>
Fri, 8 Aug 2025 11:05:27 +0000 (12:05 +0100)
- Adds stream-based read/write methods to multiple data exchange providers
- Refactors underlying APIs (VrmlAPI_Writer, StlAPI_Writer, RWStl) to support stream operations
- Implements comprehensive test coverage for stream functionality
- Adds validation utilities for improved error handling

39 files changed:
src/DataExchange/TKDE/DE/DE_ConfigurationNode.cxx
src/DataExchange/TKDE/DE/DE_ConfigurationNode.hxx
src/DataExchange/TKDE/DE/DE_Provider.cxx
src/DataExchange/TKDE/DE/DE_Provider.hxx
src/DataExchange/TKDE/DE/DE_ValidationUtils.cxx [new file with mode: 0644]
src/DataExchange/TKDE/DE/DE_ValidationUtils.hxx [new file with mode: 0644]
src/DataExchange/TKDE/DE/DE_Wrapper.cxx
src/DataExchange/TKDE/DE/DE_Wrapper.hxx
src/DataExchange/TKDE/DE/FILES.cmake
src/DataExchange/TKDEGLTF/DEGLTF/DEGLTF_Provider.cxx
src/DataExchange/TKDEIGES/DEIGES/DEIGES_Provider.cxx
src/DataExchange/TKDEOBJ/DEOBJ/DEOBJ_Provider.cxx
src/DataExchange/TKDEPLY/DEPLY/DEPLY_Provider.cxx
src/DataExchange/TKDESTEP/DESTEP/DESTEP_ConfigurationNode.cxx
src/DataExchange/TKDESTEP/DESTEP/DESTEP_ConfigurationNode.hxx
src/DataExchange/TKDESTEP/DESTEP/DESTEP_Provider.cxx
src/DataExchange/TKDESTEP/DESTEP/DESTEP_Provider.hxx
src/DataExchange/TKDESTEP/GTests/DESTEP_Provider_Test.cxx [new file with mode: 0644]
src/DataExchange/TKDESTEP/GTests/FILES.cmake
src/DataExchange/TKDESTL/DESTL/DESTL_ConfigurationNode.cxx
src/DataExchange/TKDESTL/DESTL/DESTL_ConfigurationNode.hxx
src/DataExchange/TKDESTL/DESTL/DESTL_Provider.cxx
src/DataExchange/TKDESTL/DESTL/DESTL_Provider.hxx
src/DataExchange/TKDESTL/GTests/DESTL_Provider_Test.cxx [new file with mode: 0644]
src/DataExchange/TKDESTL/GTests/FILES.cmake
src/DataExchange/TKDESTL/RWStl/RWStl.cxx
src/DataExchange/TKDESTL/RWStl/RWStl.hxx
src/DataExchange/TKDESTL/StlAPI/StlAPI_Reader.cxx
src/DataExchange/TKDESTL/StlAPI/StlAPI_Reader.hxx
src/DataExchange/TKDESTL/StlAPI/StlAPI_Writer.cxx
src/DataExchange/TKDESTL/StlAPI/StlAPI_Writer.hxx
src/DataExchange/TKDEVRML/DEVRML/DEVRML_ConfigurationNode.cxx
src/DataExchange/TKDEVRML/DEVRML/DEVRML_ConfigurationNode.hxx
src/DataExchange/TKDEVRML/DEVRML/DEVRML_Provider.cxx
src/DataExchange/TKDEVRML/DEVRML/DEVRML_Provider.hxx
src/DataExchange/TKDEVRML/GTests/DEVRML_Provider_Test.cxx [new file with mode: 0644]
src/DataExchange/TKDEVRML/GTests/FILES.cmake
src/DataExchange/TKDEVRML/VrmlAPI/VrmlAPI_Writer.cxx
src/DataExchange/TKDEVRML/VrmlAPI/VrmlAPI_Writer.hxx

index a7e057b719dc12e32a1deb4b1427348849e8f76a..bdd37294e0232115e424b3a817ef58e2d3fd4396 100644 (file)
@@ -103,6 +103,13 @@ bool DE_ConfigurationNode::IsExportSupported() const
 
 //=================================================================================================
 
+bool DE_ConfigurationNode::IsStreamSupported() const
+{
+  return false;
+}
+
+//=================================================================================================
+
 bool DE_ConfigurationNode::CheckExtension(const TCollection_AsciiString& theExtension) const
 {
   TCollection_AsciiString anExtension(theExtension);
index 870729b40aa1c4440a369509aca93e76c6653426..68075ee15b9293c1b4e31363011c700b7b19f3ab 100644 (file)
@@ -92,14 +92,18 @@ public:
                                           const Standard_Boolean theToKeep);
 
 public:
-  //! Checks the import supporting
-  //! @return Standard_True if import is support
+  //! Checks for import support.
+  //! @return Standard_True if import is supported
   Standard_EXPORT virtual bool IsImportSupported() const;
 
-  //! Checks the export supporting
-  //! @return Standard_True if export is support
+  //! Checks for export support.
+  //! @return Standard_True if export is supported
   Standard_EXPORT virtual bool IsExportSupported() const;
 
+  //! Checks for stream support.
+  //! @return Standard_True if streams are supported
+  Standard_EXPORT virtual bool IsStreamSupported() const;
+
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
   Standard_EXPORT virtual TCollection_AsciiString GetFormat() const = 0;
index 77572d3859b617ab81a51b8dc246389d7238551a..bc3bfe2cc84ee33193f66b6e98fd9c1dceb74e00 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <DE_ConfigurationNode.hxx>
 #include <Message.hxx>
+#include <NCollection_IndexedDataMap.hxx>
 
 IMPLEMENT_STANDARD_RTTIEXT(DE_Provider, Standard_Transient)
 
@@ -148,3 +149,123 @@ Standard_Boolean DE_Provider::Write(const TCollection_AsciiString& thePath,
                       << " doesn't support write operation";
   return Standard_False;
 }
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Read(ReadStreamList&                 theStreams,
+                                   const Handle(TDocStd_Document)& theDocument,
+                                   Handle(XSControl_WorkSession)&  theWS,
+                                   const Message_ProgressRange&    theProgress)
+{
+  (void)theStreams;
+  (void)theDocument;
+  (void)theWS;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream read operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Write(WriteStreamList&                theStreams,
+                                    const Handle(TDocStd_Document)& theDocument,
+                                    Handle(XSControl_WorkSession)&  theWS,
+                                    const Message_ProgressRange&    theProgress)
+{
+  (void)theStreams;
+  (void)theDocument;
+  (void)theWS;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream write operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Read(ReadStreamList&                theStreams,
+                                   TopoDS_Shape&                  theShape,
+                                   Handle(XSControl_WorkSession)& theWS,
+                                   const Message_ProgressRange&   theProgress)
+{
+  (void)theStreams;
+  (void)theShape;
+  (void)theWS;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream read operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Write(WriteStreamList&               theStreams,
+                                    const TopoDS_Shape&            theShape,
+                                    Handle(XSControl_WorkSession)& theWS,
+                                    const Message_ProgressRange&   theProgress)
+{
+  (void)theStreams;
+  (void)theShape;
+  (void)theWS;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream write operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Read(ReadStreamList&                 theStreams,
+                                   const Handle(TDocStd_Document)& theDocument,
+                                   const Message_ProgressRange&    theProgress)
+{
+  (void)theStreams;
+  (void)theDocument;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream read operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Write(WriteStreamList&                theStreams,
+                                    const Handle(TDocStd_Document)& theDocument,
+                                    const Message_ProgressRange&    theProgress)
+{
+  (void)theStreams;
+  (void)theDocument;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream write operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Read(ReadStreamList&              theStreams,
+                                   TopoDS_Shape&                theShape,
+                                   const Message_ProgressRange& theProgress)
+{
+  (void)theStreams;
+  (void)theShape;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream read operation";
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Provider::Write(WriteStreamList&             theStreams,
+                                    const TopoDS_Shape&          theShape,
+                                    const Message_ProgressRange& theProgress)
+{
+  (void)theStreams;
+  (void)theShape;
+  (void)theProgress;
+  Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
+                      << " doesn't support stream write operation";
+  return Standard_False;
+}
index 4613250a5b29b06bada46015364e32750e3e2f4a..0023f0728283bb5e319bf76608b12c5b5832e606 100644 (file)
@@ -15,6 +15,9 @@
 #define _DE_Provider_HeaderFile
 
 #include <Message_ProgressRange.hxx>
+#include <NCollection_List.hxx>
+#include <Standard_IStream.hxx>
+#include <Standard_OStream.hxx>
 
 class DE_ConfigurationNode;
 class TopoDS_Shape;
@@ -43,6 +46,45 @@ class DE_Provider : public Standard_Transient
 public:
   DEFINE_STANDARD_RTTIEXT(DE_Provider, Standard_Transient)
 
+  //! Node to store write stream information
+  //! Contains relative path and reference to output stream
+  struct WriteStreamNode
+  {
+    TCollection_AsciiString Path;   //!< Relative path to the output file
+    Standard_OStream&       Stream; //!< Reference to output stream
+
+    //! Constructor
+    WriteStreamNode(const TCollection_AsciiString& thePath, Standard_OStream& theStream)
+        : Path(thePath),
+          Stream(theStream)
+    {
+    }
+  };
+
+  //! Node to store read stream information
+  //! Contains relative path and reference to input stream
+  struct ReadStreamNode
+  {
+    TCollection_AsciiString Path;   //!< Relative path to the input file
+    Standard_IStream&       Stream; //!< Reference to input stream
+
+    //! Constructor
+    ReadStreamNode(const TCollection_AsciiString& thePath, Standard_IStream& theStream)
+        : Path(thePath),
+          Stream(theStream)
+    {
+    }
+  };
+
+public:
+  //! List to store write stream nodes
+  //! First element is the main stream, others are for internal referencing
+  using WriteStreamList = NCollection_List<WriteStreamNode>;
+
+  //! List to store read stream nodes
+  //! First element is the main stream, others are for internal referencing
+  using ReadStreamList = NCollection_List<ReadStreamNode>;
+
 public:
   //! Default constructor
   //! Configure translation process with global configuration
@@ -77,6 +119,30 @@ public:
     Handle(XSControl_WorkSession)&  theWS,
     const Message_ProgressRange&    theProgress = Message_ProgressRange());
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return True if Read was successful
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return True if Write was successful
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
   //! Reads a CAD file, according internal configuration
   //! @param[in] thePath path to the import CAD file
   //! @param[out] theDocument document to save result
@@ -97,6 +163,26 @@ public:
     const Handle(TDocStd_Document)& theDocument,
     const Message_ProgressRange&    theProgress = Message_ProgressRange());
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theProgress progress indicator
+  //! @return True if Read was successful
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theProgress progress indicator
+  //! @return True if Write was successful
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
   //! Reads a CAD file, according internal configuration
   //! @param[in] thePath path to the import CAD file
   //! @param[out] theShape shape to save result
@@ -121,6 +207,30 @@ public:
     Handle(XSControl_WorkSession)& theWS,
     const Message_ProgressRange&   theProgress = Message_ProgressRange());
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return True if Read was successful
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                theStreams,
+    TopoDS_Shape&                  theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return True if Write was successful
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&               theStreams,
+    const TopoDS_Shape&            theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange());
+
   //! Reads a CAD file, according internal configuration
   //! @param[in] thePath path to the import CAD file
   //! @param[out] theShape shape to save result
@@ -141,6 +251,26 @@ public:
     const TopoDS_Shape&            theShape,
     const Message_ProgressRange&   theProgress = Message_ProgressRange());
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theProgress progress indicator
+  //! @return True if Read was successful
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&              theStreams,
+    TopoDS_Shape&                theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theProgress progress indicator
+  //! @return True if Write was successful
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&             theStreams,
+    const TopoDS_Shape&          theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange());
+
 public:
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
diff --git a/src/DataExchange/TKDE/DE/DE_ValidationUtils.cxx b/src/DataExchange/TKDE/DE/DE_ValidationUtils.cxx
new file mode 100644 (file)
index 0000000..4bc05b7
--- /dev/null
@@ -0,0 +1,345 @@
+// Copyright (c) 2025 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.
+
+#include <DE_ValidationUtils.hxx>
+
+#include <Message.hxx>
+#include <NCollection_Buffer.hxx>
+#include <NCollection_BaseAllocator.hxx>
+#include <OSD_FileSystem.hxx>
+#include <OSD_Path.hxx>
+#include <OSD_File.hxx>
+#include <OSD_Protection.hxx>
+#include <fstream>
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::ValidateConfigurationNode(
+  const Handle(DE_ConfigurationNode)& theNode,
+  const Handle(Standard_Type)&        theExpectedType,
+  const TCollection_AsciiString&      theContext,
+  const Standard_Boolean              theIsVerbose)
+{
+  if (theNode.IsNull())
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": Configuration Node is null";
+    }
+    return Standard_False;
+  }
+
+  if (!theNode->IsKind(theExpectedType))
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext
+                          << ": Configuration Node is not of expected type. Expected: "
+                          << theExpectedType->Name() << ", got: " << theNode->DynamicType()->Name();
+    }
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::ValidateFileForReading(
+  const TCollection_AsciiString& thePath,
+  const TCollection_AsciiString& theContext,
+  const Standard_Boolean         theIsVerbose)
+{
+  if (thePath.IsEmpty())
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": File path is empty";
+    }
+    return Standard_False;
+  }
+
+  try
+  {
+    OSD_Path aOSDPath(thePath);
+    OSD_File aFile(aOSDPath);
+
+    // Check if file exists
+    if (!aFile.Exists())
+    {
+      if (theIsVerbose)
+      {
+        Message::SendFail() << "Error during " << theContext << ": File '" << thePath
+                            << "' does not exist";
+      }
+      return Standard_False;
+    }
+
+    // Try to open for reading to verify permissions
+    std::ifstream aTestFile(thePath.ToCString());
+    if (!aTestFile.is_open() || !aTestFile.good())
+    {
+      if (theIsVerbose)
+      {
+        Message::SendFail() << "Error during " << theContext << ": Cannot open file '" << thePath
+                            << "' for reading";
+      }
+      return Standard_False;
+    }
+  }
+  catch (const std::exception& anException)
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": Cannot access file '" << thePath
+                          << "': " << anException.what();
+    }
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::ValidateFileForWriting(
+  const TCollection_AsciiString& thePath,
+  const TCollection_AsciiString& theContext,
+  const Standard_Boolean         theIsVerbose)
+{
+  if (thePath.IsEmpty())
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": File path is empty";
+    }
+    return Standard_False;
+  }
+
+  try
+  {
+    // Try to open for writing to verify permissions
+    std::ofstream aTestFile(thePath.ToCString(), std::ios::out | std::ios::app);
+    if (!aTestFile.is_open() || !aTestFile.good())
+    {
+      if (theIsVerbose)
+      {
+        Message::SendFail() << "Error during " << theContext << ": Cannot open file '" << thePath
+                            << "' for writing";
+      }
+      return Standard_False;
+    }
+    // File will be closed automatically when aTestFile goes out of scope
+  }
+  catch (const std::exception& anException)
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": Cannot access file '" << thePath
+                          << "': " << anException.what();
+    }
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::ValidateReadStreamList(
+  const DE_Provider::ReadStreamList& theStreams,
+  const TCollection_AsciiString&     theContext,
+  const Standard_Boolean             theIsVerbose)
+{
+  if (theStreams.IsEmpty())
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": Stream list is empty";
+    }
+    return Standard_False;
+  }
+
+  if (theStreams.Size() > 1)
+  {
+    if (theIsVerbose)
+    {
+      Message::SendWarning() << "Warning during " << theContext << ": Received "
+                             << theStreams.Size() << " streams, using only the first one";
+    }
+  }
+
+  // Additional validation for input streams
+  try
+  {
+    const DE_Provider::ReadStreamNode& aNode = theStreams.First();
+    if (aNode.Stream.fail())
+    {
+      if (theIsVerbose)
+      {
+        TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
+        Message::SendFail() << "Error during " << theContext << ": Input stream '" << aKeyInfo
+                            << "' is in invalid state";
+      }
+      return Standard_False;
+    }
+  }
+  catch (const std::exception&)
+  {
+    if (theIsVerbose)
+    {
+      const DE_Provider::ReadStreamNode& aNode = theStreams.First();
+      TCollection_AsciiString aKeyInfo         = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
+      Message::SendFail() << "Error during " << theContext << ": Cannot access input stream '"
+                          << aKeyInfo << "'";
+    }
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::ValidateWriteStreamList(
+  DE_Provider::WriteStreamList&  theStreams,
+  const TCollection_AsciiString& theContext,
+  const Standard_Boolean         theIsVerbose)
+{
+  if (theStreams.IsEmpty())
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": Stream list is empty";
+    }
+    return Standard_False;
+  }
+
+  if (theStreams.Size() > 1)
+  {
+    if (theIsVerbose)
+    {
+      Message::SendWarning() << "Warning during " << theContext << ": Received "
+                             << theStreams.Size() << " streams, using only the first one";
+    }
+  }
+
+  // Additional validation for output streams
+  try
+  {
+    const DE_Provider::WriteStreamNode& aNode = theStreams.First();
+    if (aNode.Stream.fail())
+    {
+      if (theIsVerbose)
+      {
+        TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
+        Message::SendFail() << "Error during " << theContext << ": Output stream '" << aKeyInfo
+                            << "' is in invalid state";
+      }
+      return Standard_False;
+    }
+  }
+  catch (const std::exception&)
+  {
+    if (theIsVerbose)
+    {
+      const DE_Provider::WriteStreamNode& aNode = theStreams.First();
+      TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
+      Message::SendFail() << "Error during " << theContext << ": Cannot access output stream '"
+                          << aKeyInfo << "'";
+    }
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::ValidateDocument(const Handle(TDocStd_Document)& theDocument,
+                                                      const TCollection_AsciiString&  theContext,
+                                                      const Standard_Boolean          theIsVerbose)
+{
+  if (theDocument.IsNull())
+  {
+    if (theIsVerbose)
+    {
+      Message::SendFail() << "Error during " << theContext << ": Document handle is null";
+    }
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::WarnLengthUnitNotSupported(
+  const Standard_Real            theLengthUnit,
+  const TCollection_AsciiString& theContext,
+  const Standard_Boolean         theIsVerbose)
+{
+  if (theIsVerbose && theLengthUnit != 1.0)
+  {
+    Message::SendWarning() << "Warning during " << theContext
+                           << ": Format doesn't support custom length unit scaling (unit: "
+                           << theLengthUnit << ")";
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::CreateContentBuffer(const TCollection_AsciiString& thePath,
+                                                         Handle(NCollection_Buffer)&    theBuffer)
+{
+  const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
+  std::shared_ptr<std::istream> aStream =
+    aFileSystem->OpenIStream(thePath, std::ios::in | std::ios::binary);
+
+  if (aStream.get() == nullptr)
+  {
+    theBuffer.Nullify();
+    return Standard_False;
+  }
+
+  return CreateContentBuffer(*aStream, theBuffer);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_ValidationUtils::CreateContentBuffer(std::istream&               theStream,
+                                                         Handle(NCollection_Buffer)& theBuffer)
+{
+  constexpr std::streamsize aBufferLength = 2048;
+
+  theBuffer =
+    new NCollection_Buffer(NCollection_BaseAllocator::CommonBaseAllocator(), aBufferLength);
+
+  // Save current stream position
+  std::streampos aOriginalPos = theStream.tellg();
+
+  theStream.read(reinterpret_cast<char*>(theBuffer->ChangeData()), aBufferLength);
+  const std::streamsize aBytesRead = theStream.gcount();
+  theBuffer->ChangeData()[aBytesRead < aBufferLength ? aBytesRead : aBufferLength - 1] = '\0';
+
+  // Clear any error flags (including EOF) BEFORE attempting to reset position
+  // This is essential because seekg() fails when EOF flag is set
+  theStream.clear();
+
+  // Reset stream to original position for subsequent reads
+  theStream.seekg(aOriginalPos);
+
+  return Standard_True;
+}
\ No newline at end of file
diff --git a/src/DataExchange/TKDE/DE/DE_ValidationUtils.hxx b/src/DataExchange/TKDE/DE/DE_ValidationUtils.hxx
new file mode 100644 (file)
index 0000000..b7f5b74
--- /dev/null
@@ -0,0 +1,118 @@
+// Copyright (c) 2025 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.
+
+#ifndef _DE_ValidationUtils_HeaderFile
+#define _DE_ValidationUtils_HeaderFile
+
+#include <TCollection_AsciiString.hxx>
+#include <DE_ConfigurationNode.hxx>
+#include <DE_Provider.hxx>
+
+class TDocStd_Document;
+
+//! Utility class providing static methods for common validation operations
+//! used across DataExchange providers. Includes validation for configuration nodes,
+//! file paths, streams, and other common scenarios with optional verbose error reporting.
+class DE_ValidationUtils
+{
+public:
+  //! Validates that configuration node is not null and matches expected type
+  //! @param[in] theNode configuration node to validate
+  //! @param[in] theExpectedType expected RTTI type
+  //! @param[in] theContext context string for error messages
+  //! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
+  //! @return Standard_True if node is valid, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean ValidateConfigurationNode(
+    const Handle(DE_ConfigurationNode)& theNode,
+    const Handle(Standard_Type)&        theExpectedType,
+    const TCollection_AsciiString&      theContext,
+    const Standard_Boolean              theIsVerbose = Standard_True);
+
+  //! Checks if file exists and is readable
+  //! @param[in] thePath file path to check
+  //! @param[in] theContext context string for error messages
+  //! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
+  //! @return Standard_True if file exists and is readable, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean ValidateFileForReading(
+    const TCollection_AsciiString& thePath,
+    const TCollection_AsciiString& theContext,
+    const Standard_Boolean         theIsVerbose = Standard_True);
+
+  //! Checks if file location is writable (file may or may not exist)
+  //! @param[in] thePath file path to check
+  //! @param[in] theContext context string for error messages
+  //! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
+  //! @return Standard_True if location is writable, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean ValidateFileForWriting(
+    const TCollection_AsciiString& thePath,
+    const TCollection_AsciiString& theContext,
+    const Standard_Boolean         theIsVerbose = Standard_True);
+
+  //! Validates read stream list, warns if multiple streams
+  //! @param[in] theStreams read stream list to validate
+  //! @param[in] theContext context string for error messages
+  //! @param[in] theIsVerbose if true, sends detailed error/warning messages
+  //! @return Standard_True if stream list is valid, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean ValidateReadStreamList(
+    const DE_Provider::ReadStreamList& theStreams,
+    const TCollection_AsciiString&     theContext,
+    const Standard_Boolean             theIsVerbose = Standard_True);
+
+  //! Validates write stream list, warns if multiple streams
+  //! @param[in] theStreams write stream list to validate
+  //! @param[in] theContext context string for error messages
+  //! @param[in] theIsVerbose if true, sends detailed error/warning messages
+  //! @return Standard_True if stream list is valid, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean ValidateWriteStreamList(
+    DE_Provider::WriteStreamList&  theStreams,
+    const TCollection_AsciiString& theContext,
+    const Standard_Boolean         theIsVerbose = Standard_True);
+
+  //! Validates that TDocStd_Document handle is not null
+  //! @param[in] theDocument document to validate
+  //! @param[in] theContext context string for error messages
+  //! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
+  //! @return Standard_True if document is not null, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean ValidateDocument(
+    const Handle(TDocStd_Document)& theDocument,
+    const TCollection_AsciiString&  theContext,
+    const Standard_Boolean          theIsVerbose = Standard_True);
+
+  //! Sends warning when format doesn't support length unit scaling
+  //! @param[in] theLengthUnit length unit value to check
+  //! @param[in] theContext context string for warning messages
+  //! @param[in] theIsVerbose if true, sends warning messages via Message::SendWarning
+  //! @return Standard_True always (this is just a warning)
+  Standard_EXPORT static Standard_Boolean WarnLengthUnitNotSupported(
+    const Standard_Real            theLengthUnit,
+    const TCollection_AsciiString& theContext,
+    const Standard_Boolean         theIsVerbose = Standard_True);
+
+  //! Creates buffer by reading from file stream for content checking
+  //! @param[in] thePath file path for reading
+  //! @param[out] theBuffer output buffer with file content
+  //! @return Standard_True if successful, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean CreateContentBuffer(
+    const TCollection_AsciiString& thePath,
+    Handle(NCollection_Buffer)&    theBuffer);
+
+  //! Creates buffer by reading from input stream for content checking
+  //! @param[in,out] theStream input stream to read from (position will be restored)
+  //! @param[out] theBuffer output buffer with stream content
+  //! @return Standard_True if successful, Standard_False otherwise
+  Standard_EXPORT static Standard_Boolean CreateContentBuffer(
+    std::istream&               theStream,
+    Handle(NCollection_Buffer)& theBuffer);
+};
+
+#endif // _DE_ValidationUtils_HeaderFile
\ No newline at end of file
index 0a40a57059e86a57c68e530789adf62dbd3b04c9..78997b5628b38d905fe5584e8ee635de64303b3d 100644 (file)
@@ -16,6 +16,7 @@
 #include <DE_ConfigurationContext.hxx>
 #include <DE_ConfigurationNode.hxx>
 #include <DE_Provider.hxx>
+#include <DE_ValidationUtils.hxx>
 #include <Message_ProgressRange.hxx>
 #include <NCollection_Buffer.hxx>
 #include <OSD_File.hxx>
@@ -499,19 +500,88 @@ Standard_Boolean DE_Wrapper::FindProvider(const TCollection_AsciiString& thePath
                                           const Standard_Boolean         theToImport,
                                           Handle(DE_Provider)&           theProvider) const
 {
-  Handle(NCollection_Buffer) aBuffer;
   if (theToImport)
   {
-    const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
-    std::shared_ptr<std::istream> aStream =
-      aFileSystem->OpenIStream(thePath, std::ios::in | std::ios::binary);
-    if (aStream.get() != nullptr)
+    return FindReadProvider(thePath, Standard_True, theProvider);
+  }
+  else
+  {
+    return FindWriteProvider(thePath, theProvider);
+  }
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::FindReadProvider(const TCollection_AsciiString& thePath,
+                                              const Standard_Boolean         theCheckContent,
+                                              Handle(DE_Provider)&           theProvider) const
+{
+  Handle(NCollection_Buffer) aBuffer;
+  if (theCheckContent && !DE_ValidationUtils::CreateContentBuffer(thePath, aBuffer))
+  {
+    return Standard_False;
+  }
+  OSD_Path                      aPath(thePath);
+  const TCollection_AsciiString anExtr = aPath.Extension();
+  for (DE_ConfigurationFormatMap::Iterator aFormatIter(myConfiguration); aFormatIter.More();
+       aFormatIter.Next())
+  {
+    for (DE_ConfigurationVendorMap::Iterator aVendorIter(aFormatIter.Value()); aVendorIter.More();
+         aVendorIter.Next())
     {
-      aBuffer = new NCollection_Buffer(NCollection_BaseAllocator::CommonBaseAllocator(), 2048);
-      aStream->read((char*)aBuffer->ChangeData(), 2048);
-      aBuffer->ChangeData()[2047] = '\0';
+      const Handle(DE_ConfigurationNode)& aNode = aVendorIter.Value();
+      if (aNode->IsEnabled() && aNode->IsImportSupported()
+          && (aNode->CheckExtension(anExtr) || (theCheckContent && aNode->CheckContent(aBuffer)))
+          && aNode->UpdateLoad(Standard_True, myKeepUpdates))
+      {
+        theProvider             = aNode->BuildProvider();
+        aNode->GlobalParameters = GlobalParameters;
+        return Standard_True;
+      }
     }
   }
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::FindReadProvider(const TCollection_AsciiString& thePath,
+                                              std::istream&                  theStream,
+                                              Handle(DE_Provider)&           theProvider) const
+{
+  Handle(NCollection_Buffer) aBuffer;
+  if (!DE_ValidationUtils::CreateContentBuffer(theStream, aBuffer))
+  {
+    return Standard_False;
+  }
+
+  OSD_Path                      aPath(thePath);
+  const TCollection_AsciiString anExtr = aPath.Extension();
+  for (DE_ConfigurationFormatMap::Iterator aFormatIter(myConfiguration); aFormatIter.More();
+       aFormatIter.Next())
+  {
+    for (DE_ConfigurationVendorMap::Iterator aVendorIter(aFormatIter.Value()); aVendorIter.More();
+         aVendorIter.Next())
+    {
+      const Handle(DE_ConfigurationNode)& aNode = aVendorIter.Value();
+      if (aNode->IsEnabled() && aNode->IsImportSupported()
+          && (aNode->CheckExtension(anExtr) || aNode->CheckContent(aBuffer))
+          && aNode->UpdateLoad(Standard_True, myKeepUpdates))
+      {
+        theProvider             = aNode->BuildProvider();
+        aNode->GlobalParameters = GlobalParameters;
+        return Standard_True;
+      }
+    }
+  }
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::FindWriteProvider(const TCollection_AsciiString& thePath,
+                                               Handle(DE_Provider)&           theProvider) const
+{
   OSD_Path                      aPath(thePath);
   const TCollection_AsciiString anExtr = aPath.Extension();
   for (DE_ConfigurationFormatMap::Iterator aFormatIter(myConfiguration); aFormatIter.More();
@@ -521,11 +591,8 @@ Standard_Boolean DE_Wrapper::FindProvider(const TCollection_AsciiString& thePath
          aVendorIter.Next())
     {
       const Handle(DE_ConfigurationNode)& aNode = aVendorIter.Value();
-      if (aNode->IsEnabled()
-          && ((theToImport && aNode->IsImportSupported())
-              || (!theToImport && aNode->IsExportSupported()))
-          && (aNode->CheckExtension(anExtr) || (theToImport && aNode->CheckContent(aBuffer)))
-          && aNode->UpdateLoad(theToImport, myKeepUpdates))
+      if (aNode->IsEnabled() && aNode->IsExportSupported() && aNode->CheckExtension(anExtr)
+          && aNode->UpdateLoad(Standard_False, myKeepUpdates))
       {
         theProvider             = aNode->BuildProvider();
         aNode->GlobalParameters = GlobalParameters;
@@ -572,3 +639,251 @@ void DE_Wrapper::sort(const Handle(DE_ConfigurationContext)& theResource)
     ChangePriority(aFormatIter.Key(), aVendorPriority, Standard_True);
   }
 }
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList&    theStreams,
+                                  const Handle(TDocStd_Document)& theDocument,
+                                  Handle(XSControl_WorkSession)&  theWS,
+                                  const Message_ProgressRange&    theProgress)
+{
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  Standard_IStream&   aFirstStream = theStreams.First().Stream;
+  if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Read(theStreams, theDocument, theWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList&   theStreams,
+                                   const Handle(TDocStd_Document)& theDocument,
+                                   Handle(XSControl_WorkSession)&  theWS,
+                                   const Message_ProgressRange&    theProgress)
+{
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  if (!FindWriteProvider(aFirstKey, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Write(theStreams, theDocument, theWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList&    theStreams,
+                                  const Handle(TDocStd_Document)& theDocument,
+                                  const Message_ProgressRange&    theProgress)
+{
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  Standard_IStream&   aFirstStream = theStreams.First().Stream;
+  if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Read(theStreams, theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList&   theStreams,
+                                   const Handle(TDocStd_Document)& theDocument,
+                                   const Message_ProgressRange&    theProgress)
+{
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  if (!FindWriteProvider(aFirstKey, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Write(theStreams, theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList&   theStreams,
+                                  TopoDS_Shape&                  theShape,
+                                  Handle(XSControl_WorkSession)& theWS,
+                                  const Message_ProgressRange&   theProgress)
+{
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  Standard_IStream&   aFirstStream = theStreams.First().Stream;
+  if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Read(theStreams, theShape, theWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList&  theStreams,
+                                   const TopoDS_Shape&            theShape,
+                                   Handle(XSControl_WorkSession)& theWS,
+                                   const Message_ProgressRange&   theProgress)
+{
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  if (!FindWriteProvider(aFirstKey, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Write(theStreams, theShape, theWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList& theStreams,
+                                  TopoDS_Shape&                theShape,
+                                  const Message_ProgressRange& theProgress)
+{
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  Standard_IStream&   aFirstStream = theStreams.First().Stream;
+  if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Read(theStreams, theShape, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList& theStreams,
+                                   const TopoDS_Shape&           theShape,
+                                   const Message_ProgressRange&  theProgress)
+{
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+
+  Handle(DE_Provider) aProvider;
+  if (!FindWriteProvider(aFirstKey, aProvider))
+  {
+    Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  if (!aProvider->GetNode()->IsStreamSupported())
+  {
+    Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
+                        << aProvider->GetVendor() << " doesn't support stream operations";
+    return Standard_False;
+  }
+
+  return aProvider->Write(theStreams, theShape, theProgress);
+}
index b54fa357b7353a38341a8f47298202cb3b997796..a159adbe8d4fe23386b7e60d60c26a71c006c852 100644 (file)
@@ -15,6 +15,7 @@
 #define _DE_Wrapper_HeaderFile
 
 #include <DE_ConfigurationNode.hxx>
+#include <DE_Provider.hxx>
 #include <Message_ProgressRange.hxx>
 #include <NCollection_DataMap.hxx>
 #include <NCollection_IndexedDataMap.hxx>
@@ -163,6 +164,94 @@ public:
           const TopoDS_Shape&            theShape,
           const Message_ProgressRange&   theProgress = Message_ProgressRange());
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Read(DE_Provider::ReadStreamList&    theStreams,
+         const Handle(TDocStd_Document)& theDocument,
+         Handle(XSControl_WorkSession)&  theWS,
+         const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Write(DE_Provider::WriteStreamList&   theStreams,
+          const Handle(TDocStd_Document)& theDocument,
+          Handle(XSControl_WorkSession)&  theWS,
+          const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Read(DE_Provider::ReadStreamList&    theStreams,
+         const Handle(TDocStd_Document)& theDocument,
+         const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Write(DE_Provider::WriteStreamList&   theStreams,
+          const Handle(TDocStd_Document)& theDocument,
+          const Message_ProgressRange&    theProgress = Message_ProgressRange());
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Read(DE_Provider::ReadStreamList&   theStreams,
+         TopoDS_Shape&                  theShape,
+         Handle(XSControl_WorkSession)& theWS,
+         const Message_ProgressRange&   theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Write(DE_Provider::WriteStreamList&  theStreams,
+          const TopoDS_Shape&            theShape,
+          Handle(XSControl_WorkSession)& theWS,
+          const Message_ProgressRange&   theProgress = Message_ProgressRange());
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Read(DE_Provider::ReadStreamList& theStreams,
+         TopoDS_Shape&                theShape,
+         const Message_ProgressRange& theProgress = Message_ProgressRange());
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT Standard_Boolean
+    Write(DE_Provider::WriteStreamList& theStreams,
+          const TopoDS_Shape&           theShape,
+          const Message_ProgressRange&  theProgress = Message_ProgressRange());
+
 public:
   //! Updates values according the resource file
   //! @param[in] theResource file path to resource or resource value
@@ -243,6 +332,35 @@ public:
                                                         const Standard_Boolean         theToImport,
                                                         Handle(DE_Provider)& theProvider) const;
 
+  //! Find available read provider from the configuration for file-based operations.
+  //! If there are several providers, choose the one with the highest priority.
+  //! @param[in] thePath path to the CAD file (for extension and content checking)
+  //! @param[in] theCheckContent flag to enable content checking via file reading
+  //! @param[out] theProvider created new provider
+  //! @return Standard_True if provider found and created
+  Standard_EXPORT virtual Standard_Boolean FindReadProvider(const TCollection_AsciiString& thePath,
+                                                            const Standard_Boolean theCheckContent,
+                                                            Handle(DE_Provider)& theProvider) const;
+
+  //! Find available read provider from the configuration for stream-based operations.
+  //! If there are several providers, choose the one with the highest priority.
+  //! @param[in] thePath path to the CAD file (for extension extraction)
+  //! @param[in] theStream input stream for content checking
+  //! @param[out] theProvider created new provider
+  //! @return Standard_True if provider found and created
+  Standard_EXPORT virtual Standard_Boolean FindReadProvider(const TCollection_AsciiString& thePath,
+                                                            std::istream&        theStream,
+                                                            Handle(DE_Provider)& theProvider) const;
+
+  //! Find available write provider from the configuration.
+  //! If there are several providers, choose the one with the highest priority.
+  //! @param[in] thePath path to the CAD file (for extension checking only)
+  //! @param[out] theProvider created new provider
+  //! @return Standard_True if provider found and created
+  Standard_EXPORT virtual Standard_Boolean FindWriteProvider(
+    const TCollection_AsciiString& thePath,
+    Handle(DE_Provider)&           theProvider) const;
+
   //! Updates all registered nodes, all changes will be saved in nodes
   //! @param[in] theToForceUpdate flag that turns on/of nodes, according to updated ability to
   //! import/export
index 93721bfed7be2dcd732f4dfe2158491403e07dca..de873dd81859302bdb97fcf01bdee58a4fab901a 100644 (file)
@@ -12,6 +12,8 @@ set(OCCT_DE_FILES
   DE_ShapeFixConfigurationNode.cxx
   DE_ShapeFixConfigurationNode.hxx
   DE_ShapeFixParameters.hxx
+  DE_ValidationUtils.cxx
+  DE_ValidationUtils.hxx
   DE_Wrapper.cxx
   DE_Wrapper.hxx
 )
index 84c782b181ad203ead81ed117e6cf75bf1d169c4..f430bec339344c9acecaf2f40e697517fc022ee3 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <DEGLTF_Provider.hxx>
 
+#include <DE_ValidationUtils.hxx>
 #include <Message.hxx>
 #include <RWGltf_CafWriter.hxx>
 #include <TDocStd_Document.hxx>
@@ -86,17 +87,15 @@ bool DEGLTF_Provider::Read(const TCollection_AsciiString&  thePath,
                            const Handle(TDocStd_Document)& theDocument,
                            const Message_ProgressRange&    theProgress)
 {
-  if (theDocument.IsNull())
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
   {
-    Message::SendFail() << "Error in the DEGLTF_Provider during reading the file " << thePath
-                        << "\t: theDocument shouldn't be null";
     return false;
   }
-  if (GetNode().IsNull()
-      || (!GetNode().IsNull() && !GetNode()->IsKind(STANDARD_TYPE(DEGLTF_ConfigurationNode))))
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DEGLTF_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendFail() << "Error in the DEGLTF_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DEGLTF_ConfigurationNode) aNode = Handle(DEGLTF_ConfigurationNode)::DownCast(GetNode());
@@ -121,10 +120,11 @@ bool DEGLTF_Provider::Write(const TCollection_AsciiString&  thePath,
                             const Handle(TDocStd_Document)& theDocument,
                             const Message_ProgressRange&    theProgress)
 {
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEGLTF_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DEGLTF_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendFail() << "Error in the DEGLTF_Provider during writing the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DEGLTF_ConfigurationNode) aNode = Handle(DEGLTF_ConfigurationNode)::DownCast(GetNode());
@@ -141,9 +141,11 @@ bool DEGLTF_Provider::Write(const TCollection_AsciiString&  thePath,
   aConverter.SetInputCoordinateSystem(aNode->InternalParameters.SystemCS);
   if (aNode->GlobalParameters.LengthUnit != 1000.)
   {
+    TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
     Message::SendWarning()
-      << "Warning in the DEGLTF_Provider during writing the file " << thePath
-      << "\t: Target format doesn't support custom units. Model will be scaled to Meters";
+      << "Warning during " << aContext
+      << ": Target format doesn't support custom units. Model will be scaled to Meters (unit: "
+      << aNode->GlobalParameters.LengthUnit << ")";
   }
   aConverter.SetOutputLengthUnit(1.); // gltf units always Meters
   aConverter.SetOutputCoordinateSystem(aNode->InternalParameters.FileCS);
index 748a66730c56832f568e8916fc981c5546b2cdcd..aa57e92f46a421ea682767aa3a9dad0c27c2eb73 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <DEIGES_Provider.hxx>
 
+#include <DE_ValidationUtils.hxx>
 #include <DEIGES_ConfigurationNode.hxx>
 #include <IGESCAFControl_Reader.hxx>
 #include <IGESCAFControl_Writer.hxx>
 
 IMPLEMENT_STANDARD_RTTIEXT(DEIGES_Provider, DE_Provider)
 
+namespace
+{
+// Helper function to validate configuration node
+Standard_Boolean validateConfigurationNode(const Handle(DE_ConfigurationNode)& theNode,
+                                           const TCollection_AsciiString&      theContext)
+{
+  return DE_ValidationUtils::ValidateConfigurationNode(theNode,
+                                                       STANDARD_TYPE(DEIGES_ConfigurationNode),
+                                                       theContext);
+}
+
+// Helper function to configure IGES CAF reader parameters
+void configureIGESCAFReader(IGESCAFControl_Reader&                  theReader,
+                            const Handle(DEIGES_ConfigurationNode)& theNode)
+{
+  theReader.SetReadVisible(theNode->InternalParameters.ReadOnlyVisible);
+  theReader.SetColorMode(theNode->InternalParameters.ReadColor);
+  theReader.SetNameMode(theNode->InternalParameters.ReadName);
+  theReader.SetLayerMode(theNode->InternalParameters.ReadLayer);
+  theReader.SetShapeFixParameters(theNode->ShapeFixParameters);
+}
+
+// Helper function to configure IGES control reader parameters
+void configureIGESControlReader(IGESControl_Reader&                     theReader,
+                                const Handle(DEIGES_ConfigurationNode)& theNode)
+{
+  theReader.SetReadVisible(theNode->InternalParameters.ReadOnlyVisible);
+  theReader.SetShapeFixParameters(theNode->ShapeFixParameters);
+}
+
+// Helper function to setup IGES unit configuration
+void setupIGESUnits(IGESData_GlobalSection&                 theGS,
+                    const Handle(DEIGES_ConfigurationNode)& theNode,
+                    const Handle(TDocStd_Document)&         theDocument,
+                    const TCollection_AsciiString&          thePath,
+                    Standard_Boolean                        theUseDocumentUnits)
+{
+  Standard_Integer aFlag =
+    IGESData_BasicEditor::GetFlagByValue(theNode->GlobalParameters.LengthUnit);
+
+  if (theUseDocumentUnits && !theDocument.IsNull())
+  {
+    Standard_Real    aScaleFactorMM = 1.;
+    Standard_Boolean aHasUnits =
+      XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
+                                          aScaleFactorMM,
+                                          UnitsMethods_LengthUnit_Millimeter);
+    if (aHasUnits)
+    {
+      theGS.SetCascadeUnit(aScaleFactorMM);
+    }
+    else
+    {
+      theGS.SetCascadeUnit(theNode->GlobalParameters.SystemUnit);
+      Message::SendWarning()
+        << "Warning in the DEIGES_Provider during writing the file " << thePath
+        << "\t: The document has no information on Units. Using global parameter as initial Unit.";
+    }
+  }
+  else
+  {
+    theGS.SetCascadeUnit(theNode->GlobalParameters.SystemUnit);
+  }
+
+  if (aFlag == 0)
+  {
+    theGS.SetScale(theNode->GlobalParameters.LengthUnit);
+  }
+}
+
+// Helper function to configure IGES CAF writer parameters
+void configureIGESCAFWriter(IGESCAFControl_Writer&                  theWriter,
+                            const Handle(DEIGES_ConfigurationNode)& theNode,
+                            const Handle(TDocStd_Document)&         theDocument,
+                            const TCollection_AsciiString&          thePath)
+{
+  IGESData_GlobalSection aGS = theWriter.Model()->GlobalSection();
+  setupIGESUnits(aGS, theNode, theDocument, thePath, Standard_True);
+
+  theWriter.Model()->SetGlobalSection(aGS);
+  theWriter.SetColorMode(theNode->InternalParameters.WriteColor);
+  theWriter.SetNameMode(theNode->InternalParameters.WriteName);
+  theWriter.SetLayerMode(theNode->InternalParameters.WriteLayer);
+  theWriter.SetShapeFixParameters(theNode->ShapeFixParameters);
+}
+
+// Helper function to configure IGES control writer for shapes
+void configureIGESControlWriter(IGESControl_Writer&                     theWriter,
+                                const Handle(DEIGES_ConfigurationNode)& theNode)
+{
+  IGESData_GlobalSection   aGS = theWriter.Model()->GlobalSection();
+  Handle(TDocStd_Document) aNullDoc;
+  setupIGESUnits(aGS, theNode, aNullDoc, "", Standard_False);
+
+  theWriter.Model()->SetGlobalSection(aGS);
+  theWriter.SetShapeFixParameters(theNode->ShapeFixParameters);
+}
+
+// Helper function to setup IGES writer unit flags
+TCollection_AsciiString getIGESUnitString(const Handle(DEIGES_ConfigurationNode)& theNode)
+{
+  Standard_Integer aFlag =
+    IGESData_BasicEditor::GetFlagByValue(theNode->GlobalParameters.LengthUnit);
+  return (aFlag > 0) ? IGESData_BasicEditor::UnitFlagName(aFlag) : "MM";
+}
+
+// Helper function to process read file operation
+Standard_Boolean processReadFile(IGESControl_Reader&            theReader,
+                                 const TCollection_AsciiString& thePath)
+{
+  IFSelect_ReturnStatus aReadStat = theReader.ReadFile(thePath.ToCString());
+  if (aReadStat != IFSelect_RetDone)
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
+                        << "\t: abandon, no model loaded";
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+} // namespace
+
 //=================================================================================================
 
 DEIGES_Provider::DEIGES_Provider() {}
@@ -160,35 +283,26 @@ bool DEIGES_Provider::Read(const TCollection_AsciiString&  thePath,
                            Handle(XSControl_WorkSession)&  theWS,
                            const Message_ProgressRange&    theProgress)
 {
-  if (theDocument.IsNull())
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
+      || !validateConfigurationNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
-                        << "\t: theDocument shouldn't be null";
-    return false;
-  }
-  if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
-  {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
+
   Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
   personizeWS(theWS);
   initStatic(aNode);
+
   XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
                                       aNode->GlobalParameters.LengthUnit,
                                       UnitsMethods_LengthUnit_Millimeter);
+
   IGESCAFControl_Reader aReader;
   aReader.SetWS(theWS);
+  configureIGESCAFReader(aReader, aNode);
 
-  aReader.SetReadVisible(aNode->InternalParameters.ReadOnlyVisible);
-
-  aReader.SetColorMode(aNode->InternalParameters.ReadColor);
-  aReader.SetNameMode(aNode->InternalParameters.ReadName);
-  aReader.SetLayerMode(aNode->InternalParameters.ReadLayer);
-  aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
-  IFSelect_ReturnStatus aReadStat = IFSelect_RetVoid;
-  aReadStat                       = aReader.ReadFile(thePath.ToCString());
+  IFSelect_ReturnStatus aReadStat = aReader.ReadFile(thePath.ToCString());
   if (aReadStat != IFSelect_RetDone)
   {
     Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
@@ -215,54 +329,30 @@ bool DEIGES_Provider::Write(const TCollection_AsciiString&  thePath,
                             Handle(XSControl_WorkSession)&  theWS,
                             const Message_ProgressRange&    theProgress)
 {
-  if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
+      || !validateConfigurationNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
+
   Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
   personizeWS(theWS);
   initStatic(aNode);
-  Standard_Integer aFlag = IGESData_BasicEditor::GetFlagByValue(aNode->GlobalParameters.LengthUnit);
-  IGESCAFControl_Writer  aWriter(theWS,
-                                (aFlag > 0) ? IGESData_BasicEditor::UnitFlagName(aFlag) : "MM");
-  IGESData_GlobalSection aGS            = aWriter.Model()->GlobalSection();
-  Standard_Real          aScaleFactorMM = 1.;
-  Standard_Boolean       aHasUnits =
-    XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
-                                        aScaleFactorMM,
-                                        UnitsMethods_LengthUnit_Millimeter);
-  if (aHasUnits)
-  {
-    aGS.SetCascadeUnit(aScaleFactorMM);
-  }
-  else
-  {
-    aGS.SetCascadeUnit(aNode->GlobalParameters.SystemUnit);
-    Message::SendWarning()
-      << "Warning in the DEIGES_Provider during writing the file " << thePath
-      << "\t: The document has no information on Units. Using global parameter as initial Unit.";
-  }
-  if (aFlag == 0)
-  {
-    aGS.SetScale(aNode->GlobalParameters.LengthUnit);
-  }
-  aWriter.Model()->SetGlobalSection(aGS);
-  aWriter.SetColorMode(aNode->InternalParameters.WriteColor);
-  aWriter.SetNameMode(aNode->InternalParameters.WriteName);
-  aWriter.SetLayerMode(aNode->InternalParameters.WriteLayer);
-  aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
+
+  IGESCAFControl_Writer aWriter(theWS, Standard_False);
+  configureIGESCAFWriter(aWriter, aNode, theDocument, thePath);
+
   if (!aWriter.Transfer(theDocument, theProgress))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
+    Message::SendFail() << "Error in the DEIGES_Provider during writing the file " << thePath
                         << "\t: The document cannot be translated or gives no result";
     resetStatic();
     return false;
   }
   if (!aWriter.Write(thePath.ToCString()))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
+    Message::SendFail() << "Error in the DEIGES_Provider during writing the file " << thePath
                         << "\t: Write failed";
     resetStatic();
     return false;
@@ -298,31 +388,26 @@ bool DEIGES_Provider::Read(const TCollection_AsciiString& thePath,
                            Handle(XSControl_WorkSession)& theWS,
                            const Message_ProgressRange&   theProgress)
 {
-  (void)theProgress;
-  if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
+  if (!validateConfigurationNode(GetNode(), TCollection_AsciiString("reading the file ") + thePath))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
+
   Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
   initStatic(aNode);
   personizeWS(theWS);
+
   IGESControl_Reader aReader;
   aReader.SetWS(theWS);
-  aReader.SetReadVisible(aNode->InternalParameters.ReadOnlyVisible);
-  aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
+  configureIGESControlReader(aReader, aNode);
 
-  IFSelect_ReturnStatus aReadStat = IFSelect_RetVoid;
-  aReadStat                       = aReader.ReadFile(thePath.ToCString());
-  if (aReadStat != IFSelect_RetDone)
+  if (!processReadFile(aReader, thePath))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
-                        << "\t: Could not read file, no model loaded";
     resetStatic();
     return false;
   }
-  if (aReader.TransferRoots() <= 0)
+
+  if (aReader.TransferRoots(theProgress) <= 0)
   {
     Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
                         << "\t: Cannot read any relevant data from the IGES file";
@@ -341,28 +426,21 @@ bool DEIGES_Provider::Write(const TCollection_AsciiString& thePath,
                             Handle(XSControl_WorkSession)& theWS,
                             const Message_ProgressRange&   theProgress)
 {
-  (void)theWS;
-  (void)theProgress;
-  if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!validateConfigurationNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
+
   Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
   initStatic(aNode);
-  Standard_Integer aFlag = IGESData_BasicEditor::GetFlagByValue(aNode->GlobalParameters.LengthUnit);
-  IGESControl_Writer aWriter((aFlag > 0) ? IGESData_BasicEditor::UnitFlagName(aFlag) : "MM",
+  personizeWS(theWS);
+
+  IGESControl_Writer aWriter(getIGESUnitString(aNode).ToCString(),
                              aNode->InternalParameters.WriteBRepMode);
-  IGESData_GlobalSection aGS = aWriter.Model()->GlobalSection();
-  aGS.SetCascadeUnit(aNode->GlobalParameters.SystemUnit);
-  if (!aFlag)
-  {
-    aGS.SetScale(aNode->GlobalParameters.LengthUnit);
-  }
-  aWriter.Model()->SetGlobalSection(aGS);
-  aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
-  Standard_Boolean aIsOk = aWriter.AddShape(theShape);
+  configureIGESControlWriter(aWriter, aNode);
+
+  Standard_Boolean aIsOk = aWriter.AddShape(theShape, theProgress);
   if (!aIsOk)
   {
     Message::SendFail() << "DEIGES_Provider: Shape not written";
@@ -370,7 +448,7 @@ bool DEIGES_Provider::Write(const TCollection_AsciiString& thePath,
     return false;
   }
 
-  if (!(aWriter.Write(thePath.ToCString())))
+  if (!aWriter.Write(thePath.ToCString()))
   {
     Message::SendFail() << "DEIGES_Provider: Error on writing file " << thePath;
     resetStatic();
@@ -413,3 +491,253 @@ TCollection_AsciiString DEIGES_Provider::GetVendor() const
 {
   return TCollection_AsciiString("OCC");
 }
+
+/*
+
+// TODO: Implement IGES stream support
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Read(ReadStreamList&                  theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       Handle(XSControl_WorkSession)&  theWS,
+                                       const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString        aFullContext = aContext + " " + aFirstKey;
+  Standard_IStream&              aStream      = theStreams.First().Stream;
+
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
+      || !validateConfigurationNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
+  initStatic(aNode);
+  personizeWS(theWS);
+
+  XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
+                                      aNode->GlobalParameters.LengthUnit,
+                                      UnitsMethods_LengthUnit_Millimeter);
+
+  IGESCAFControl_Reader aReader;
+  aReader.SetWS(theWS);
+  configureIGESCAFReader(aReader, aNode);
+
+  IFSelect_ReturnStatus aReadStat = aReader.ReadStream(aFirstKey.ToCString(), aStream);
+  if (aReadStat != IFSelect_RetDone)
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
+                        << "\t: abandon, no model loaded";
+    resetStatic();
+    return Standard_False;
+  }
+
+  if (!aReader.Transfer(theDocument, theProgress))
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
+                        << "\t: Cannot read any relevant data from the IGES stream";
+    resetStatic();
+    return Standard_False;
+  }
+  resetStatic();
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Write(WriteStreamList&                 theStreams,
+                                        const Handle(TDocStd_Document)& theDocument,
+                                        Handle(XSControl_WorkSession)&  theWS,
+                                        const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+  Standard_OStream&              aStream   = theStreams.First().Stream;
+
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
+      || !validateConfigurationNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
+  initStatic(aNode);
+  personizeWS(theWS);
+
+  IGESCAFControl_Writer aWriter(theWS, Standard_False);
+  configureIGESCAFWriter(aWriter, aNode, theDocument, aFirstKey);
+
+  if (!aWriter.Transfer(theDocument, theProgress))
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during writing stream " << aFirstKey
+                        << "\t: The document cannot be translated or gives no result";
+    resetStatic();
+    return Standard_False;
+  }
+
+  if (!aWriter.Write(aStream))
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during writing stream " << aFirstKey
+                        << "\t: Write failed";
+    resetStatic();
+    return Standard_False;
+  }
+  resetStatic();
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Read(ReadStreamList&                 theStreams,
+                                       TopoDS_Shape&                  theShape,
+                                       Handle(XSControl_WorkSession)& theWS,
+                                       const Message_ProgressRange&   theProgress)
+{
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+  Standard_IStream&              aStream   = theStreams.First().Stream;
+
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!validateConfigurationNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
+  initStatic(aNode);
+  personizeWS(theWS);
+
+  IGESControl_Reader aReader;
+  aReader.SetWS(theWS);
+  configureIGESControlReader(aReader, aNode);
+
+  IFSelect_ReturnStatus aReadStat = aReader.ReadStream(aFirstKey.ToCString(), aStream);
+  if (aReadStat != IFSelect_RetDone)
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
+                        << "\t: Could not read stream, no model loaded";
+    resetStatic();
+    return Standard_False;
+  }
+
+  if (aReader.TransferRoots(theProgress) <= 0)
+  {
+    Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
+                        << "\t: Cannot read any relevant data from the IGES stream";
+    resetStatic();
+    return Standard_False;
+  }
+  theShape = aReader.OneShape();
+  resetStatic();
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Write(WriteStreamList&                theStreams,
+                                        const TopoDS_Shape&            theShape,
+                                        Handle(XSControl_WorkSession)& theWS,
+                                        const Message_ProgressRange&   theProgress)
+{
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+  Standard_OStream&              aStream   = theStreams.First().Stream;
+
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!validateConfigurationNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
+  initStatic(aNode);
+  personizeWS(theWS);
+
+  IGESControl_Writer aWriter(getIGESUnitString(aNode).ToCString(),
+                             aNode->InternalParameters.WriteBRepMode);
+  configureIGESControlWriter(aWriter, aNode);
+
+  Standard_Boolean isOk = aWriter.AddShape(theShape, theProgress);
+  if (!isOk)
+  {
+    Message::SendFail() << "Error: DEIGES_Provider failed to transfer shape for stream "
+                        << aFirstKey;
+    resetStatic();
+    return Standard_False;
+  }
+
+  if (!aWriter.Write(aStream))
+  {
+    Message::SendFail() << "Error: DEIGES_Provider failed to write shape to stream " << aFirstKey;
+    resetStatic();
+    return Standard_False;
+  }
+  resetStatic();
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Read(ReadStreamList&                  theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       const Message_ProgressRange&    theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Read(theStreams, theDocument, aWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Write(WriteStreamList&                 theStreams,
+                                        const Handle(TDocStd_Document)& theDocument,
+                                        const Message_ProgressRange&    theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Write(theStreams, theDocument, aWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Read(ReadStreamList&               theStreams,
+                                       TopoDS_Shape&                theShape,
+                                       const Message_ProgressRange& theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Read(theStreams, theShape, aWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEIGES_Provider::Write(WriteStreamList&              theStreams,
+                                        const TopoDS_Shape&          theShape,
+                                        const Message_ProgressRange& theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Write(theStreams, theShape, aWS, theProgress);
+}
+
+*/
index 0660c5f24a18ec682b57ad5d72d20ecb829ff135..b90f7b2d68e89aeac7f5ef4585bd89c23460331b 100644 (file)
@@ -14,6 +14,7 @@
 #include <DEOBJ_Provider.hxx>
 
 #include <BRep_Builder.hxx>
+#include <DE_ValidationUtils.hxx>
 #include <DEOBJ_ConfigurationNode.hxx>
 #include <RWObj_CafReader.hxx>
 #include <RWObj_CafWriter.hxx>
@@ -62,16 +63,15 @@ bool DEOBJ_Provider::Read(const TCollection_AsciiString&  thePath,
                           const Handle(TDocStd_Document)& theDocument,
                           const Message_ProgressRange&    theProgress)
 {
-  if (theDocument.IsNull())
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
   {
-    Message::SendFail() << "Error in the DEOBJ_Provider during reading the file " << thePath
-                        << "\t: theDocument shouldn't be null";
     return false;
   }
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEOBJ_ConfigurationNode)))
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DEOBJ_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendFail() << "Error in the DEOBJ_ConfigurationNode during reading the file "
-                        << thePath << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DEOBJ_ConfigurationNode) aNode = Handle(DEOBJ_ConfigurationNode)::DownCast(GetNode());
index 7317c3b6bfb7708782cf4c059f49e3e4834cde15..93bba3cefe51642396acf8b4a161f115873dcac7 100644 (file)
@@ -14,6 +14,7 @@
 #include <DEPLY_Provider.hxx>
 
 #include <BRep_Builder.hxx>
+#include <DE_ValidationUtils.hxx>
 #include <DEPLY_ConfigurationNode.hxx>
 #include <DE_Wrapper.hxx>
 #include <Message.hxx>
@@ -55,10 +56,11 @@ bool DEPLY_Provider::Write(const TCollection_AsciiString&  thePath,
                            const Handle(TDocStd_Document)& theDocument,
                            const Message_ProgressRange&    theProgress)
 {
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEPLY_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DEPLY_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendFail() << "Error in the DEPLY_Provider during writing the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DEPLY_ConfigurationNode) aNode = Handle(DEPLY_ConfigurationNode)::DownCast(GetNode());
index b35be0c85e1a6f573316b7c1a131445dac2ed27d..be25e53a7986f5c83dcc2501f3130149ef1ff134 100644 (file)
@@ -635,6 +635,13 @@ bool DESTEP_ConfigurationNode::IsExportSupported() const
 
 //=================================================================================================
 
+bool DESTEP_ConfigurationNode::IsStreamSupported() const
+{
+  return true;
+}
+
+//=================================================================================================
+
 TCollection_AsciiString DESTEP_ConfigurationNode::GetFormat() const
 {
   return TCollection_AsciiString("STEP");
index 338ea06739d809a603e8bd6ec02a4bcb751d8d90..93e22f19ea7483124df9f72d7bfa4de156859914 100644 (file)
@@ -68,6 +68,10 @@ public:
   //! @return true if export is supported
   Standard_EXPORT virtual bool IsExportSupported() const Standard_OVERRIDE;
 
+  //! Checks for stream support.
+  //! @return Standard_True if streams are supported
+  Standard_EXPORT virtual bool IsStreamSupported() const Standard_OVERRIDE;
+
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
   Standard_EXPORT virtual TCollection_AsciiString GetFormat() const Standard_OVERRIDE;
index f3d04175549f0e3ba390a9307c63c0e672320d56..06e5cd3c0a7f1f7a852fc1c5638989694c2779a8 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <DESTEP_Provider.hxx>
 
+#include <DE_ValidationUtils.hxx>
 #include <DESTEP_ConfigurationNode.hxx>
 #include <DESTEP_Parameters.hxx>
 #include <Interface_Static.hxx>
 #include <STEPCAFControl_Controller.hxx>
 #include <STEPCAFControl_Reader.hxx>
 #include <STEPCAFControl_Writer.hxx>
+#include <STEPControl_Reader.hxx>
+#include <STEPControl_Writer.hxx>
 #include <StepData_StepModel.hxx>
 #include <UnitsMethods.hxx>
 #include <XCAFDoc_DocumentTool.hxx>
 #include <XSControl_WorkSession.hxx>
+#include <OSD_OpenFile.hxx>
+#include <fstream>
 
 IMPLEMENT_STANDARD_RTTIEXT(DESTEP_Provider, DE_Provider)
 
+namespace
+{
+//! Helper function to validate configuration node
+Standard_Boolean validateNode(const Handle(DE_ConfigurationNode)& theNode,
+                              const TCollection_AsciiString&      theContext)
+{
+  return DE_ValidationUtils::ValidateConfigurationNode(theNode,
+                                                       STANDARD_TYPE(DESTEP_ConfigurationNode),
+                                                       theContext);
+}
+
+//! Configures STEPCAFControl_Reader with specified parameters and optional document setup.
+//! @param[in,out] theReader STEP CAF reader to configure
+//! @param[in] theParams Parameters containing read settings
+//! @param[in] theWS Work session to initialize reader with (optional, if provided reader will
+//! be initialized)
+//! @param[in] theDocument Target document for length unit setup (optional)
+//! @param[in] theLengthUnit Length unit for document setup (used only if theDocument is provided)
+//! @param[in] theShapeFixParams Shape fix parameters (optional, uses default if not provided)
+//! @note Sets up colors, names, layers, properties, metadata, and shape fix parameters
+void configureSTEPCAFReader(STEPCAFControl_Reader&          theReader,
+                            const DESTEP_Parameters&        theParams,
+                            Handle(XSControl_WorkSession)&  theWS,
+                            const Handle(TDocStd_Document)& theDocument,
+                            Standard_Real                   theLengthUnit,
+                            const DE_ShapeFixParameters&    theShapeFixParams)
+{
+  theReader.Init(theWS);
+
+  theReader.SetColorMode(theParams.ReadColor);
+  theReader.SetNameMode(theParams.ReadName);
+  theReader.SetLayerMode(theParams.ReadLayer);
+  theReader.SetPropsMode(theParams.ReadProps);
+  theReader.SetMetaMode(theParams.ReadMetadata);
+  theReader.SetProductMetaMode(theParams.ReadProductMetadata);
+
+  theReader.SetShapeFixParameters(theShapeFixParams);
+
+  XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
+                                      theLengthUnit,
+                                      UnitsMethods_LengthUnit_Millimeter);
+}
+
+//! Configures STEPCAFControl_Writer with full setup.
+//! @param[in,out] theWriter STEP CAF writer to configure
+//! @param[in] theParams Parameters containing write settings
+//! @param[in,out] theWS Work session to initialize writer with
+//! @param[in] theDocument Source document for length unit extraction
+//! @param[in] theLengthUnit Length unit for document setup
+//! @param[in] theShapeFixParams Shape fix parameters
+//! @note Sets up all write parameters including colors, names, layers, props, materials
+void configureSTEPCAFWriter(STEPCAFControl_Writer&          theWriter,
+                            const DESTEP_Parameters&        theParams,
+                            Handle(XSControl_WorkSession)&  theWS,
+                            const Handle(TDocStd_Document)& theDocument,
+                            Standard_Real                   theLengthUnit,
+                            const DE_ShapeFixParameters&    theShapeFixParams)
+{
+  theWriter.Init(theWS);
+
+  theWriter.SetColorMode(theParams.WriteColor);
+  theWriter.SetNameMode(theParams.WriteName);
+  theWriter.SetLayerMode(theParams.WriteLayer);
+  theWriter.SetPropsMode(theParams.WriteProps);
+  theWriter.SetMaterialMode(theParams.WriteMaterial);
+  theWriter.SetVisualMaterialMode(theParams.WriteVisMaterial);
+  theWriter.SetCleanDuplicates(theParams.CleanDuplicates);
+
+  theWriter.SetShapeFixParameters(theShapeFixParams);
+
+  Handle(StepData_StepModel) aModel =
+    Handle(StepData_StepModel)::DownCast(theWriter.Writer().WS()->Model());
+
+  Standard_Real aScaleFactorMM = 1.;
+  if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
+                                          aScaleFactorMM,
+                                          UnitsMethods_LengthUnit_Millimeter))
+  {
+    aModel->SetLocalLengthUnit(aScaleFactorMM);
+  }
+  else
+  {
+    aModel->SetLocalLengthUnit(theLengthUnit);
+    Message::SendWarning()
+      << "Warning in the DESTEP_Provider during writing"
+      << "\t: The document has no information on Units. Using global parameter as initial Unit.";
+  }
+}
+
+//! Checks if output stream is in writable state.
+//! @param[in] theStream Output stream to check
+//! @param[in] theKey Stream identifier for error reporting
+//! @return Standard_True if stream is writable, Standard_False otherwise
+bool checkStreamWritability(Standard_OStream& theStream, const TCollection_AsciiString& theKey)
+{
+  if (!theStream.good())
+  {
+    TCollection_AsciiString aKeyInfo = theKey.IsEmpty() ? "<empty key>" : theKey;
+    Message::SendFail() << "Error: Output stream '" << aKeyInfo
+                        << "' is not in good state for writing";
+    return false;
+  }
+
+  return true;
+}
+
+} // namespace
+
 //=================================================================================================
 
 DESTEP_Provider::DESTEP_Provider() {}
@@ -45,35 +158,28 @@ bool DESTEP_Provider::Read(const TCollection_AsciiString&  thePath,
                            Handle(XSControl_WorkSession)&  theWS,
                            const Message_ProgressRange&    theProgress)
 {
-  if (theDocument.IsNull())
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
+      || !validateNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
-                        << "\t: theDocument shouldn't be null";
-    return false;
-  }
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
-  {
-    Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
+
   Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
   personizeWS(theWS);
-  XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
-                                      aNode->GlobalParameters.LengthUnit,
-                                      UnitsMethods_LengthUnit_Millimeter);
+
   STEPCAFControl_Reader aReader;
-  aReader.Init(theWS);
-  aReader.SetColorMode(aNode->InternalParameters.ReadColor);
-  aReader.SetNameMode(aNode->InternalParameters.ReadName);
-  aReader.SetLayerMode(aNode->InternalParameters.ReadLayer);
-  aReader.SetPropsMode(aNode->InternalParameters.ReadProps);
-  aReader.SetMetaMode(aNode->InternalParameters.ReadMetadata);
-  aReader.SetProductMetaMode(aNode->InternalParameters.ReadProductMetadata);
-  aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
+  configureSTEPCAFReader(aReader,
+                         aNode->InternalParameters,
+                         theWS,
+                         theDocument,
+                         aNode->GlobalParameters.LengthUnit,
+                         aNode->ShapeFixParameters);
+
   IFSelect_ReturnStatus aReadStat = IFSelect_RetVoid;
   DESTEP_Parameters     aParams   = aNode->InternalParameters;
   aReadStat                       = aReader.ReadFile(thePath.ToCString(), aParams);
+
   if (aReadStat != IFSelect_RetDone)
   {
     Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
@@ -97,43 +203,28 @@ bool DESTEP_Provider::Write(const TCollection_AsciiString&  thePath,
                             Handle(XSControl_WorkSession)&  theWS,
                             const Message_ProgressRange&    theProgress)
 {
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
+      || !validateNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DESTEP_Provider during writing the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
   personizeWS(theWS);
+
   STEPCAFControl_Writer aWriter;
-  aWriter.Init(theWS);
+  configureSTEPCAFWriter(aWriter,
+                         aNode->InternalParameters,
+                         theWS,
+                         theDocument,
+                         aNode->GlobalParameters.SystemUnit,
+                         aNode->ShapeFixParameters);
+
   Handle(StepData_StepModel) aModel =
     Handle(StepData_StepModel)::DownCast(aWriter.Writer().WS()->Model());
   STEPControl_StepModelType aMode =
     static_cast<STEPControl_StepModelType>(aNode->InternalParameters.WriteModelType);
-  aWriter.SetColorMode(aNode->InternalParameters.WriteColor);
-  aWriter.SetNameMode(aNode->InternalParameters.WriteName);
-  aWriter.SetLayerMode(aNode->InternalParameters.WriteLayer);
-  aWriter.SetPropsMode(aNode->InternalParameters.WriteProps);
-  aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
-  aWriter.SetMaterialMode(aNode->InternalParameters.WriteMaterial);
-  aWriter.SetVisualMaterialMode(aNode->InternalParameters.WriteVisMaterial);
-  aWriter.SetCleanDuplicates(aNode->InternalParameters.CleanDuplicates);
-  DESTEP_Parameters aParams        = aNode->InternalParameters;
-  Standard_Real     aScaleFactorMM = 1.;
-  if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
-                                          aScaleFactorMM,
-                                          UnitsMethods_LengthUnit_Millimeter))
-  {
-    aModel->SetLocalLengthUnit(aScaleFactorMM);
-  }
-  else
-  {
-    aModel->SetLocalLengthUnit(aNode->GlobalParameters.SystemUnit);
-    Message::SendWarning()
-      << "Warning in the DESTEP_Provider during writing the file " << thePath
-      << "\t: The document has no information on Units. Using global parameter as initial Unit.";
-  }
+  DESTEP_Parameters       aParams = aNode->InternalParameters;
   UnitsMethods_LengthUnit aTargetUnit =
     UnitsMethods::GetLengthUnitByFactorValue(aNode->GlobalParameters.LengthUnit,
                                              UnitsMethods_LengthUnit_Millimeter);
@@ -194,11 +285,9 @@ bool DESTEP_Provider::Read(const TCollection_AsciiString& thePath,
                            Handle(XSControl_WorkSession)& theWS,
                            const Message_ProgressRange&   theProgress)
 {
-  (void)theProgress;
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!validateNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
@@ -217,7 +306,7 @@ bool DESTEP_Provider::Read(const TCollection_AsciiString& thePath,
     return false;
   }
   aModel->SetLocalLengthUnit(aNode->GlobalParameters.LengthUnit);
-  if (aReader.TransferRoots() <= 0)
+  if (aReader.TransferRoots(theProgress) <= 0)
   {
     Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
                         << "\t:Cannot read any relevant data from the STEP file";
@@ -234,10 +323,9 @@ bool DESTEP_Provider::Write(const TCollection_AsciiString& thePath,
                             Handle(XSControl_WorkSession)& theWS,
                             const Message_ProgressRange&   theProgress)
 {
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!validateNode(GetNode(), aContext))
   {
-    Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
   Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
@@ -312,6 +400,281 @@ bool DESTEP_Provider::Write(const TCollection_AsciiString& thePath,
 
 //=================================================================================================
 
+Standard_Boolean DESTEP_Provider::Read(ReadStreamList&                 theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       Handle(XSControl_WorkSession)&  theWS,
+                                       const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  TCollection_AsciiString aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
+      || !validateNode(GetNode(), aFullContext)
+      || !DE_ValidationUtils::ValidateReadStreamList(theStreams, aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Standard_IStream& aStream = theStreams.First().Stream;
+
+  personizeWS(theWS);
+
+  Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
+  STEPCAFControl_Reader            aReader(theWS, Standard_False);
+  configureSTEPCAFReader(aReader,
+                         aNode->InternalParameters,
+                         theWS,
+                         theDocument,
+                         aNode->GlobalParameters.LengthUnit,
+                         aNode->ShapeFixParameters);
+
+  Standard_Boolean isOk = aReader.ReadStream(aFirstKey.ToCString(), aStream);
+  if (!isOk)
+  {
+    Message::SendFail() << "Error: DESTEP_Provider failed to read stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  return aReader.Transfer(theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Write(WriteStreamList&                theStreams,
+                                        const Handle(TDocStd_Document)& theDocument,
+                                        Handle(XSControl_WorkSession)&  theWS,
+                                        const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  TCollection_AsciiString aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
+      || !validateNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Standard_OStream& aStream = theStreams.First().Stream;
+  if (!checkStreamWritability(aStream, aFirstKey))
+  {
+    return Standard_False;
+  }
+
+  personizeWS(theWS);
+
+  Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
+
+  STEPCAFControl_Writer aWriter(theWS, Standard_False);
+  configureSTEPCAFWriter(aWriter,
+                         aNode->InternalParameters,
+                         theWS,
+                         theDocument,
+                         aNode->GlobalParameters.LengthUnit,
+                         aNode->ShapeFixParameters);
+
+  Handle(StepData_StepModel) aModel =
+    Handle(StepData_StepModel)::DownCast(aWriter.Writer().WS()->Model());
+  DESTEP_Parameters       aParams = aNode->InternalParameters;
+  UnitsMethods_LengthUnit aTargetUnit =
+    UnitsMethods::GetLengthUnitByFactorValue(aNode->GlobalParameters.LengthUnit,
+                                             UnitsMethods_LengthUnit_Millimeter);
+  aParams.WriteUnit = aTargetUnit;
+  aModel->SetWriteLengthUnit(aNode->GlobalParameters.LengthUnit);
+  STEPControl_StepModelType aMode =
+    static_cast<STEPControl_StepModelType>(aNode->InternalParameters.WriteModelType);
+  Standard_Boolean isOk = aWriter.Transfer(theDocument, aParams, aMode, 0, theProgress);
+  if (!isOk)
+  {
+    Message::SendFail() << "Error: DESTEP_Provider failed to transfer document for stream "
+                        << aFirstKey;
+    return Standard_False;
+  }
+  return aWriter.WriteStream(aStream);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Read(ReadStreamList&                 theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       const Message_ProgressRange&    theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Read(theStreams, theDocument, aWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Write(WriteStreamList&                theStreams,
+                                        const Handle(TDocStd_Document)& theDocument,
+                                        const Message_ProgressRange&    theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Write(theStreams, theDocument, aWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Read(ReadStreamList&                theStreams,
+                                       TopoDS_Shape&                  theShape,
+                                       Handle(XSControl_WorkSession)& theWS,
+                                       const Message_ProgressRange&   theProgress)
+{
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  TCollection_AsciiString aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!validateNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Standard_IStream& aStream = theStreams.First().Stream;
+  personizeWS(theWS);
+
+  Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
+
+  // Use STEPControl_Reader for shape operations from streams
+  STEPControl_Reader aReader;
+  aReader.SetWS(theWS);
+  aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
+
+  // Read from stream using the reader's internal model
+  IFSelect_ReturnStatus aReadStat = aReader.ReadStream(aFirstKey.ToCString(), aStream);
+  if (aReadStat != IFSelect_RetDone)
+  {
+    Message::SendFail() << "Error: DESTEP_Provider failed to read from stream " << aFirstKey;
+    return Standard_False;
+  }
+  Handle(StepData_StepModel) aModel = aReader.StepModel();
+  aModel->SetLocalLengthUnit(aNode->GlobalParameters.LengthUnit);
+
+  // Transfer the first root to get the shape
+  if (aReader.TransferRoots(theProgress) <= 0)
+  {
+    Message::SendFail() << "Error: DESTEP_Provider found no transferable roots in stream "
+                        << aFirstKey;
+    return Standard_False;
+  }
+
+  theShape = aReader.OneShape();
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Write(WriteStreamList&               theStreams,
+                                        const TopoDS_Shape&            theShape,
+                                        Handle(XSControl_WorkSession)& theWS,
+                                        const Message_ProgressRange&   theProgress)
+{
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  TCollection_AsciiString aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
+  if (!validateNode(GetNode(), aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Standard_OStream& aStream = theStreams.First().Stream;
+  personizeWS(theWS);
+
+  Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
+
+  // Use STEPControl_Writer for shape operations to streams
+  STEPControl_Writer aWriter;
+  aWriter.SetWS(theWS);
+
+  Handle(StepData_StepModel) aModel = aWriter.Model();
+  aModel->SetLocalLengthUnit(aNode->GlobalParameters.SystemUnit);
+
+  UnitsMethods_LengthUnit aTargetUnit =
+    UnitsMethods::GetLengthUnitByFactorValue(aNode->GlobalParameters.LengthUnit,
+                                             UnitsMethods_LengthUnit_Millimeter);
+  DESTEP_Parameters aParams = aNode->InternalParameters;
+  aParams.WriteUnit         = aTargetUnit;
+
+  if (aTargetUnit == UnitsMethods_LengthUnit_Undefined)
+  {
+    aModel->SetWriteLengthUnit(1.0);
+    Message::SendWarning()
+      << "Custom units are not supported by STEP format, but LengthUnit global parameter doesn't "
+         "fit any predefined unit. Units will be scaled to Millimeters";
+  }
+  else
+  {
+    aModel->SetWriteLengthUnit(aNode->GlobalParameters.LengthUnit);
+  }
+
+  aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
+
+  IFSelect_ReturnStatus aWriteStat = aWriter.Transfer(theShape,
+                                                      aNode->InternalParameters.WriteModelType,
+                                                      aParams,
+                                                      true,
+                                                      theProgress);
+  if (aWriteStat != IFSelect_RetDone)
+  {
+    Message::SendFail() << "Error: DESTEP_Provider failed to transfer shape for stream "
+                        << aFirstKey;
+    return Standard_False;
+  }
+
+  if (aNode->InternalParameters.CleanDuplicates)
+  {
+    aWriter.CleanDuplicateEntities();
+  }
+
+  // Write to stream
+  if (!aWriter.WriteStream(aStream))
+  {
+    Message::SendFail() << "Error: DESTEP_Provider failed to write to stream " << aFirstKey;
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Read(ReadStreamList&              theStreams,
+                                       TopoDS_Shape&                theShape,
+                                       const Message_ProgressRange& theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Read(theStreams, theShape, aWS, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTEP_Provider::Write(WriteStreamList&             theStreams,
+                                        const TopoDS_Shape&          theShape,
+                                        const Message_ProgressRange& theProgress)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+  return Write(theStreams, theShape, aWS, theProgress);
+}
+
+//=================================================================================================
+
 TCollection_AsciiString DESTEP_Provider::GetFormat() const
 {
   return TCollection_AsciiString("STEP");
index c049d75b3f3dbdc0fa6e5b9c1a4849ccc13cf766..308fd3e79c602633f4c3dc8fa08da9256239e1d2 100644 (file)
@@ -109,6 +109,54 @@ public:
     Handle(XSControl_WorkSession)& theWS,
     const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                theStreams,
+    TopoDS_Shape&                  theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&               theStreams,
+    const TopoDS_Shape&            theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
   //! Reads a CAD file, according internal configuration
   //! @param[in] thePath path to the import CAD file
   //! @param[out] theShape shape to save result
@@ -129,6 +177,46 @@ public:
     const TopoDS_Shape&            theShape,
     const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&              theStreams,
+    TopoDS_Shape&                theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&             theStreams,
+    const TopoDS_Shape&          theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
 public:
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
diff --git a/src/DataExchange/TKDESTEP/GTests/DESTEP_Provider_Test.cxx b/src/DataExchange/TKDESTEP/GTests/DESTEP_Provider_Test.cxx
new file mode 100644 (file)
index 0000000..b616c73
--- /dev/null
@@ -0,0 +1,475 @@
+// Copyright (c) 2025 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.
+
+#include <DESTEP_Provider.hxx>
+#include <DESTEP_ConfigurationNode.hxx>
+#include <DE_Wrapper.hxx>
+
+#include <BRepPrimAPI_MakeBox.hxx>
+#include <BRepPrimAPI_MakeSphere.hxx>
+#include <BRepPrimAPI_MakeCylinder.hxx>
+#include <TopoDS_Shape.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopAbs_ShapeEnum.hxx>
+#include <TDocStd_Document.hxx>
+#include <TDocStd_Application.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XSControl_WorkSession.hxx>
+
+#include <sstream>
+#include <gtest/gtest.h>
+
+class DESTEP_ProviderTest : public ::testing::Test
+{
+protected:
+  void SetUp() override
+  {
+    // Initialize provider with default configuration
+    Handle(DESTEP_ConfigurationNode) aNode = new DESTEP_ConfigurationNode();
+    myProvider                             = new DESTEP_Provider(aNode);
+
+    // Create test BRep shapes (perfect for STEP format)
+    myBox      = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape();
+    mySphere   = BRepPrimAPI_MakeSphere(5.0).Shape();
+    myCylinder = BRepPrimAPI_MakeCylinder(3.0, 8.0).Shape();
+
+    // Create test document
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    anApp->NewDocument("BinXCAF", myDocument);
+  }
+
+  void TearDown() override
+  {
+    myProvider.Nullify();
+    myDocument.Nullify();
+  }
+
+  // Helper method to count shape elements
+  Standard_Integer CountShapeElements(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType)
+  {
+    Standard_Integer aCount = 0;
+    for (TopExp_Explorer anExplorer(theShape, theType); anExplorer.More(); anExplorer.Next())
+    {
+      aCount++;
+    }
+    return aCount;
+  }
+
+  // Helper method to validate STEP content
+  bool IsValidSTEPContent(const std::string& theContent)
+  {
+    return !theContent.empty() && theContent.find("ISO-10303-21;") != std::string::npos
+           && theContent.find("HEADER;") != std::string::npos
+           && theContent.find("DATA;") != std::string::npos
+           && theContent.find("ENDSEC;") != std::string::npos;
+  }
+
+protected:
+  Handle(DESTEP_Provider)  myProvider;
+  TopoDS_Shape             myBox;
+  TopoDS_Shape             mySphere;
+  TopoDS_Shape             myCylinder;
+  Handle(TDocStd_Document) myDocument;
+};
+
+// Test basic provider creation and format/vendor information
+TEST_F(DESTEP_ProviderTest, BasicProperties)
+{
+  EXPECT_STREQ("STEP", myProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
+  EXPECT_FALSE(myProvider->GetNode().IsNull());
+}
+
+// Test stream-based shape write and read operations
+TEST_F(DESTEP_ProviderTest, StreamShapeWriteRead)
+{
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("test.step", anOStream));
+
+  // Write box shape to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myBox));
+
+  std::string aStepContent = anOStream.str();
+  EXPECT_FALSE(aStepContent.empty());
+  EXPECT_TRUE(IsValidSTEPContent(aStepContent));
+
+  if (!aStepContent.empty())
+  {
+    // Read back from stream
+    std::istringstream          anIStream(aStepContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("test.step", anIStream));
+
+    TopoDS_Shape aReadShape;
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
+    EXPECT_FALSE(aReadShape.IsNull());
+
+    if (!aReadShape.IsNull())
+    {
+      // STEP should preserve solid geometry
+      Standard_Integer aReadSolids     = CountShapeElements(aReadShape, TopAbs_SOLID);
+      Standard_Integer aOriginalSolids = CountShapeElements(myBox, TopAbs_SOLID);
+      EXPECT_EQ(aReadSolids, aOriginalSolids);
+    }
+  }
+}
+
+// Test stream-based document write and read operations
+TEST_F(DESTEP_ProviderTest, StreamDocumentWriteRead)
+{
+  // Add box to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aShapeLabel = aShapeTool->AddShape(myBox);
+  EXPECT_FALSE(aShapeLabel.IsNull());
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("document.step", anOStream));
+
+  // Write document to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
+
+  std::string aStepContent = anOStream.str();
+  EXPECT_FALSE(aStepContent.empty());
+  EXPECT_TRUE(IsValidSTEPContent(aStepContent));
+
+  if (!aStepContent.empty())
+  {
+    // Create new document for reading
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    Handle(TDocStd_Document)    aNewDocument;
+    anApp->NewDocument("BinXCAF", aNewDocument);
+
+    // Read back from stream
+    std::istringstream          anIStream(aStepContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("document.step", anIStream));
+
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
+
+    // Validate document content
+    Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+    TDF_LabelSequence         aLabels;
+    aNewShapeTool->GetShapes(aLabels);
+    EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
+  }
+}
+
+// Test DE_Wrapper integration for STEP operations
+TEST_F(DESTEP_ProviderTest, DE_WrapperIntegration)
+{
+  // Initialize DE_Wrapper and bind STEP provider
+  DE_Wrapper                       aWrapper;
+  Handle(DESTEP_ConfigurationNode) aNode = new DESTEP_ConfigurationNode();
+
+  // Bind the configured node to wrapper
+  EXPECT_TRUE(aWrapper.Bind(aNode));
+
+  // Test write with DE_Wrapper using sphere
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("test.step", anOStream));
+
+  EXPECT_TRUE(aWrapper.Write(aWriteStreams, mySphere));
+
+  std::string aStepContent = anOStream.str();
+  EXPECT_FALSE(aStepContent.empty());
+  EXPECT_TRUE(IsValidSTEPContent(aStepContent));
+
+  if (!aStepContent.empty())
+  {
+    // Test DE_Wrapper stream operations
+    std::istringstream          anIStream(aStepContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("test.step", anIStream));
+
+    TopoDS_Shape aReadShape;
+    bool         aWrapperResult = aWrapper.Read(aReadStreams, aReadShape);
+
+    // Test direct provider with same content for comparison
+    std::istringstream          anIStream2(aStepContent);
+    DE_Provider::ReadStreamList aReadStreams2;
+    aReadStreams2.Append(DE_Provider::ReadStreamNode("test.step", anIStream2));
+
+    Handle(DESTEP_Provider) aDirectProvider = new DESTEP_Provider(aNode);
+    TopoDS_Shape            aDirectShape;
+    bool                    aDirectResult = aDirectProvider->Read(aReadStreams2, aDirectShape);
+
+    // REQUIREMENT: DE_Wrapper must work exactly the same as direct provider
+    EXPECT_EQ(aWrapperResult, aDirectResult);
+    EXPECT_EQ(aReadShape.IsNull(), aDirectShape.IsNull());
+
+    if (aDirectResult && !aDirectShape.IsNull())
+    {
+      Standard_Integer aSolids = CountShapeElements(aDirectShape, TopAbs_SOLID);
+      EXPECT_GT(aSolids, 0);
+    }
+  }
+}
+
+// Test multiple shapes in single document
+TEST_F(DESTEP_ProviderTest, MultipleShapesInDocument)
+{
+  // Add multiple shapes to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool     = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aBoxLabel      = aShapeTool->AddShape(myBox);
+  TDF_Label                 aSphereLabel   = aShapeTool->AddShape(mySphere);
+  TDF_Label                 aCylinderLabel = aShapeTool->AddShape(myCylinder);
+
+  EXPECT_FALSE(aBoxLabel.IsNull());
+  EXPECT_FALSE(aSphereLabel.IsNull());
+  EXPECT_FALSE(aCylinderLabel.IsNull());
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("multi_shapes.step", anOStream));
+
+  // Write document with multiple shapes
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
+
+  std::string aStepContent = anOStream.str();
+  EXPECT_FALSE(aStepContent.empty());
+  EXPECT_TRUE(IsValidSTEPContent(aStepContent));
+
+  // Read back into new document
+  Handle(TDocStd_Application) anApp = new TDocStd_Application();
+  Handle(TDocStd_Document)    aNewDocument;
+  anApp->NewDocument("BinXCAF", aNewDocument);
+
+  std::istringstream          anIStream(aStepContent);
+  DE_Provider::ReadStreamList aReadStreams;
+  aReadStreams.Append(DE_Provider::ReadStreamNode("multi_shapes.step", anIStream));
+
+  EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
+
+  // Validate document content
+  Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+  TDF_LabelSequence         aLabels;
+  aNewShapeTool->GetShapes(aLabels);
+  EXPECT_EQ(aLabels.Length(), 3); // Should have exactly 3 shapes in document
+}
+
+// Test different BRep geometry types
+TEST_F(DESTEP_ProviderTest, DifferentBRepGeometries)
+{
+  // Test box geometry
+  std::ostringstream           aBoxStream;
+  DE_Provider::WriteStreamList aBoxWriteStreams;
+  aBoxWriteStreams.Append(DE_Provider::WriteStreamNode("box.step", aBoxStream));
+
+  EXPECT_TRUE(myProvider->Write(aBoxWriteStreams, myBox));
+  std::string aBoxContent = aBoxStream.str();
+
+  // Test sphere geometry
+  std::ostringstream           aSphereStream;
+  DE_Provider::WriteStreamList aSphereWriteStreams;
+  aSphereWriteStreams.Append(DE_Provider::WriteStreamNode("sphere.step", aSphereStream));
+
+  EXPECT_TRUE(myProvider->Write(aSphereWriteStreams, mySphere));
+  std::string aSphereContent = aSphereStream.str();
+
+  // Test cylinder geometry
+  std::ostringstream           aCylinderStream;
+  DE_Provider::WriteStreamList aCylinderWriteStreams;
+  aCylinderWriteStreams.Append(DE_Provider::WriteStreamNode("cylinder.step", aCylinderStream));
+
+  EXPECT_TRUE(myProvider->Write(aCylinderWriteStreams, myCylinder));
+  std::string aCylinderContent = aCylinderStream.str();
+
+  // All content should be valid STEP format
+  EXPECT_TRUE(IsValidSTEPContent(aBoxContent));
+  EXPECT_TRUE(IsValidSTEPContent(aSphereContent));
+  EXPECT_TRUE(IsValidSTEPContent(aCylinderContent));
+
+  // Different geometries should produce different STEP content
+  EXPECT_NE(aBoxContent, aSphereContent);
+  EXPECT_NE(aBoxContent, aCylinderContent);
+  EXPECT_NE(aSphereContent, aCylinderContent);
+
+  // All should read back successfully
+  std::istringstream          aBoxIStream(aBoxContent);
+  DE_Provider::ReadStreamList aBoxReadStreams;
+  aBoxReadStreams.Append(DE_Provider::ReadStreamNode("box.step", aBoxIStream));
+
+  TopoDS_Shape aBoxReadShape;
+  EXPECT_TRUE(myProvider->Read(aBoxReadStreams, aBoxReadShape));
+  EXPECT_FALSE(aBoxReadShape.IsNull());
+
+  std::istringstream          aSphereIStream(aSphereContent);
+  DE_Provider::ReadStreamList aSphereReadStreams;
+  aSphereReadStreams.Append(DE_Provider::ReadStreamNode("sphere.step", aSphereIStream));
+
+  TopoDS_Shape aSphereReadShape;
+  EXPECT_TRUE(myProvider->Read(aSphereReadStreams, aSphereReadShape));
+  EXPECT_FALSE(aSphereReadShape.IsNull());
+}
+
+// Test DE_Wrapper with different file extensions
+TEST_F(DESTEP_ProviderTest, DE_WrapperFileExtensions)
+{
+  DE_Wrapper                       aWrapper;
+  Handle(DESTEP_ConfigurationNode) aNode = new DESTEP_ConfigurationNode();
+  EXPECT_TRUE(aWrapper.Bind(aNode));
+
+  // Test different STEP extensions
+  std::vector<std::string> aExtensions = {"test.step", "test.STEP", "test.stp", "test.STP"};
+
+  for (const auto& anExt : aExtensions)
+  {
+    std::ostringstream           anOStream;
+    DE_Provider::WriteStreamList aWriteStreams;
+    aWriteStreams.Append(DE_Provider::WriteStreamNode(anExt.c_str(), anOStream));
+
+    EXPECT_TRUE(aWrapper.Write(aWriteStreams, myBox))
+      << "Failed to write with extension: " << anExt;
+
+    std::string aContent = anOStream.str();
+    EXPECT_FALSE(aContent.empty()) << "Empty content for extension: " << anExt;
+    EXPECT_TRUE(IsValidSTEPContent(aContent)) << "Invalid STEP content for extension: " << anExt;
+
+    // Test read back
+    std::istringstream          anIStream(aContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode(anExt.c_str(), anIStream));
+
+    TopoDS_Shape aReadShape;
+    EXPECT_TRUE(aWrapper.Read(aReadStreams, aReadShape))
+      << "Failed to read with extension: " << anExt;
+    EXPECT_FALSE(aReadShape.IsNull()) << "Null shape read with extension: " << anExt;
+  }
+}
+
+// Test error conditions and edge cases
+TEST_F(DESTEP_ProviderTest, ErrorHandling)
+{
+  // Test with empty streams
+  DE_Provider::WriteStreamList anEmptyWriteStreams;
+  EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myBox));
+
+  DE_Provider::ReadStreamList anEmptyReadStreams;
+  TopoDS_Shape                aShape;
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
+
+  // Test with null shape
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("null_test.step", anOStream));
+  TopoDS_Shape aNullShape;
+
+  // Writing null shape should fail
+  EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullShape));
+
+  // Test reading invalid STEP content
+  std::string                 anInvalidContent = "This is not valid STEP content";
+  std::istringstream          anInvalidStream(anInvalidContent);
+  DE_Provider::ReadStreamList anInvalidReadStreams;
+  anInvalidReadStreams.Append(DE_Provider::ReadStreamNode("invalid.step", anInvalidStream));
+
+  TopoDS_Shape anInvalidShape;
+  EXPECT_FALSE(myProvider->Read(anInvalidReadStreams, anInvalidShape));
+
+  // Test with null document
+  Handle(TDocStd_Document) aNullDoc;
+  EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullDoc));
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aNullDoc));
+}
+
+// Test DESTEP configuration modes
+TEST_F(DESTEP_ProviderTest, ConfigurationModes)
+{
+  Handle(DESTEP_ConfigurationNode) aNode =
+    Handle(DESTEP_ConfigurationNode)::DownCast(myProvider->GetNode());
+
+  // Test basic configuration access
+  EXPECT_FALSE(aNode.IsNull());
+
+  // Test provider format and vendor are correct
+  EXPECT_STREQ("STEP", myProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
+
+  // Test that we can create provider with different configuration
+  Handle(DESTEP_ConfigurationNode) aNewNode     = new DESTEP_ConfigurationNode();
+  Handle(DESTEP_Provider)          aNewProvider = new DESTEP_Provider(aNewNode);
+
+  EXPECT_STREQ("STEP", aNewProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", aNewProvider->GetVendor().ToCString());
+  EXPECT_FALSE(aNewProvider->GetNode().IsNull());
+}
+
+// Test WorkSession integration
+TEST_F(DESTEP_ProviderTest, WorkSessionIntegration)
+{
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+
+  // Test write operation with work session
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("ws_test.step", anOStream));
+
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myBox, aWS));
+
+  std::string aStepContent = anOStream.str();
+  EXPECT_FALSE(aStepContent.empty());
+  EXPECT_TRUE(IsValidSTEPContent(aStepContent));
+
+  // Test read operation with work session
+  std::istringstream          anIStream(aStepContent);
+  DE_Provider::ReadStreamList aReadStreams;
+  aReadStreams.Append(DE_Provider::ReadStreamNode("ws_test.step", anIStream));
+
+  TopoDS_Shape aReadShape;
+  EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape, aWS));
+  EXPECT_FALSE(aReadShape.IsNull());
+}
+
+// Test document operations with WorkSession
+TEST_F(DESTEP_ProviderTest, DocumentWorkSessionIntegration)
+{
+  // Add shape to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aShapeLabel = aShapeTool->AddShape(mySphere);
+  EXPECT_FALSE(aShapeLabel.IsNull());
+
+  Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("doc_ws_test.step", anOStream));
+
+  // Test document write with work session
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument, aWS));
+
+  std::string aStepContent = anOStream.str();
+  EXPECT_FALSE(aStepContent.empty());
+  EXPECT_TRUE(IsValidSTEPContent(aStepContent));
+
+  // Create new document for reading
+  Handle(TDocStd_Application) anApp = new TDocStd_Application();
+  Handle(TDocStd_Document)    aNewDocument;
+  anApp->NewDocument("BinXCAF", aNewDocument);
+
+  // Test document read with work session
+  std::istringstream          anIStream(aStepContent);
+  DE_Provider::ReadStreamList aReadStreams;
+  aReadStreams.Append(DE_Provider::ReadStreamNode("doc_ws_test.step", anIStream));
+
+  EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument, aWS));
+
+  // Validate document content
+  Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+  TDF_LabelSequence         aLabels;
+  aNewShapeTool->GetShapes(aLabels);
+  EXPECT_GT(aLabels.Length(), 0);
+}
\ No newline at end of file
index df6050cd9d8ffa3d927a6890a9a680c8e254fe8a..d51e3db50253e2c88b46fedf6450e34ea497aebb 100644 (file)
@@ -2,6 +2,7 @@
 set(OCCT_TKDESTEP_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
 
 set(OCCT_TKDESTEP_GTests_FILES
+    DESTEP_Provider_Test.cxx
     STEPConstruct_RenderingProperties_Test.cxx
     StepData_StepWriter_Test.cxx
     StepTidy_BaseTestFixture.pxx
index 107aa782b6dd61258890a55854c33061b496a68a..cdcf2c16ffe43aa920c47bbb55af1c5af913a70e 100644 (file)
@@ -134,6 +134,13 @@ bool DESTL_ConfigurationNode::IsExportSupported() const
 
 //=================================================================================================
 
+bool DESTL_ConfigurationNode::IsStreamSupported() const
+{
+  return true;
+}
+
+//=================================================================================================
+
 TCollection_AsciiString DESTL_ConfigurationNode::GetFormat() const
 {
   return TCollection_AsciiString("STL");
index c252dc031edaeada53d7aab15321b4d9b9346bda..191ffd430157cd1c1b6991352675a2875e6189ea 100644 (file)
@@ -64,6 +64,10 @@ public:
   //! @return true if export is supported
   Standard_EXPORT virtual bool IsExportSupported() const Standard_OVERRIDE;
 
+  //! Checks for stream support.
+  //! @return Standard_True if streams are supported
+  Standard_EXPORT virtual bool IsStreamSupported() const Standard_OVERRIDE;
+
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
   Standard_EXPORT virtual TCollection_AsciiString GetFormat() const Standard_OVERRIDE;
index a99bf85905c43ea45d12cc0f47a034265fac2132..bbecddd9d79dac99224d44e96758d724ef63e9a1 100644 (file)
 #include <DESTL_Provider.hxx>
 
 #include <BRep_Builder.hxx>
+#include <DE_ValidationUtils.hxx>
 #include <DESTL_ConfigurationNode.hxx>
 #include <Message.hxx>
+#include <NCollection_Vector.hxx>
+#include <Poly_Triangle.hxx>
 #include <RWStl.hxx>
+#include <RWStl_Reader.hxx>
 #include <StlAPI.hxx>
+#include <StlAPI_Reader.hxx>
 #include <StlAPI_Writer.hxx>
+#include <Standard_ReadLineBuffer.hxx>
 #include <TDocStd_Document.hxx>
 #include <XCAFDoc_DocumentTool.hxx>
 #include <XCAFDoc_ShapeTool.hxx>
+#include <cstring>
 
 IMPLEMENT_STANDARD_RTTIEXT(DESTL_Provider, DE_Provider)
 
@@ -64,10 +71,9 @@ bool DESTL_Provider::Read(const TCollection_AsciiString&  thePath,
                           const Handle(TDocStd_Document)& theDocument,
                           const Message_ProgressRange&    theProgress)
 {
-  if (theDocument.IsNull())
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
   {
-    Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
-                        << "\t: theDocument shouldn't be null";
     return false;
   }
   TopoDS_Shape aShape;
@@ -86,25 +92,35 @@ bool DESTL_Provider::Write(const TCollection_AsciiString&  thePath,
                            const Handle(TDocStd_Document)& theDocument,
                            const Message_ProgressRange&    theProgress)
 {
-  TopoDS_Shape              aShape;
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
+  {
+    return false;
+  }
+
+  // Extract shape from document
   TDF_LabelSequence         aLabels;
   Handle(XCAFDoc_ShapeTool) aSTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
   aSTool->GetFreeShapes(aLabels);
+
   if (aLabels.Length() <= 0)
   {
     Message::SendFail() << "Error in the DESTL_Provider during writing the file " << thePath
-                        << "\t: Document contain no shapes";
+                        << ": Document contain no shapes";
     return false;
   }
 
-  Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
-  if (aNode->GlobalParameters.LengthUnit != 1.0)
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DESTL_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendWarning()
-      << "Warning in the DESTL_Provider during writing the file " << thePath
-      << "\t: Target Units for writing were changed, but current format doesn't support scaling";
+    return false;
   }
 
+  Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
+  DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit, aContext);
+
+  TopoDS_Shape aShape;
   if (aLabels.Length() == 1)
   {
     aShape = aSTool->GetShape(aLabels.Value(1));
@@ -121,6 +137,7 @@ bool DESTL_Provider::Write(const TCollection_AsciiString&  thePath,
     }
     aShape = aComp;
   }
+
   return Write(thePath, aShape, theProgress);
 }
 
@@ -154,14 +171,18 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
 {
   Message::SendWarning()
     << "OCCT Stl reader does not support model scaling according to custom length unit";
-  if (!GetNode()->IsKind(STANDARD_TYPE(DESTL_ConfigurationNode)))
+
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DESTL_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
-    return true;
+    return false;
   }
+
   Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
   double aMergeAngle                    = aNode->InternalParameters.ReadMergeAngle * M_PI / 180.0;
+
   if (aMergeAngle != M_PI_2)
   {
     if (aMergeAngle < 0.0 || aMergeAngle > M_PI_2)
@@ -171,6 +192,7 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
       return false;
     }
   }
+
   if (!aNode->InternalParameters.ReadBRep)
   {
     Handle(Poly_Triangulation) aTriangulation =
@@ -184,7 +206,9 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
   }
   else
   {
-    Standard_DISABLE_DEPRECATION_WARNINGS if (!StlAPI::Read(theShape, thePath.ToCString()))
+    Standard_DISABLE_DEPRECATION_WARNINGS
+
+      if (!StlAPI::Read(theShape, thePath.ToCString()))
     {
       Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath;
       return false;
@@ -202,25 +226,23 @@ bool DESTL_Provider::Write(const TCollection_AsciiString& thePath,
 {
   Message::SendWarning()
     << "OCCT Stl writer does not support model scaling according to custom length unit";
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTL_ConfigurationNode)))
+
+  TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DESTL_ConfigurationNode),
+                                                     aContext))
   {
-    Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
+
   Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
-  if (aNode->GlobalParameters.LengthUnit != 1.0)
-  {
-    Message::SendWarning()
-      << "Warning in the DESTL_Provider during writing the file " << thePath
-      << "\t: Target Units for writing were changed, but current format doesn't support scaling";
-  }
+  DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit, aContext);
 
   StlAPI_Writer aWriter;
   aWriter.ASCIIMode() = aNode->InternalParameters.WriteAscii;
   if (!aWriter.Write(theShape, thePath.ToCString(), theProgress))
   {
-    Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
+    Message::SendFail() << "Error in the DESTL_Provider during writing the file " << thePath
                         << "\t: Mesh writing has been failed";
     return false;
   }
@@ -229,6 +251,271 @@ bool DESTL_Provider::Write(const TCollection_AsciiString& thePath,
 
 //=================================================================================================
 
+Standard_Boolean DESTL_Provider::Read(ReadStreamList&                 theStreams,
+                                      const Handle(TDocStd_Document)& theDocument,
+                                      Handle(XSControl_WorkSession)&  theWS,
+                                      const Message_ProgressRange&    theProgress)
+{
+  (void)theWS;
+  return Read(theStreams, theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Write(WriteStreamList&                theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       Handle(XSControl_WorkSession)&  theWS,
+                                       const Message_ProgressRange&    theProgress)
+{
+  (void)theWS;
+  return Write(theStreams, theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Read(ReadStreamList&                theStreams,
+                                      TopoDS_Shape&                  theShape,
+                                      Handle(XSControl_WorkSession)& theWS,
+                                      const Message_ProgressRange&   theProgress)
+{
+  (void)theWS;
+  return Read(theStreams, theShape, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Write(WriteStreamList&               theStreams,
+                                       const TopoDS_Shape&            theShape,
+                                       Handle(XSControl_WorkSession)& theWS,
+                                       const Message_ProgressRange&   theProgress)
+{
+  (void)theWS;
+  return Write(theStreams, theShape, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Read(ReadStreamList&                 theStreams,
+                                      const Handle(TDocStd_Document)& theDocument,
+                                      const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString        aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
+  {
+    return Standard_False;
+  }
+
+  TopoDS_Shape aShape;
+  if (!Read(theStreams, aShape, theProgress))
+  {
+    return Standard_False;
+  }
+
+  Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
+  aShapeTool->AddShape(aShape);
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Write(WriteStreamList&                theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString        aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
+  {
+    return Standard_False;
+  }
+
+  // Extract shape from document
+  TDF_LabelSequence         aLabels;
+  Handle(XCAFDoc_ShapeTool) aSTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
+  aSTool->GetFreeShapes(aLabels);
+
+  if (aLabels.Length() <= 0)
+  {
+    Message::SendFail() << "Error in the DESTL_Provider during writing stream " << aFirstKey
+                        << ": Document contain no shapes";
+    return Standard_False;
+  }
+
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DESTL_ConfigurationNode),
+                                                     aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DESTL_ConfigurationNode) aNode  = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
+  TCollection_AsciiString aLengthContext = TCollection_AsciiString("writing stream ") + aFirstKey;
+  DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit,
+                                                 aLengthContext);
+
+  TopoDS_Shape aShape;
+  if (aLabels.Length() == 1)
+  {
+    aShape = aSTool->GetShape(aLabels.Value(1));
+  }
+  else
+  {
+    TopoDS_Compound aComp;
+    BRep_Builder    aBuilder;
+    aBuilder.MakeCompound(aComp);
+    for (Standard_Integer anIndex = 1; anIndex <= aLabels.Length(); anIndex++)
+    {
+      TopoDS_Shape aS = aSTool->GetShape(aLabels.Value(anIndex));
+      aBuilder.Add(aComp, aS);
+    }
+    aShape = aComp;
+  }
+
+  return Write(theStreams, aShape, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Read(ReadStreamList&              theStreams,
+                                      TopoDS_Shape&                theShape,
+                                      const Message_ProgressRange& theProgress)
+{
+  // Validate stream map
+  if (theStreams.IsEmpty())
+  {
+    Message::SendFail() << "Error: DESTL_Provider stream map is empty";
+    return Standard_False;
+  }
+  if (theStreams.Size() > 1)
+  {
+    Message::SendWarning() << "Warning: DESTL_Provider received " << theStreams.Size()
+                           << " streams for reading, using only the first one";
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+  Standard_IStream&              aStream   = theStreams.First().Stream;
+
+  Message::SendWarning()
+    << "OCCT Stl reader does not support model scaling according to custom length unit";
+
+  TCollection_AsciiString aNodeContext = TCollection_AsciiString("reading stream ") + aFirstKey;
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DESTL_ConfigurationNode),
+                                                     aNodeContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
+  double aMergeAngle                    = aNode->InternalParameters.ReadMergeAngle * M_PI / 180.0;
+
+  if (aMergeAngle != M_PI_2)
+  {
+    if (aMergeAngle < 0.0 || aMergeAngle > M_PI_2)
+    {
+      Message::SendFail() << "Error in the DESTL_Provider during reading stream " << aFirstKey
+                          << ": The merge angle is out of the valid range";
+      return Standard_False;
+    }
+  }
+
+  if (!aNode->InternalParameters.ReadBRep)
+  {
+    Handle(Poly_Triangulation) aTriangulation =
+      RWStl::ReadStream(aStream, aMergeAngle, theProgress);
+    if (aTriangulation.IsNull())
+    {
+      Message::SendFail() << "Error in the DESTL_Provider during reading stream " << aFirstKey
+                          << ": Failed to create triangulation";
+      return Standard_False;
+    }
+
+    TopoDS_Face  aFace;
+    BRep_Builder aB;
+    aB.MakeFace(aFace);
+    aB.UpdateFace(aFace, aTriangulation);
+    theShape = aFace;
+  }
+  else
+  {
+    Standard_DISABLE_DEPRECATION_WARNINGS
+
+      StlAPI_Reader aReader;
+    if (!aReader.Read(theShape, aStream))
+    {
+      Message::SendFail() << "Error in the DESTL_Provider during reading stream " << aFirstKey;
+      return Standard_False;
+    }
+    Standard_ENABLE_DEPRECATION_WARNINGS
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DESTL_Provider::Write(WriteStreamList&             theStreams,
+                                       const TopoDS_Shape&          theShape,
+                                       const Message_ProgressRange& theProgress)
+{
+  // Validate stream map
+  if (theStreams.IsEmpty())
+  {
+    Message::SendFail() << "Error: DESTL_Provider stream map is empty";
+    return Standard_False;
+  }
+  if (theStreams.Size() > 1)
+  {
+    Message::SendWarning() << "Warning: DESTL_Provider received " << theStreams.Size()
+                           << " streams for writing, using only the first one";
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+  Standard_OStream&              aStream   = theStreams.First().Stream;
+
+  Message::SendWarning()
+    << "OCCT Stl writer does not support model scaling according to custom length unit";
+
+  TCollection_AsciiString aNodeContext = TCollection_AsciiString("writing stream ") + aFirstKey;
+  if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
+                                                     STANDARD_TYPE(DESTL_ConfigurationNode),
+                                                     aNodeContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DESTL_ConfigurationNode) aNode  = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
+  TCollection_AsciiString aLengthContext = TCollection_AsciiString("writing stream ") + aFirstKey;
+  DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit,
+                                                 aLengthContext);
+
+  StlAPI_Writer aWriter;
+  aWriter.ASCIIMode() = aNode->InternalParameters.WriteAscii;
+  if (!aWriter.Write(theShape, aStream, theProgress))
+  {
+    Message::SendFail() << "Error in the DESTL_Provider during writing stream " << aFirstKey
+                        << ": Mesh writing has been failed";
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
 TCollection_AsciiString DESTL_Provider::GetFormat() const
 {
   return TCollection_AsciiString("STL");
index 739c8323cd11f56986c21a759347473176afe52b..009d420d71b46eec1cf458a8af056ba186046c53 100644 (file)
@@ -108,6 +108,54 @@ public:
     Handle(XSControl_WorkSession)& theWS,
     const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                theStreams,
+    TopoDS_Shape&                  theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&               theStreams,
+    const TopoDS_Shape&            theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
   //! Reads a CAD file, according internal configuration
   //! @param[in] thePath path to the import CAD file
   //! @param[out] theShape shape to save result
@@ -128,6 +176,46 @@ public:
     const TopoDS_Shape&            theShape,
     const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&              theStreams,
+    TopoDS_Shape&                theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&             theStreams,
+    const TopoDS_Shape&          theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
 public:
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
diff --git a/src/DataExchange/TKDESTL/GTests/DESTL_Provider_Test.cxx b/src/DataExchange/TKDESTL/GTests/DESTL_Provider_Test.cxx
new file mode 100644 (file)
index 0000000..7a04225
--- /dev/null
@@ -0,0 +1,559 @@
+// Copyright (c) 2025 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.
+
+#include <DESTL_Provider.hxx>
+#include <DESTL_ConfigurationNode.hxx>
+#include <DE_Wrapper.hxx>
+
+#include <BRepPrimAPI_MakeBox.hxx>
+#include <BRepPrimAPI_MakeSphere.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <BRep_Builder.hxx>
+#include <TopoDS_Shape.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopAbs_ShapeEnum.hxx>
+#include <gp_Pnt.hxx>
+#include <gp_Pln.hxx>
+#include <gp_Dir.hxx>
+#include <Poly_Triangulation.hxx>
+#include <Poly_Triangle.hxx>
+#include <TColgp_Array1OfPnt.hxx>
+#include <Poly_Array1OfTriangle.hxx>
+#include <BRep_Tool.hxx>
+#include <TopLoc_Location.hxx>
+#include <TDocStd_Document.hxx>
+#include <TDocStd_Application.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+
+#include <sstream>
+#include <gtest/gtest.h>
+
+class DESTL_ProviderTest : public ::testing::Test
+{
+protected:
+  void SetUp() override
+  {
+    // Initialize provider with default configuration
+    Handle(DESTL_ConfigurationNode) aNode = new DESTL_ConfigurationNode();
+    myProvider                            = new DESTL_Provider(aNode);
+
+    // Create triangulated shape for testing (STL format requires triangulated data)
+
+    myTriangularFace = CreateTriangulatedFace();
+
+    // Create test document
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    anApp->NewDocument("BinXCAF", myDocument);
+  }
+
+  void TearDown() override
+  {
+    myProvider.Nullify();
+    myDocument.Nullify();
+  }
+
+  // Helper method to count shape elements
+  Standard_Integer CountShapeElements(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType)
+  {
+    Standard_Integer aCount = 0;
+    for (TopExp_Explorer anExplorer(theShape, theType); anExplorer.More(); anExplorer.Next())
+    {
+      aCount++;
+    }
+    return aCount;
+  }
+
+  // Helper method to create a triangulated face with mesh data (suitable for STL)
+  TopoDS_Shape CreateTriangulatedFace()
+  {
+    // Create vertices for triangulation
+    TColgp_Array1OfPnt aNodes(1, 4);
+    aNodes.SetValue(1, gp_Pnt(0.0, 0.0, 0.0));   // Bottom-left
+    aNodes.SetValue(2, gp_Pnt(10.0, 0.0, 0.0));  // Bottom-right
+    aNodes.SetValue(3, gp_Pnt(10.0, 10.0, 0.0)); // Top-right
+    aNodes.SetValue(4, gp_Pnt(0.0, 10.0, 0.0));  // Top-left
+
+    // Create triangles (two triangles forming a quad)
+    Poly_Array1OfTriangle aTriangles(1, 2);
+    aTriangles.SetValue(1, Poly_Triangle(1, 2, 3)); // First triangle
+    aTriangles.SetValue(2, Poly_Triangle(1, 3, 4)); // Second triangle
+
+    // Create triangulation
+    Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(aNodes, aTriangles);
+
+    // Create a simple planar face
+    gp_Pln                  aPlane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0, 0, 1));
+    BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 0.0, 10.0, 0.0, 10.0);
+
+    if (!aFaceBuilder.IsDone())
+    {
+      return TopoDS_Shape();
+    }
+
+    TopoDS_Face aFace = aFaceBuilder.Face();
+
+    // Attach triangulation to the face
+    BRep_Builder aBuilder;
+    aBuilder.UpdateFace(aFace, aTriangulation);
+
+    return aFace;
+  }
+
+protected:
+  Handle(DESTL_Provider)   myProvider;
+  TopoDS_Shape             myTriangularFace;
+  Handle(TDocStd_Document) myDocument;
+};
+
+// Test basic provider creation and format/vendor information
+TEST_F(DESTL_ProviderTest, BasicProperties)
+{
+  EXPECT_STREQ("STL", myProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
+  EXPECT_FALSE(myProvider->GetNode().IsNull());
+}
+
+// Test stream-based shape write and read operations
+TEST_F(DESTL_ProviderTest, StreamShapeWriteRead)
+{
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("test.stl", anOStream));
+
+  // Write triangulated face to stream (STL works with mesh data)
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myTriangularFace));
+
+  std::string aStlContent = anOStream.str();
+  EXPECT_FALSE(aStlContent.empty());
+  EXPECT_TRUE(aStlContent.find("solid") != std::string::npos
+              || aStlContent.find("facet") != std::string::npos);
+
+  if (!aStlContent.empty())
+  {
+    // Read back from stream
+    std::istringstream          anIStream(aStlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("test.stl", anIStream));
+
+    TopoDS_Shape aReadShape;
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
+    EXPECT_FALSE(aReadShape.IsNull());
+
+    if (!aReadShape.IsNull())
+    {
+      // STL should produce faces with triangulation
+      Standard_Integer aReadFaces = CountShapeElements(aReadShape, TopAbs_FACE);
+      EXPECT_GT(aReadFaces, 0); // Should have faces
+    }
+  }
+}
+
+// Test stream-based document write and read operations
+TEST_F(DESTL_ProviderTest, StreamDocumentWriteRead)
+{
+  // Add triangulated shape to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aShapeLabel = aShapeTool->AddShape(myTriangularFace);
+  EXPECT_FALSE(aShapeLabel.IsNull());
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("document.stl", anOStream));
+
+  // Write document to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
+
+  std::string aStlContent = anOStream.str();
+  EXPECT_FALSE(aStlContent.empty());
+
+  if (!aStlContent.empty())
+  {
+    // Create new document for reading
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    Handle(TDocStd_Document)    aNewDocument;
+    anApp->NewDocument("BinXCAF", aNewDocument);
+
+    // Read back from stream
+    std::istringstream          anIStream(aStlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("document.stl", anIStream));
+
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
+
+    // Validate document content
+    Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+    TDF_LabelSequence         aLabels;
+    aNewShapeTool->GetShapes(aLabels);
+    EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
+  }
+}
+
+// Test DE_Wrapper integration for STL operations
+TEST_F(DESTL_ProviderTest, DE_WrapperIntegration)
+{
+  // Initialize DE_Wrapper and bind STL provider
+  DE_Wrapper                      aWrapper;
+  Handle(DESTL_ConfigurationNode) aNode = new DESTL_ConfigurationNode();
+
+  // Bind the configured node to wrapper
+  EXPECT_TRUE(aWrapper.Bind(aNode));
+
+  // Test write with DE_Wrapper using triangular face
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("test.stl", anOStream));
+
+  EXPECT_TRUE(aWrapper.Write(aWriteStreams, myTriangularFace));
+
+  std::string aStlContent = anOStream.str();
+  EXPECT_FALSE(aStlContent.empty());
+
+  if (!aStlContent.empty())
+  {
+    // Test DE_Wrapper stream operations
+    std::istringstream          anIStream(aStlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("test.stl", anIStream));
+
+    TopoDS_Shape aReadShape;
+    bool         aWrapperResult = aWrapper.Read(aReadStreams, aReadShape);
+
+    // Test direct provider with same content for comparison
+    std::istringstream          anIStream2(aStlContent);
+    DE_Provider::ReadStreamList aReadStreams2;
+    aReadStreams2.Append(DE_Provider::ReadStreamNode("test.stl", anIStream2));
+
+    Handle(DESTL_Provider) aDirectProvider = new DESTL_Provider(aNode);
+    TopoDS_Shape           aDirectShape;
+    bool                   aDirectResult = aDirectProvider->Read(aReadStreams2, aDirectShape);
+
+    // REQUIREMENT: DE_Wrapper must work exactly the same as direct provider
+    EXPECT_EQ(aWrapperResult, aDirectResult);
+    EXPECT_EQ(aReadShape.IsNull(), aDirectShape.IsNull());
+
+    if (aDirectResult && !aDirectShape.IsNull())
+    {
+      Standard_Integer aFaces = CountShapeElements(aDirectShape, TopAbs_FACE);
+      EXPECT_GT(aFaces, 0);
+    }
+  }
+}
+
+// Test error conditions and edge cases with null document validation
+TEST_F(DESTL_ProviderTest, ErrorHandling)
+{
+  // Test with empty streams
+  DE_Provider::WriteStreamList anEmptyWriteStreams;
+  EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myTriangularFace));
+
+  DE_Provider::ReadStreamList anEmptyReadStreams;
+  TopoDS_Shape                aShape;
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
+
+  // Test with null shape
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("null_test.stl", anOStream));
+  TopoDS_Shape aNullShape;
+
+  // Writing null shape should succeed but produce minimal content
+  myProvider->Write(aWriteStreams, aNullShape);
+  std::string aContent = anOStream.str();
+  // STL may produce empty content for null shape
+
+  // Test reading invalid STL content
+  std::string                 anInvalidContent = "This is not valid STL content";
+  std::istringstream          anInvalidStream(anInvalidContent);
+  DE_Provider::ReadStreamList anInvalidReadStreams;
+  anInvalidReadStreams.Append(DE_Provider::ReadStreamNode("invalid.stl", anInvalidStream));
+
+  TopoDS_Shape anInvalidShape;
+  EXPECT_FALSE(myProvider->Read(anInvalidReadStreams, anInvalidShape));
+
+  // Test with null document
+  Handle(TDocStd_Document) aNullDoc;
+  EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullDoc));
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aNullDoc));
+}
+
+// Test DESTL configuration modes
+TEST_F(DESTL_ProviderTest, ConfigurationModes)
+{
+  Handle(DESTL_ConfigurationNode) aNode =
+    Handle(DESTL_ConfigurationNode)::DownCast(myProvider->GetNode());
+
+  // Test ASCII mode configuration
+  aNode->InternalParameters.WriteAscii = Standard_True;
+  EXPECT_TRUE(aNode->InternalParameters.WriteAscii);
+
+  // Test Binary mode configuration
+  aNode->InternalParameters.WriteAscii = Standard_False;
+  EXPECT_FALSE(aNode->InternalParameters.WriteAscii);
+
+  // Test merge angle configuration
+  aNode->InternalParameters.ReadMergeAngle = 45.0;
+  EXPECT_DOUBLE_EQ(45.0, aNode->InternalParameters.ReadMergeAngle);
+
+  // Test that provider format and vendor are correct
+  EXPECT_STREQ("STL", myProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
+}
+
+// Test ASCII vs Binary mode configurations
+TEST_F(DESTL_ProviderTest, AsciiVsBinaryModes)
+{
+  Handle(DESTL_ConfigurationNode) aNode =
+    Handle(DESTL_ConfigurationNode)::DownCast(myProvider->GetNode());
+
+  // Test ASCII mode
+  aNode->InternalParameters.WriteAscii = Standard_True;
+
+  std::ostringstream           anAsciiStream;
+  DE_Provider::WriteStreamList anAsciiWriteStreams;
+  anAsciiWriteStreams.Append(DE_Provider::WriteStreamNode("ascii_test.stl", anAsciiStream));
+
+  EXPECT_TRUE(myProvider->Write(anAsciiWriteStreams, myTriangularFace));
+  std::string anAsciiContent = anAsciiStream.str();
+  EXPECT_FALSE(anAsciiContent.empty());
+  EXPECT_TRUE(anAsciiContent.find("solid") != std::string::npos);
+
+  // Test Binary mode
+  aNode->InternalParameters.WriteAscii = Standard_False;
+
+  std::ostringstream           aBinaryStream;
+  DE_Provider::WriteStreamList aBinaryWriteStreams;
+  aBinaryWriteStreams.Append(DE_Provider::WriteStreamNode("binary_test.stl", aBinaryStream));
+
+  EXPECT_TRUE(myProvider->Write(aBinaryWriteStreams, myTriangularFace));
+  std::string aBinaryContent = aBinaryStream.str();
+  EXPECT_FALSE(aBinaryContent.empty());
+
+  // Binary and ASCII content should be different
+  EXPECT_NE(anAsciiContent, aBinaryContent);
+}
+
+// Test multiple shapes in single document
+TEST_F(DESTL_ProviderTest, MultipleShapesInDocument)
+{
+  // Add triangulated face to document multiple times (to create multiple shapes)
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aFaceLabel1 = aShapeTool->AddShape(myTriangularFace);
+  TDF_Label aFaceLabel2 = aShapeTool->AddShape(myTriangularFace); // Add same face again
+
+  EXPECT_FALSE(aFaceLabel1.IsNull());
+  EXPECT_FALSE(aFaceLabel2.IsNull());
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("multi_shapes.stl", anOStream));
+
+  // Write document with multiple shapes
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
+
+  std::string aStlContent = anOStream.str();
+  EXPECT_FALSE(aStlContent.empty());
+
+  // Read back into new document
+  Handle(TDocStd_Application) anApp = new TDocStd_Application();
+  Handle(TDocStd_Document)    aNewDocument;
+  anApp->NewDocument("BinXCAF", aNewDocument);
+
+  std::istringstream          anIStream(aStlContent);
+  DE_Provider::ReadStreamList aReadStreams;
+  aReadStreams.Append(DE_Provider::ReadStreamNode("multi_shapes.stl", anIStream));
+
+  EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
+
+  // Validate document content
+  Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+  TDF_LabelSequence         aLabels;
+  aNewShapeTool->GetShapes(aLabels);
+  EXPECT_GT(aLabels.Length(), 0);
+}
+
+// Test triangulated face handling (suitable for STL)
+TEST_F(DESTL_ProviderTest, TriangulatedFaceHandling)
+{
+  // Use our triangulated face which already has mesh data
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("triangulated_face.stl", anOStream));
+
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myTriangularFace));
+
+  std::string aStlContent = anOStream.str();
+  EXPECT_FALSE(aStlContent.empty());
+  EXPECT_GT(aStlContent.size(), 100); // Should have meaningful content
+
+  // Read back
+  std::istringstream          anIStream(aStlContent);
+  DE_Provider::ReadStreamList aReadStreams;
+  aReadStreams.Append(DE_Provider::ReadStreamNode("triangulated_face.stl", anIStream));
+
+  TopoDS_Shape aReadShape;
+  EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
+  EXPECT_FALSE(aReadShape.IsNull());
+}
+
+// Test DE_Wrapper with different file extensions
+TEST_F(DESTL_ProviderTest, DE_WrapperFileExtensions)
+{
+  DE_Wrapper                      aWrapper;
+  Handle(DESTL_ConfigurationNode) aNode = new DESTL_ConfigurationNode();
+  EXPECT_TRUE(aWrapper.Bind(aNode));
+
+  // Test different STL extensions
+  std::vector<std::string> aExtensions = {"test.stl", "test.STL", "mesh.stl"};
+
+  for (const auto& anExt : aExtensions)
+  {
+    std::ostringstream           anOStream;
+    DE_Provider::WriteStreamList aWriteStreams;
+    aWriteStreams.Append(DE_Provider::WriteStreamNode(anExt.c_str(), anOStream));
+
+    EXPECT_TRUE(aWrapper.Write(aWriteStreams, myTriangularFace))
+      << "Failed to write with extension: " << anExt;
+
+    std::string aContent = anOStream.str();
+    EXPECT_FALSE(aContent.empty()) << "Empty content for extension: " << anExt;
+
+    // Test read back
+    std::istringstream          anIStream(aContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode(anExt.c_str(), anIStream));
+
+    TopoDS_Shape aReadShape;
+    EXPECT_TRUE(aWrapper.Read(aReadStreams, aReadShape))
+      << "Failed to read with extension: " << anExt;
+    EXPECT_FALSE(aReadShape.IsNull()) << "Null shape read with extension: " << anExt;
+  }
+}
+
+// Test stream operations with empty and invalid data
+TEST_F(DESTL_ProviderTest, StreamErrorConditions)
+{
+  // Test empty stream list
+  DE_Provider::WriteStreamList anEmptyWriteStreams;
+  EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myTriangularFace));
+
+  DE_Provider::ReadStreamList anEmptyReadStreams;
+  TopoDS_Shape                aShape;
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
+
+  // Test corrupted STL data
+  std::string                 aCorruptedData = "This is not STL data at all";
+  std::istringstream          aCorruptedStream(aCorruptedData);
+  DE_Provider::ReadStreamList aCorruptedReadStreams;
+  aCorruptedReadStreams.Append(DE_Provider::ReadStreamNode("corrupted.stl", aCorruptedStream));
+
+  TopoDS_Shape aCorruptedShape;
+  EXPECT_FALSE(myProvider->Read(aCorruptedReadStreams, aCorruptedShape));
+
+  // Test partial STL data
+  std::string                 aPartialData = "solid test\n  facet normal 0 0 1\n"; // Incomplete
+  std::istringstream          aPartialStream(aPartialData);
+  DE_Provider::ReadStreamList aPartialReadStreams;
+  aPartialReadStreams.Append(DE_Provider::ReadStreamNode("partial.stl", aPartialStream));
+
+  TopoDS_Shape aPartialShape;
+  EXPECT_FALSE(myProvider->Read(aPartialReadStreams, aPartialShape));
+}
+
+// Test configuration parameter validation
+TEST_F(DESTL_ProviderTest, ConfigurationParameterValidation)
+{
+  Handle(DESTL_ConfigurationNode) aNode =
+    Handle(DESTL_ConfigurationNode)::DownCast(myProvider->GetNode());
+
+  // Test valid merge angle
+  aNode->InternalParameters.ReadMergeAngle = 30.0;
+  EXPECT_DOUBLE_EQ(30.0, aNode->InternalParameters.ReadMergeAngle);
+
+  // Test edge case merge angles
+  aNode->InternalParameters.ReadMergeAngle = 0.0; // Minimum
+  EXPECT_DOUBLE_EQ(0.0, aNode->InternalParameters.ReadMergeAngle);
+
+  aNode->InternalParameters.ReadMergeAngle = 90.0; // Maximum
+  EXPECT_DOUBLE_EQ(90.0, aNode->InternalParameters.ReadMergeAngle);
+
+  // Test BRep vs triangulation modes
+  aNode->InternalParameters.ReadBRep = Standard_True;
+  EXPECT_TRUE(aNode->InternalParameters.ReadBRep);
+
+  aNode->InternalParameters.ReadBRep = Standard_False;
+  EXPECT_FALSE(aNode->InternalParameters.ReadBRep);
+}
+
+// Test multiple triangulated faces
+TEST_F(DESTL_ProviderTest, MultipleTriangulatedFaces)
+{
+  // Create another triangulated face with different geometry
+  TColgp_Array1OfPnt aNodes(1, 3);
+  aNodes.SetValue(1, gp_Pnt(0.0, 0.0, 0.0));
+  aNodes.SetValue(2, gp_Pnt(5.0, 0.0, 0.0));
+  aNodes.SetValue(3, gp_Pnt(2.5, 5.0, 0.0));
+
+  Poly_Array1OfTriangle aTriangles(1, 1);
+  aTriangles.SetValue(1, Poly_Triangle(1, 2, 3));
+
+  Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(aNodes, aTriangles);
+
+  gp_Pln                  aPlane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0, 0, 1));
+  BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 0.0, 5.0, 0.0, 5.0);
+  TopoDS_Face             aTriangleFace = aFaceBuilder.Face();
+
+  BRep_Builder aBuilder;
+  aBuilder.UpdateFace(aTriangleFace, aTriangulation);
+
+  // Test first triangulated face
+  std::ostringstream           aStream1;
+  DE_Provider::WriteStreamList aWriteStreams1;
+  aWriteStreams1.Append(DE_Provider::WriteStreamNode("face1.stl", aStream1));
+
+  EXPECT_TRUE(myProvider->Write(aWriteStreams1, myTriangularFace));
+  std::string aContent1 = aStream1.str();
+
+  // Test second triangulated face
+  std::ostringstream           aStream2;
+  DE_Provider::WriteStreamList aWriteStreams2;
+  aWriteStreams2.Append(DE_Provider::WriteStreamNode("face2.stl", aStream2));
+
+  EXPECT_TRUE(myProvider->Write(aWriteStreams2, aTriangleFace));
+  std::string aContent2 = aStream2.str();
+
+  // Both content should be non-empty
+  EXPECT_FALSE(aContent1.empty());
+  EXPECT_FALSE(aContent2.empty());
+
+  // Different triangulated faces should produce different STL content
+  EXPECT_NE(aContent1, aContent2);
+
+  // Both should read back successfully
+  std::istringstream          aIStream1(aContent1);
+  DE_Provider::ReadStreamList aReadStreams1;
+  aReadStreams1.Append(DE_Provider::ReadStreamNode("face1.stl", aIStream1));
+
+  TopoDS_Shape aReadShape1;
+  EXPECT_TRUE(myProvider->Read(aReadStreams1, aReadShape1));
+  EXPECT_FALSE(aReadShape1.IsNull());
+
+  std::istringstream          aIStream2(aContent2);
+  DE_Provider::ReadStreamList aReadStreams2;
+  aReadStreams2.Append(DE_Provider::ReadStreamNode("face2.stl", aIStream2));
+
+  TopoDS_Shape aReadShape2;
+  EXPECT_TRUE(myProvider->Read(aReadStreams2, aReadShape2));
+  EXPECT_FALSE(aReadShape2.IsNull());
+}
index 0a43c909d6b296ba476c7d42215793f63c729827..e88079fa28c84e0fc702575811e2e615bdaedb99 100644 (file)
@@ -2,4 +2,5 @@
 set(OCCT_TKDESTL_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
 
 set(OCCT_TKDESTL_GTests_FILES
+  DESTL_Provider_Test.cxx
 )
index 9ffdb2de47b571dd2b7e973c5891596ac9538d2e..5568f374ddb00c1873cccb12565520f03b71e653 100644 (file)
 #include <OSD_FileSystem.hxx>
 #include <OSD_OpenFile.hxx>
 #include <RWStl_Reader.hxx>
+#include <Standard_ReadLineBuffer.hxx>
+#include <iomanip>
+#include <iostream>
+#include <cstring>
 
 namespace
 {
@@ -254,16 +258,13 @@ Standard_Boolean RWStl::WriteBinary(const Handle(Poly_Triangulation)& theMesh,
   TCollection_AsciiString aPath;
   thePath.SystemName(aPath);
 
-  FILE* aFile = OSD_OpenFile(aPath, "wb");
-  if (aFile == NULL)
+  std::ofstream aStream(aPath.ToCString(), std::ios::binary);
+  if (!aStream.is_open())
   {
     return Standard_False;
   }
 
-  Standard_Boolean isOK = writeBinary(theMesh, aFile, theProgress);
-
-  fclose(aFile);
-  return isOK;
+  return WriteBinary(theMesh, aStream, theProgress);
 }
 
 //=================================================================================================
@@ -280,45 +281,44 @@ Standard_Boolean RWStl::WriteAscii(const Handle(Poly_Triangulation)& theMesh,
   TCollection_AsciiString aPath;
   thePath.SystemName(aPath);
 
-  FILE* aFile = OSD_OpenFile(aPath, "w");
-  if (aFile == NULL)
+  std::ofstream aStream(aPath.ToCString());
+  if (!aStream.is_open())
   {
     return Standard_False;
   }
 
-  Standard_Boolean isOK = writeASCII(theMesh, aFile, theProgress);
-  fclose(aFile);
-  return isOK;
+  return WriteAscii(theMesh, aStream, theProgress);
 }
 
 //=================================================================================================
 
-Standard_Boolean RWStl::writeASCII(const Handle(Poly_Triangulation)& theMesh,
-                                   FILE*                             theFile,
+Standard_Boolean RWStl::WriteAscii(const Handle(Poly_Triangulation)& theMesh,
+                                   Standard_OStream&                 theStream,
                                    const Message_ProgressRange&      theProgress)
 {
-  // note that space after 'solid' is necessary for many systems
-  if (fwrite("solid \n", 1, 7, theFile) != 7)
+  if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
   {
     return Standard_False;
   }
 
-  char aBuffer[512];
-  memset(aBuffer, 0, sizeof(aBuffer));
+  // note that space after 'solid' is necessary for many systems
+  theStream << "solid \n";
+  if (theStream.fail())
+  {
+    return Standard_False;
+  }
 
   const Standard_Integer NBTriangles = theMesh->NbTriangles();
   Message_ProgressScope  aPS(theProgress, "Triangles", NBTriangles);
+  Standard_Integer       anElem[3] = {0, 0, 0};
 
-  Standard_Integer anElem[3] = {0, 0, 0};
   for (Standard_Integer aTriIter = 1; aTriIter <= NBTriangles; ++aTriIter)
   {
     const Poly_Triangle aTriangle = theMesh->Triangle(aTriIter);
     aTriangle.Get(anElem[0], anElem[1], anElem[2]);
-
     const gp_Pnt aP1 = theMesh->Node(anElem[0]);
     const gp_Pnt aP2 = theMesh->Node(anElem[1]);
     const gp_Pnt aP3 = theMesh->Node(anElem[2]);
-
     const gp_Vec aVec1(aP1, aP2);
     const gp_Vec aVec2(aP1, aP3);
     gp_Vec       aVNorm = aVec1.Crossed(aVec2);
@@ -331,28 +331,16 @@ Standard_Boolean RWStl::writeASCII(const Handle(Poly_Triangulation)& theMesh,
       aVNorm.SetCoord(0.0, 0.0, 0.0);
     }
 
-    Sprintf(aBuffer,
-            " facet normal % 12e % 12e % 12e\n"
-            "   outer loop\n"
-            "     vertex % 12e % 12e % 12e\n"
-            "     vertex % 12e % 12e % 12e\n"
-            "     vertex % 12e % 12e % 12e\n"
-            "   endloop\n"
-            " endfacet\n",
-            aVNorm.X(),
-            aVNorm.Y(),
-            aVNorm.Z(),
-            aP1.X(),
-            aP1.Y(),
-            aP1.Z(),
-            aP2.X(),
-            aP2.Y(),
-            aP2.Z(),
-            aP3.X(),
-            aP3.Y(),
-            aP3.Z());
-
-    if (fprintf(theFile, "%s", aBuffer) < 0)
+    theStream << " facet normal " << std::scientific << std::setprecision(12) << aVNorm.X() << " "
+              << aVNorm.Y() << " " << aVNorm.Z() << "\n"
+              << "   outer loop\n"
+              << "     vertex " << aP1.X() << " " << aP1.Y() << " " << aP1.Z() << "\n"
+              << "     vertex " << aP2.X() << " " << aP2.Y() << " " << aP2.Z() << "\n"
+              << "     vertex " << aP3.X() << " " << aP3.Y() << " " << aP3.Z() << "\n"
+              << "   endloop\n"
+              << " endfacet\n";
+
+    if (theStream.fail())
     {
       return Standard_False;
     }
@@ -366,37 +354,38 @@ Standard_Boolean RWStl::writeASCII(const Handle(Poly_Triangulation)& theMesh,
     }
   }
 
-  if (fwrite("endsolid\n", 1, 9, theFile) != 9)
-  {
-    return Standard_False;
-  }
-
-  return Standard_True;
+  theStream << "endsolid\n";
+  return !theStream.fail();
 }
 
 //=================================================================================================
 
-Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
-                                    FILE*                             theFile,
+Standard_Boolean RWStl::WriteBinary(const Handle(Poly_Triangulation)& theMesh,
+                                    Standard_OStream&                 theStream,
                                     const Message_ProgressRange&      theProgress)
 {
-  char aHeader[80] = "STL Exported by Open CASCADE Technology [dev.opencascade.org]";
-  if (fwrite(aHeader, 1, 80, theFile) != 80)
+  if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
   {
     return Standard_False;
   }
 
-  const Standard_Integer aNBTriangles = theMesh->NbTriangles();
-  Message_ProgressScope  aPS(theProgress, "Triangles", aNBTriangles);
+  char aHeader[80] = "STL Exported by Open CASCADE Technology [dev.opencascade.org]";
+  theStream.write(aHeader, 80);
+  if (theStream.fail())
+  {
+    return Standard_False;
+  }
 
+  const Standard_Integer                 aNBTriangles = theMesh->NbTriangles();
+  Message_ProgressScope                  aPS(theProgress, "Triangles", aNBTriangles);
   const Standard_Size                    aNbChunkTriangles = 4096;
   const Standard_Size                    aChunkSize = aNbChunkTriangles * THE_STL_SIZEOF_FACET;
   NCollection_Array1<Standard_Character> aData(1, aChunkSize);
   Standard_Character*                    aDataChunk = &aData.ChangeFirst();
-
-  Standard_Character aConv[4];
+  Standard_Character                     aConv[4];
   convertInteger(aNBTriangles, aConv);
-  if (fwrite(aConv, 1, 4, theFile) != 4)
+  theStream.write(aConv, 4);
+  if (theStream.fail())
   {
     return Standard_False;
   }
@@ -407,14 +396,12 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
     Standard_Integer    id[3];
     const Poly_Triangle aTriangle = theMesh->Triangle(aTriIter);
     aTriangle.Get(id[0], id[1], id[2]);
-
     const gp_Pnt aP1 = theMesh->Node(id[0]);
     const gp_Pnt aP2 = theMesh->Node(id[1]);
     const gp_Pnt aP3 = theMesh->Node(id[2]);
-
-    gp_Vec aVec1(aP1, aP2);
-    gp_Vec aVec2(aP1, aP3);
-    gp_Vec aVNorm = aVec1.Crossed(aVec2);
+    gp_Vec       aVec1(aP1, aP2);
+    gp_Vec       aVec2(aP1, aP3);
+    gp_Vec       aVNorm = aVec1.Crossed(aVec2);
     if (aVNorm.SquareMagnitude() > gp::Resolution())
     {
       aVNorm.Normalize();
@@ -423,48 +410,42 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
     {
       aVNorm.SetCoord(0.0, 0.0, 0.0);
     }
-
     convertDouble(aVNorm.X(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aVNorm.Y(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aVNorm.Z(), &aDataChunk[aByteCount]);
     aByteCount += 4;
-
     convertDouble(aP1.X(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aP1.Y(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aP1.Z(), &aDataChunk[aByteCount]);
     aByteCount += 4;
-
     convertDouble(aP2.X(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aP2.Y(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aP2.Z(), &aDataChunk[aByteCount]);
     aByteCount += 4;
-
     convertDouble(aP3.X(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aP3.Y(), &aDataChunk[aByteCount]);
     aByteCount += 4;
     convertDouble(aP3.Z(), &aDataChunk[aByteCount]);
     aByteCount += 4;
+    aDataChunk[aByteCount]     = 0;
+    aDataChunk[aByteCount + 1] = 0;
+    aByteCount += 2;
 
-    aDataChunk[aByteCount] = 0;
-    aByteCount += 1;
-    aDataChunk[aByteCount] = 0;
-    aByteCount += 1;
-
-    // Chunk is filled. Dump it to the file.
+    // Chunk is full, write it out.
     if (aByteCount == aChunkSize)
     {
-      if (fwrite(aDataChunk, 1, aChunkSize, theFile) != aChunkSize)
+      theStream.write(aDataChunk, aChunkSize);
+      if (theStream.fail())
       {
         return Standard_False;
       }
-
       aByteCount = 0;
     }
 
@@ -480,7 +461,8 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
   // Write last part if necessary.
   if (aByteCount != aChunkSize)
   {
-    if (fwrite(aDataChunk, 1, aByteCount, theFile) != aByteCount)
+    theStream.write(aDataChunk, aByteCount);
+    if (theStream.fail())
     {
       return Standard_False;
     }
@@ -488,3 +470,66 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
 
   return Standard_True;
 }
+
+//=================================================================================================
+
+Handle(Poly_Triangulation) RWStl::ReadBinaryStream(Standard_IStream&            theStream,
+                                                   const Standard_Real          theMergeAngle,
+                                                   const Message_ProgressRange& theProgress)
+{
+  Reader aReader;
+  aReader.SetMergeAngle(theMergeAngle);
+  if (!aReader.ReadBinary(theStream, theProgress))
+  {
+    return Handle(Poly_Triangulation)();
+  }
+  return aReader.GetTriangulation();
+}
+
+//=================================================================================================
+
+Handle(Poly_Triangulation) RWStl::ReadAsciiStream(Standard_IStream&            theStream,
+                                                  const Standard_Real          theMergeAngle,
+                                                  const Message_ProgressRange& theProgress)
+{
+  Reader aReader;
+  aReader.SetMergeAngle(theMergeAngle);
+
+  // get length of stream to feed progress indicator
+  theStream.seekg(0, theStream.end);
+  std::streampos theEnd = theStream.tellg();
+  theStream.seekg(0, theStream.beg);
+
+  Standard_ReadLineBuffer aBuffer(THE_BUFFER_SIZE);
+  if (!aReader.ReadAscii(theStream, aBuffer, theEnd, theProgress))
+  {
+    return Handle(Poly_Triangulation)();
+  }
+  return aReader.GetTriangulation();
+}
+
+//=================================================================================================
+
+Handle(Poly_Triangulation) RWStl::ReadStream(Standard_IStream&            theStream,
+                                             const Standard_Real          theMergeAngle,
+                                             const Message_ProgressRange& theProgress)
+{
+  // Try to detect ASCII vs Binary format by peeking at the first few bytes
+  std::streampos            anOriginalPos = theStream.tellg();
+  constexpr std::streamsize aHeaderSize   = 6;
+  char                      aHeader[aHeaderSize];
+  theStream.read(aHeader, aHeaderSize - 1);
+  aHeader[aHeaderSize - 1] = '\0';
+  theStream.seekg(anOriginalPos);
+
+  bool isAscii = (strncmp(aHeader, "solid", 5) == 0);
+
+  if (isAscii)
+  {
+    return RWStl::ReadAsciiStream(theStream, theMergeAngle, theProgress);
+  }
+  else
+  {
+    return RWStl::ReadBinaryStream(theStream, theMergeAngle, theProgress);
+  }
+}
index f8b519ea36b101f9c92ce864252ae88202b1eb11..058a3b84952bc3c1561d6757e49205b9145b7fa8 100644 (file)
@@ -34,6 +34,12 @@ public:
     const OSD_Path&                   thePath,
     const Message_ProgressRange&      theProgress = Message_ProgressRange());
 
+  //! Write triangulation to binary STL stream.
+  Standard_EXPORT static Standard_Boolean WriteBinary(
+    const Handle(Poly_Triangulation)& theMesh,
+    Standard_OStream&                 theStream,
+    const Message_ProgressRange&      theProgress = Message_ProgressRange());
+
   //! write the meshing in a file following the
   //! Ascii  format of an STL file.
   //! Returns false if the cannot be opened;
@@ -42,6 +48,12 @@ public:
     const OSD_Path&                   thePath,
     const Message_ProgressRange&      theProgress = Message_ProgressRange());
 
+  //! Write triangulation to ASCII STL stream.
+  Standard_EXPORT static Standard_Boolean WriteAscii(
+    const Handle(Poly_Triangulation)& theMesh,
+    Standard_OStream&                 theStream,
+    const Message_ProgressRange&      theProgress = Message_ProgressRange());
+
   //! Read specified STL file and returns its content as triangulation.
   //! In case of error, returns Null handle.
   Standard_EXPORT static Handle(Poly_Triangulation) ReadFile(
@@ -92,16 +104,26 @@ public:
     const OSD_Path&              thePath,
     const Message_ProgressRange& theProgress = Message_ProgressRange());
 
-private:
-  //! Write ASCII version.
-  static Standard_Boolean writeASCII(const Handle(Poly_Triangulation)& theMesh,
-                                     FILE*                             theFile,
-                                     const Message_ProgressRange&      theProgress);
+  //! Read triangulation from binary STL stream
+  //! In case of error, returns Null handle.
+  Standard_EXPORT static Handle(Poly_Triangulation) ReadBinaryStream(
+    Standard_IStream&            theStream,
+    const Standard_Real          theMergeAngle = M_PI / 2.0,
+    const Message_ProgressRange& theProgress   = Message_ProgressRange());
+
+  //! Read triangulation from ASCII STL stream
+  //! In case of error, returns Null handle.
+  Standard_EXPORT static Handle(Poly_Triangulation) ReadAsciiStream(
+    Standard_IStream&            theStream,
+    const Standard_Real          theMergeAngle = M_PI / 2.0,
+    const Message_ProgressRange& theProgress   = Message_ProgressRange());
 
-  //! Write binary version.
-  static Standard_Boolean writeBinary(const Handle(Poly_Triangulation)& theMesh,
-                                      FILE*                             theFile,
-                                      const Message_ProgressRange&      theProgress);
+  //! Read STL data from stream (auto-detects ASCII vs Binary)
+  //! In case of error, returns Null handle.
+  Standard_EXPORT static Handle(Poly_Triangulation) ReadStream(
+    Standard_IStream&            theStream,
+    const Standard_Real          theMergeAngle = M_PI / 2.0,
+    const Message_ProgressRange& theProgress   = Message_ProgressRange());
 };
 
 #endif
index 5eb075a8e670df896e5261acc23abcb791ea8e8c..4f501997e233049c465aee86ddae7483d5e77601 100644 (file)
@@ -37,3 +37,24 @@ Standard_Boolean StlAPI_Reader::Read(TopoDS_Shape& theShape, const Standard_CStr
   theShape = aResult;
   return Standard_True;
 }
+
+//=================================================================================================
+
+Standard_Boolean StlAPI_Reader::Read(TopoDS_Shape& theShape, Standard_IStream& theStream)
+{
+  Handle(Poly_Triangulation) aMesh = RWStl::ReadStream(theStream);
+  if (aMesh.IsNull())
+    return Standard_False;
+
+  BRepBuilderAPI_MakeShapeOnMesh aConverter(aMesh);
+  aConverter.Build();
+  if (!aConverter.IsDone())
+    return Standard_False;
+
+  TopoDS_Shape aResult = aConverter.Shape();
+  if (aResult.IsNull())
+    return Standard_False;
+
+  theShape = aResult;
+  return Standard_True;
+}
index c8ccea531bf057efcc87159dfd014e2418cd2840..709d2a4aef6b496d7ab29141bb0c80970d331cde 100644 (file)
@@ -17,6 +17,7 @@
 #define _StlAPI_Reader_HeaderFile
 
 #include <Standard_Handle.hxx>
+#include <Standard_IStream.hxx>
 
 class TopoDS_Shape;
 
@@ -30,6 +31,12 @@ public:
   //! Reads STL file to the TopoDS_Shape (each triangle is converted to the face).
   //! @return True if reading is successful
   Standard_EXPORT Standard_Boolean Read(TopoDS_Shape& theShape, const Standard_CString theFileName);
+
+  //! Reads STL data from stream to the TopoDS_Shape (each triangle is converted to the face).
+  //! @param theShape result shape
+  //! @param theStream stream to read from
+  //! @return True if reading is successful
+  Standard_EXPORT Standard_Boolean Read(TopoDS_Shape& theShape, Standard_IStream& theStream);
 };
 
 #endif // _StlAPI_Reader_HeaderFile
index 3f9eb3fb3a464d08681fc7070eb73d3784649a66..73b1d5c62d8fbd7c85409868ab3cf4ce49eb0e3d 100644 (file)
@@ -22,6 +22,7 @@
 #include <TopoDS_Face.hxx>
 #include <TopExp_Explorer.hxx>
 #include <Poly_Triangulation.hxx>
+#include <fstream>
 
 //=================================================================================================
 
@@ -36,6 +37,21 @@ StlAPI_Writer::StlAPI_Writer()
 Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape&          theShape,
                                       const Standard_CString       theFileName,
                                       const Message_ProgressRange& theProgress)
+{
+  std::ofstream aStream(theFileName, myASCIIMode ? std::ios::out : std::ios::binary);
+  if (!aStream.is_open())
+  {
+    return Standard_False;
+  }
+
+  return Write(theShape, aStream, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape&          theShape,
+                                      Standard_OStream&            theStream,
+                                      const Message_ProgressRange& theProgress)
 {
   Standard_Integer aNbNodes     = 0;
   Standard_Integer aNbTriangles = 0;
@@ -115,9 +131,8 @@ Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape&          theShape,
     aTriangleOffet += aTriangulation->NbTriangles();
   }
 
-  OSD_Path         aPath(theFileName);
-  Standard_Boolean isDone = (myASCIIMode ? RWStl::WriteAscii(aMesh, aPath, theProgress)
-                                         : RWStl::WriteBinary(aMesh, aPath, theProgress));
+  Standard_Boolean isDone = (myASCIIMode ? RWStl::WriteAscii(aMesh, theStream, theProgress)
+                                         : RWStl::WriteBinary(aMesh, theStream, theProgress));
 
   if (isDone && (aNbFacesNoTri > 0))
   {
@@ -130,4 +145,4 @@ Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape&          theShape,
   }
 
   return isDone;
-}
+}
\ No newline at end of file
index f5841753f23c91761828673b88e67aea0785a6c8..a228db85a63ba7216b3649f388a64f730cbf4625 100644 (file)
@@ -45,6 +45,13 @@ public:
           const Standard_CString       theFileName,
           const Message_ProgressRange& theProgress = Message_ProgressRange());
 
+  //! Converts a given shape to STL format and writes it to the specified stream.
+  //! \return the error state.
+  Standard_EXPORT Standard_Boolean
+    Write(const TopoDS_Shape&          theShape,
+          Standard_OStream&            theStream,
+          const Message_ProgressRange& theProgress = Message_ProgressRange());
+
 private:
   Standard_Boolean myASCIIMode;
 };
index eb03f3fd18034812838bf9a0884de10eaa0c2c53..66e48f77b3f69fd9bcbe32b0bdfe6930e8a1d355 100644 (file)
@@ -172,6 +172,13 @@ bool DEVRML_ConfigurationNode::IsExportSupported() const
 
 //=================================================================================================
 
+bool DEVRML_ConfigurationNode::IsStreamSupported() const
+{
+  return true;
+}
+
+//=================================================================================================
+
 TCollection_AsciiString DEVRML_ConfigurationNode::GetFormat() const
 {
   return TCollection_AsciiString("VRML");
index 6371244d6f200d3cec35701fe729a624e1531ada..92aaecfe8e3780f908eee4299b47e3d2efcefa31 100644 (file)
@@ -65,6 +65,10 @@ public:
   //! @return true if export is supported
   Standard_EXPORT virtual bool IsExportSupported() const Standard_OVERRIDE;
 
+  //! Checks for stream support.
+  //! @return Standard_True if streams are supported
+  Standard_EXPORT virtual bool IsStreamSupported() const Standard_OVERRIDE;
+
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
   Standard_EXPORT virtual TCollection_AsciiString GetFormat() const Standard_OVERRIDE;
index d98be8a987653b6f044c963a12722d10029ad4c4..9b2652eec8ac604571c7ad873f63e192a14a9ff3 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <DEVRML_Provider.hxx>
 
+#include <DE_ValidationUtils.hxx>
 #include <DEVRML_ConfigurationNode.hxx>
 #include <Message.hxx>
 #include <OSD_Path.hxx>
 #include <XCAFDoc_DocumentTool.hxx>
 #include <XCAFDoc_ShapeTool.hxx>
 
+#include <stdexcept>
+
 IMPLEMENT_STANDARD_RTTIEXT(DEVRML_Provider, DE_Provider)
 
+namespace
+{
+// Helper function to validate configuration node and downcast
+static Handle(DEVRML_ConfigurationNode) ValidateConfigurationNode(
+  const Handle(DE_ConfigurationNode)& theNode,
+  const TCollection_AsciiString&      theContext)
+{
+  if (!DE_ValidationUtils::ValidateConfigurationNode(theNode,
+                                                     STANDARD_TYPE(DEVRML_ConfigurationNode),
+                                                     theContext))
+  {
+    return Handle(DEVRML_ConfigurationNode)();
+  }
+  return Handle(DEVRML_ConfigurationNode)::DownCast(theNode);
+}
+
+// Static function to handle VrmlData_Scene status errors
+static Standard_Boolean HandleVrmlSceneStatus(const VrmlData_Scene&          theScene,
+                                              const TCollection_AsciiString& theContext)
+{
+  const char* aStr = nullptr;
+  switch (theScene.Status())
+  {
+    case VrmlData_StatusOK:
+      return Standard_True;
+    case VrmlData_EmptyData:
+      aStr = "EmptyData";
+      break;
+    case VrmlData_UnrecoverableError:
+      aStr = "UnrecoverableError";
+      break;
+    case VrmlData_GeneralError:
+      aStr = "GeneralError";
+      break;
+    case VrmlData_EndOfFile:
+      aStr = "EndOfFile";
+      break;
+    case VrmlData_NotVrmlFile:
+      aStr = "NotVrmlFile";
+      break;
+    case VrmlData_CannotOpenFile:
+      aStr = "CannotOpenFile";
+      break;
+    case VrmlData_VrmlFormatError:
+      aStr = "VrmlFormatError";
+      break;
+    case VrmlData_NumericInputError:
+      aStr = "NumericInputError";
+      break;
+    case VrmlData_IrrelevantNumber:
+      aStr = "IrrelevantNumber";
+      break;
+    case VrmlData_BooleanInputError:
+      aStr = "BooleanInputError";
+      break;
+    case VrmlData_StringInputError:
+      aStr = "StringInputError";
+      break;
+    case VrmlData_NodeNameUnknown:
+      aStr = "NodeNameUnknown";
+      break;
+    case VrmlData_NonPositiveSize:
+      aStr = "NonPositiveSize";
+      break;
+    case VrmlData_ReadUnknownNode:
+      aStr = "ReadUnknownNode";
+      break;
+    case VrmlData_NonSupportedFeature:
+      aStr = "NonSupportedFeature";
+      break;
+    case VrmlData_OutputStreamUndefined:
+      aStr = "OutputStreamUndefined";
+      break;
+    case VrmlData_NotImplemented:
+      aStr = "NotImplemented";
+      break;
+    default:
+      break;
+  }
+
+  if (aStr)
+  {
+    Message::SendFail() << "Error in the DEVRML_Provider during " << theContext
+                        << ": ++ VRML Error: " << aStr << " in line " << theScene.GetLineError();
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+// Static function to calculate scaling factor
+static Standard_Real CalculateScalingFactor(const Handle(TDocStd_Document)&         theDocument,
+                                            const Handle(DEVRML_ConfigurationNode)& theNode,
+                                            const TCollection_AsciiString&          theContext)
+{
+  Standard_Real aScaling       = 1.;
+  Standard_Real aScaleFactorMM = 1.;
+  if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
+                                          aScaleFactorMM,
+                                          UnitsMethods_LengthUnit_Millimeter))
+  {
+    aScaling = aScaleFactorMM / theNode->GlobalParameters.LengthUnit;
+  }
+  else
+  {
+    aScaling = theNode->GlobalParameters.SystemUnit / theNode->GlobalParameters.LengthUnit;
+    Message::SendWarning()
+      << "Warning in the DEVRML_Provider during " << theContext
+      << ": The document has no information on Units. Using global parameter as initial Unit.";
+  }
+  return aScaling;
+}
+
+// Static function to extract VRML directory path from file path
+static TCollection_AsciiString ExtractVrmlDirectory(const TCollection_AsciiString& thePath)
+{
+  OSD_Path                aPath(thePath.ToCString());
+  TCollection_AsciiString aVrmlDir(".");
+  TCollection_AsciiString aDisk = aPath.Disk();
+  TCollection_AsciiString aTrek = aPath.Trek();
+  if (!aTrek.IsEmpty())
+  {
+    if (!aDisk.IsEmpty())
+    {
+      aVrmlDir = aDisk;
+    }
+    else
+    {
+      aVrmlDir.Clear();
+    }
+    aTrek.ChangeAll('|', '/');
+    aVrmlDir += aTrek;
+  }
+  return aVrmlDir;
+}
+
+// Static function to process VRML scene from stream and extract shape
+static Standard_Boolean ProcessVrmlScene(Standard_IStream&                       theStream,
+                                         const Handle(DEVRML_ConfigurationNode)& theNode,
+                                         const TCollection_AsciiString&          theVrmlDir,
+                                         TopoDS_Shape&                           theShape,
+                                         const TCollection_AsciiString&          theContext)
+{
+  VrmlData_Scene aScene;
+  aScene.SetLinearScale(theNode->GlobalParameters.LengthUnit);
+  aScene.SetVrmlDir(theVrmlDir);
+
+  aScene << theStream;
+
+  if (!HandleVrmlSceneStatus(aScene, theContext))
+  {
+    return Standard_False;
+  }
+
+  if (aScene.Status() == VrmlData_StatusOK)
+  {
+    VrmlData_DataMapOfShapeAppearance aShapeAppMap;
+    TopoDS_Shape                      aShape = aScene.GetShape(aShapeAppMap);
+    theShape                                 = aShape;
+
+    // Verify that a valid shape was extracted
+    if (theShape.IsNull())
+    {
+      Message::SendFail() << "Error in the DEVRML_Provider during " << theContext
+                          << ": VRML scene processed successfully but no geometry was extracted";
+      return Standard_False;
+    }
+  }
+  else
+  {
+    // Scene status was not OK but HandleVrmlSceneStatus didn't catch it
+    Message::SendFail() << "Error in the DEVRML_Provider during " << theContext
+                        << ": VRML scene status is not OK but no specific error was reported";
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+} // namespace
+
 //=================================================================================================
 
 DEVRML_Provider::DEVRML_Provider() {}
@@ -64,19 +246,16 @@ bool DEVRML_Provider::Read(const TCollection_AsciiString&  thePath,
                            const Handle(TDocStd_Document)& theDocument,
                            const Message_ProgressRange&    theProgress)
 {
-  if (theDocument.IsNull())
+  TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
   {
-    Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
-                        << "\t: theDocument shouldn't be null";
     return false;
   }
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEVRML_ConfigurationNode)))
+  Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aContext);
+  if (aNode.IsNull())
   {
-    Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
-  Handle(DEVRML_ConfigurationNode) aNode = Handle(DEVRML_ConfigurationNode)::DownCast(GetNode());
 
   VrmlAPI_CafReader aVrmlReader;
   aVrmlReader.SetDocument(theDocument);
@@ -110,32 +289,24 @@ bool DEVRML_Provider::Write(const TCollection_AsciiString&  thePath,
                             const Message_ProgressRange&    theProgress)
 {
   (void)theProgress;
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEVRML_ConfigurationNode)))
+  TCollection_AsciiString aContext = "writing the file ";
+  aContext += thePath;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
+  {
+    return false;
+  }
+
+  Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aContext);
+  if (aNode.IsNull())
   {
-    Message::SendFail() << "Error in the DEVRML_Provider during writing the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
-  Handle(DEVRML_ConfigurationNode) aNode = Handle(DEVRML_ConfigurationNode)::DownCast(GetNode());
 
   VrmlAPI_Writer aWriter;
   aWriter.SetRepresentation(
     static_cast<VrmlAPI_RepresentationOfShape>(aNode->InternalParameters.WriteRepresentationType));
-  Standard_Real aScaling       = 1.;
-  Standard_Real aScaleFactorMM = 1.;
-  if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
-                                          aScaleFactorMM,
-                                          UnitsMethods_LengthUnit_Millimeter))
-  {
-    aScaling = aScaleFactorMM / aNode->GlobalParameters.LengthUnit;
-  }
-  else
-  {
-    aScaling = aNode->GlobalParameters.SystemUnit / aNode->GlobalParameters.LengthUnit;
-    Message::SendWarning()
-      << "Warning in the DEVRML_Provider during writing the file " << thePath
-      << "\t: The document has no information on Units. Using global parameter as initial Unit.";
-  }
+
+  Standard_Real aScaling = CalculateScalingFactor(theDocument, aNode, aContext);
   if (!aWriter.WriteDoc(theDocument, thePath.ToCString(), aScaling))
   {
     Message::SendFail() << "Error in the DEVRML_Provider during wtiting the file " << thePath
@@ -175,125 +346,26 @@ bool DEVRML_Provider::Read(const TCollection_AsciiString& thePath,
                            const Message_ProgressRange&   theProgress)
 {
   (void)theProgress;
-  if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEVRML_ConfigurationNode)))
+  TCollection_AsciiString aContext = "reading the file ";
+  aContext += thePath;
+  Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aContext);
+  if (aNode.IsNull())
   {
-    Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
-                        << "\t: Incorrect or empty Configuration Node";
     return false;
   }
-  Handle(DEVRML_ConfigurationNode) aNode = Handle(DEVRML_ConfigurationNode)::DownCast(GetNode());
-
-  TopoDS_Shape                      aShape;
-  VrmlData_DataMapOfShapeAppearance aShapeAppMap;
 
   std::filebuf aFic;
   std::istream aStream(&aFic);
 
-  if (aFic.open(thePath.ToCString(), std::ios::in))
-  {
-    // Get path of the VRML file.
-    OSD_Path                aPath(thePath.ToCString());
-    TCollection_AsciiString aVrmlDir(".");
-    TCollection_AsciiString aDisk = aPath.Disk();
-    TCollection_AsciiString aTrek = aPath.Trek();
-    if (!aTrek.IsEmpty())
-    {
-      if (!aDisk.IsEmpty())
-      {
-        aVrmlDir = aDisk;
-      }
-      else
-      {
-        aVrmlDir.Clear();
-      }
-      aTrek.ChangeAll('|', '/');
-      aVrmlDir += aTrek;
-    }
-
-    VrmlData_Scene aScene;
-    aScene.SetLinearScale(aNode->GlobalParameters.LengthUnit);
-
-    aScene.SetVrmlDir(aVrmlDir);
-    aScene << aStream;
-    const char* aStr = 0L;
-    switch (aScene.Status())
-    {
-      case VrmlData_StatusOK: {
-        aShape = aScene.GetShape(aShapeAppMap);
-        break;
-      }
-      case VrmlData_EmptyData:
-        aStr = "EmptyData";
-        break;
-      case VrmlData_UnrecoverableError:
-        aStr = "UnrecoverableError";
-        break;
-      case VrmlData_GeneralError:
-        aStr = "GeneralError";
-        break;
-      case VrmlData_EndOfFile:
-        aStr = "EndOfFile";
-        break;
-      case VrmlData_NotVrmlFile:
-        aStr = "NotVrmlFile";
-        break;
-      case VrmlData_CannotOpenFile:
-        aStr = "CannotOpenFile";
-        break;
-      case VrmlData_VrmlFormatError:
-        aStr = "VrmlFormatError";
-        break;
-      case VrmlData_NumericInputError:
-        aStr = "NumericInputError";
-        break;
-      case VrmlData_IrrelevantNumber:
-        aStr = "IrrelevantNumber";
-        break;
-      case VrmlData_BooleanInputError:
-        aStr = "BooleanInputError";
-        break;
-      case VrmlData_StringInputError:
-        aStr = "StringInputError";
-        break;
-      case VrmlData_NodeNameUnknown:
-        aStr = "NodeNameUnknown";
-        break;
-      case VrmlData_NonPositiveSize:
-        aStr = "NonPositiveSize";
-        break;
-      case VrmlData_ReadUnknownNode:
-        aStr = "ReadUnknownNode";
-        break;
-      case VrmlData_NonSupportedFeature:
-        aStr = "NonSupportedFeature";
-        break;
-      case VrmlData_OutputStreamUndefined:
-        aStr = "OutputStreamUndefined";
-        break;
-      case VrmlData_NotImplemented:
-        aStr = "NotImplemented";
-        break;
-      default:
-        break;
-    }
-    if (aStr)
-    {
-      Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
-                          << "\t: ++ VRML Error: " << aStr << " in line " << aScene.GetLineError();
-      return false;
-    }
-    else
-    {
-      theShape = aShape;
-    }
-  }
-  else
+  if (!aFic.open(thePath.ToCString(), std::ios::in))
   {
     Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
                         << "\t: cannot open file";
     return false;
   }
-  return true;
+
+  TCollection_AsciiString aVrmlDir = ExtractVrmlDirectory(thePath);
+  return ProcessVrmlScene(aStream, aNode, aVrmlDir, theShape, aContext);
 }
 
 //=================================================================================================
@@ -310,6 +382,189 @@ bool DEVRML_Provider::Write(const TCollection_AsciiString& thePath,
 
 //=================================================================================================
 
+Standard_Boolean DEVRML_Provider::Read(ReadStreamList&                 theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       Handle(XSControl_WorkSession)&  theWS,
+                                       const Message_ProgressRange&    theProgress)
+{
+  (void)theWS;
+  return Read(theStreams, theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Write(WriteStreamList&                theStreams,
+                                        const Handle(TDocStd_Document)& theDocument,
+                                        Handle(XSControl_WorkSession)&  theWS,
+                                        const Message_ProgressRange&    theProgress)
+{
+  (void)theWS;
+  return Write(theStreams, theDocument, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Read(ReadStreamList&                theStreams,
+                                       TopoDS_Shape&                  theShape,
+                                       Handle(XSControl_WorkSession)& theWS,
+                                       const Message_ProgressRange&   theProgress)
+{
+  (void)theWS;
+  return Read(theStreams, theShape, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Write(WriteStreamList&               theStreams,
+                                        const TopoDS_Shape&            theShape,
+                                        Handle(XSControl_WorkSession)& theWS,
+                                        const Message_ProgressRange&   theProgress)
+{
+  (void)theWS;
+  return Write(theStreams, theShape, theProgress);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Read(ReadStreamList&                 theStreams,
+                                       const Handle(TDocStd_Document)& theDocument,
+                                       const Message_ProgressRange&    theProgress)
+{
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString        aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
+  {
+    return Standard_False;
+  }
+
+  TopoDS_Shape aShape;
+  if (!Read(theStreams, aShape, theProgress))
+  {
+    return Standard_False;
+  }
+
+  Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
+  aShapeTool->AddShape(aShape);
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Write(WriteStreamList&                theStreams,
+                                        const Handle(TDocStd_Document)& theDocument,
+                                        const Message_ProgressRange&    theProgress)
+{
+  (void)theProgress;
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString        aFullContext = aContext + " " + aFirstKey;
+  if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
+  {
+    return Standard_False;
+  }
+
+  Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aFullContext);
+  if (aNode.IsNull())
+  {
+    return Standard_False;
+  }
+
+  Standard_Real aScaling = CalculateScalingFactor(theDocument, aNode, aContext);
+
+  // Use VrmlAPI_Writer with stream support
+  VrmlAPI_Writer aWriter;
+  aWriter.SetRepresentation(
+    static_cast<VrmlAPI_RepresentationOfShape>(aNode->InternalParameters.WriteRepresentationType));
+
+  Standard_OStream& aStream = theStreams.First().Stream;
+
+  if (!aWriter.WriteDoc(theDocument, aStream, aScaling))
+  {
+    Message::SendFail() << "Error in the DEVRML_Provider during " << aContext
+                        << ": WriteDoc operation failed";
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Read(ReadStreamList&              theStreams,
+                                       TopoDS_Shape&                theShape,
+                                       const Message_ProgressRange& theProgress)
+{
+  (void)theProgress;
+  TCollection_AsciiString aContext = "reading stream";
+  if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
+  Standard_IStream&              aStream   = theStreams.First().Stream;
+
+  TCollection_AsciiString          aFullContext = aContext + " " + aFirstKey;
+  Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aFullContext);
+  if (aNode.IsNull())
+  {
+    return Standard_False;
+  }
+
+  return ProcessVrmlScene(aStream, aNode, ".", theShape, aContext);
+}
+
+//=================================================================================================
+
+Standard_Boolean DEVRML_Provider::Write(WriteStreamList&             theStreams,
+                                        const TopoDS_Shape&          theShape,
+                                        const Message_ProgressRange& theProgress)
+{
+  (void)theProgress;
+  TCollection_AsciiString aContext = "writing stream";
+  if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
+  {
+    return Standard_False;
+  }
+
+  const TCollection_AsciiString&   aFirstKey    = theStreams.First().Path;
+  TCollection_AsciiString          aFullContext = aContext + " " + aFirstKey;
+  Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aFullContext);
+  if (aNode.IsNull())
+  {
+    return Standard_False;
+  }
+
+  // Use VrmlAPI_Writer with stream support
+  VrmlAPI_Writer aWriter;
+  aWriter.SetRepresentation(
+    static_cast<VrmlAPI_RepresentationOfShape>(aNode->InternalParameters.WriteRepresentationType));
+
+  Standard_OStream& aStream = theStreams.First().Stream;
+
+  if (!aWriter.Write(theShape, aStream, 2)) // Use version 2 by default
+  {
+    Message::SendFail() << "Error in the DEVRML_Provider during " << aContext
+                        << ": Write operation failed";
+    return Standard_False;
+  }
+
+  return Standard_True;
+}
+
+//=================================================================================================
+
 TCollection_AsciiString DEVRML_Provider::GetFormat() const
 {
   return TCollection_AsciiString("VRML");
index df250af8f1d041bfaef39856ccfab96b018edd8f..05bb057980b3b2f5775338bdc04399e1cb37ab68 100644 (file)
@@ -108,6 +108,54 @@ public:
     Handle(XSControl_WorkSession)& theWS,
     const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    Handle(XSControl_WorkSession)&  theWS,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                theStreams,
+    TopoDS_Shape&                  theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theWS current work session
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&               theStreams,
+    const TopoDS_Shape&            theShape,
+    Handle(XSControl_WorkSession)& theWS,
+    const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
   //! Reads a CAD file, according internal configuration
   //! @param[in] thePath path to the import CAD file
   //! @param[out] theShape shape to save result
@@ -128,6 +176,46 @@ public:
     const TopoDS_Shape&            theShape,
     const Message_ProgressRange&   theProgress = Message_ProgressRange()) Standard_OVERRIDE;
 
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theDocument document to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&                 theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theDocument document to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&                theStreams,
+    const Handle(TDocStd_Document)& theDocument,
+    const Message_ProgressRange&    theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Reads streams according to internal configuration
+  //! @param[in] theStreams streams to read from
+  //! @param[out] theShape shape to save result
+  //! @param[in] theProgress progress indicator
+  //! @return true if Read operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Read(
+    ReadStreamList&              theStreams,
+    TopoDS_Shape&                theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
+  //! Writes streams according to internal configuration
+  //! @param[in] theStreams streams to write to
+  //! @param[out] theShape shape to export
+  //! @param[in] theProgress progress indicator
+  //! @return true if Write operation has ended correctly
+  Standard_EXPORT virtual Standard_Boolean Write(
+    WriteStreamList&             theStreams,
+    const TopoDS_Shape&          theShape,
+    const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
+
 public:
   //! Gets CAD format name of associated provider
   //! @return provider CAD format
diff --git a/src/DataExchange/TKDEVRML/GTests/DEVRML_Provider_Test.cxx b/src/DataExchange/TKDEVRML/GTests/DEVRML_Provider_Test.cxx
new file mode 100644 (file)
index 0000000..1328e61
--- /dev/null
@@ -0,0 +1,519 @@
+// Copyright (c) 2025 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.
+
+#include <DEVRML_Provider.hxx>
+#include <DEVRML_ConfigurationNode.hxx>
+#include <DE_Wrapper.hxx>
+
+#include <BRepPrimAPI_MakeBox.hxx>
+#include <BRepPrimAPI_MakeSphere.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <BRep_Builder.hxx>
+#include <TopoDS_Shape.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopAbs_ShapeEnum.hxx>
+#include <gp_Pnt.hxx>
+#include <gp_Pln.hxx>
+#include <gp_Dir.hxx>
+#include <Poly_Triangulation.hxx>
+#include <Poly_Triangle.hxx>
+#include <TColgp_Array1OfPnt.hxx>
+#include <Poly_Array1OfTriangle.hxx>
+#include <BRep_Tool.hxx>
+#include <TopLoc_Location.hxx>
+#include <TDocStd_Document.hxx>
+#include <TDocStd_Application.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+
+#include <sstream>
+#include <gtest/gtest.h>
+
+class DEVRML_ProviderTest : public ::testing::Test
+{
+protected:
+  void SetUp() override
+  {
+    // Initialize provider with default configuration (will be modified per test)
+    Handle(DEVRML_ConfigurationNode) aNode = new DEVRML_ConfigurationNode();
+    myProvider                             = new DEVRML_Provider(aNode);
+
+    // Create test shapes
+    myBox            = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(); // For wireframe testing
+    mySphere         = BRepPrimAPI_MakeSphere(5.0).Shape();           // For wireframe testing
+    myTriangularFace = CreateTriangulatedFace();                      // For shaded/face testing
+
+    // Create test document
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    anApp->NewDocument("BinXCAF", myDocument);
+  }
+
+  void TearDown() override
+  {
+    myProvider.Nullify();
+    myDocument.Nullify();
+  }
+
+  // Helper method to count shape elements
+  Standard_Integer CountShapeElements(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType)
+  {
+    Standard_Integer aCount = 0;
+    for (TopExp_Explorer anExplorer(theShape, theType); anExplorer.More(); anExplorer.Next())
+    {
+      aCount++;
+    }
+    return aCount;
+  }
+
+  // Helper method to create a triangulated face with mesh data
+  TopoDS_Shape CreateTriangulatedFace()
+  {
+    // Create vertices for triangulation
+    TColgp_Array1OfPnt aNodes(1, 4);
+    aNodes.SetValue(1, gp_Pnt(0.0, 0.0, 0.0));   // Bottom-left
+    aNodes.SetValue(2, gp_Pnt(10.0, 0.0, 0.0));  // Bottom-right
+    aNodes.SetValue(3, gp_Pnt(10.0, 10.0, 0.0)); // Top-right
+    aNodes.SetValue(4, gp_Pnt(0.0, 10.0, 0.0));  // Top-left
+
+    // Create triangles (two triangles forming a quad)
+    Poly_Array1OfTriangle aTriangles(1, 2);
+    aTriangles.SetValue(1, Poly_Triangle(1, 2, 3)); // First triangle
+    aTriangles.SetValue(2, Poly_Triangle(1, 3, 4)); // Second triangle
+
+    // Create triangulation
+    Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(aNodes, aTriangles);
+
+    // Create a simple planar face
+    gp_Pln                  aPlane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0, 0, 1));
+    BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 0.0, 10.0, 0.0, 10.0);
+
+    if (!aFaceBuilder.IsDone())
+    {
+      return TopoDS_Shape();
+    }
+
+    TopoDS_Face aFace = aFaceBuilder.Face();
+
+    // Attach triangulation to the face (without location parameter)
+    BRep_Builder aBuilder;
+    aBuilder.UpdateFace(aFace, aTriangulation);
+
+    return aFace;
+  }
+
+protected:
+  Handle(DEVRML_Provider)  myProvider;
+  TopoDS_Shape             myBox;
+  TopoDS_Shape             mySphere;
+  TopoDS_Shape             myTriangularFace;
+  Handle(TDocStd_Document) myDocument;
+};
+
+// Test basic provider creation and format/vendor information
+TEST_F(DEVRML_ProviderTest, BasicProperties)
+{
+  EXPECT_STREQ("VRML", myProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
+  EXPECT_FALSE(myProvider->GetNode().IsNull());
+}
+
+// Test stream-based shape write and read operations with wireframe (edges)
+TEST_F(DEVRML_ProviderTest, StreamShapeWriteReadWireframe)
+{
+  // Configure provider for wireframe mode (default)
+  Handle(DEVRML_ConfigurationNode) aNode =
+    Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Wireframe;
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("wireframe.vrml", anOStream));
+
+  // Write box to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myBox));
+
+  std::string aVrmlContent = anOStream.str();
+  EXPECT_FALSE(aVrmlContent.empty());
+  EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
+
+  if (!aVrmlContent.empty())
+  {
+    // Read back from stream
+    std::istringstream          anIStream(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("wireframe.vrml", anIStream));
+
+    TopoDS_Shape aReadShape;
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
+    EXPECT_FALSE(aReadShape.IsNull());
+
+    if (!aReadShape.IsNull())
+    {
+      // Wireframe mode should produce edges, not faces
+      Standard_Integer aReadEdges = CountShapeElements(aReadShape, TopAbs_EDGE);
+      EXPECT_TRUE(aReadEdges > 0); // Should have edges from wireframe
+    }
+  }
+}
+
+// Test stream-based shape write and read operations with shaded (faces)
+TEST_F(DEVRML_ProviderTest, StreamShapeWriteReadShaded)
+{
+  // Configure provider for shaded mode
+  Handle(DEVRML_ConfigurationNode) aNode =
+    Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("shaded.vrml", anOStream));
+
+  // Write triangular face to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myTriangularFace));
+
+  std::string aVrmlContent = anOStream.str();
+  EXPECT_FALSE(aVrmlContent.empty());
+  EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
+
+  if (!aVrmlContent.empty())
+  {
+    // Read back from stream
+    std::istringstream          anIStream(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("shaded.vrml", anIStream));
+
+    TopoDS_Shape aReadShape;
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
+    EXPECT_FALSE(aReadShape.IsNull());
+
+    if (!aReadShape.IsNull())
+    {
+      // Shaded mode should produce faces
+      Standard_Integer aReadFaces = CountShapeElements(aReadShape, TopAbs_FACE);
+      EXPECT_TRUE(aReadFaces > 0); // Should have faces from shaded mode
+    }
+  }
+}
+
+// Test stream-based document write and read operations
+TEST_F(DEVRML_ProviderTest, StreamDocumentWriteRead)
+{
+  // Configure provider for shaded mode for better document compatibility
+  Handle(DEVRML_ConfigurationNode) aNode =
+    Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
+
+  // Add shape to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aShapeLabel = aShapeTool->AddShape(myTriangularFace);
+  EXPECT_FALSE(aShapeLabel.IsNull());
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("document.vrml", anOStream));
+
+  // Write document to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
+
+  std::string aVrmlContent = anOStream.str();
+  EXPECT_FALSE(aVrmlContent.empty());
+  EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
+
+  if (!aVrmlContent.empty())
+  {
+    // Create new document for reading
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    Handle(TDocStd_Document)    aNewDocument;
+    anApp->NewDocument("BinXCAF", aNewDocument);
+
+    // Read back from stream
+    std::istringstream          anIStream(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("document.vrml", anIStream));
+
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
+
+    // Validate document content
+    Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+    TDF_LabelSequence         aLabels;
+    aNewShapeTool->GetShapes(aLabels);
+    EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
+  }
+}
+
+// Test stream-based document with multiple shapes
+TEST_F(DEVRML_ProviderTest, StreamDocumentMultipleShapes)
+{
+  // Configure provider for shaded mode for better multi-shape compatibility
+  Handle(DEVRML_ConfigurationNode) aNode =
+    Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
+
+  // Add multiple shapes to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aFirstLabel = aShapeTool->AddShape(myTriangularFace);
+  EXPECT_FALSE(aFirstLabel.IsNull());
+
+  // Add a second shape - using the sphere for variety
+  TDF_Label aSecondLabel = aShapeTool->AddShape(mySphere);
+  EXPECT_FALSE(aSecondLabel.IsNull());
+
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("multi_shapes.vrml", anOStream));
+
+  // Write document to stream
+  EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
+
+  std::string aVrmlContent = anOStream.str();
+  EXPECT_FALSE(aVrmlContent.empty());
+  EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
+
+  if (!aVrmlContent.empty())
+  {
+    // Create new document for reading
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    Handle(TDocStd_Document)    aNewDocument;
+    anApp->NewDocument("BinXCAF", aNewDocument);
+
+    // Read back from stream
+    std::istringstream          anIStream(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("multi_shapes.vrml", anIStream));
+
+    EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
+
+    // Validate document content
+    Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+    TDF_LabelSequence         aLabels;
+    aNewShapeTool->GetShapes(aLabels);
+    EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
+  }
+}
+
+// Test DE_Wrapper integration for VRML operations
+TEST_F(DEVRML_ProviderTest, DE_WrapperIntegration)
+{
+  // Initialize DE_Wrapper and bind VRML provider
+  DE_Wrapper                       aWrapper;
+  Handle(DEVRML_ConfigurationNode) aNode = new DEVRML_ConfigurationNode();
+  // Configure for shaded mode to ensure faces are generated
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
+
+  // Bind the configured node to wrapper
+  EXPECT_TRUE(aWrapper.Bind(aNode));
+
+  // Test write with DE_Wrapper using triangular face
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("test.vrml", anOStream));
+
+  EXPECT_TRUE(aWrapper.Write(aWriteStreams, myTriangularFace));
+
+  std::string aVrmlContent = anOStream.str();
+  EXPECT_FALSE(aVrmlContent.empty());
+  EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
+
+  if (!aVrmlContent.empty())
+  {
+    // Test DE_Wrapper stream operations - the key functionality we wanted to verify
+    std::istringstream          anIStream(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("test.vrml", anIStream));
+
+    TopoDS_Shape aReadShape;
+    bool         aWrapperResult = aWrapper.Read(aReadStreams, aReadShape);
+
+    // Test direct provider with same content for comparison
+    std::istringstream          anIStream2(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams2;
+    aReadStreams2.Append(DE_Provider::ReadStreamNode("test.vrml", anIStream2));
+
+    Handle(DEVRML_Provider) aDirectProvider = new DEVRML_Provider(aNode);
+    TopoDS_Shape            aDirectShape;
+    bool                    aDirectResult = aDirectProvider->Read(aReadStreams2, aDirectShape);
+
+    // REQUIREMENT: DE_Wrapper must work exactly the same as direct provider
+    EXPECT_EQ(aWrapperResult, aDirectResult);
+    EXPECT_EQ(aReadShape.IsNull(), aDirectShape.IsNull());
+
+    if (aDirectResult && !aDirectShape.IsNull())
+    {
+      Standard_Integer aFaces = CountShapeElements(aDirectShape, TopAbs_FACE);
+      EXPECT_GT(aFaces, 0);
+    }
+    else if (aWrapperResult && !aReadShape.IsNull())
+    {
+      Standard_Integer aFaces = CountShapeElements(aReadShape, TopAbs_FACE);
+      EXPECT_GT(aFaces, 0);
+    }
+  }
+}
+
+// Test DE_Wrapper document operations
+TEST_F(DEVRML_ProviderTest, DE_WrapperDocumentOperations)
+{
+  // Initialize DE_Wrapper and bind VRML provider
+  DE_Wrapper                       aWrapper;
+  Handle(DEVRML_ConfigurationNode) aNode = new DEVRML_ConfigurationNode();
+  // Configure for shaded mode for better document operations
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
+
+  // Bind the node to wrapper
+  EXPECT_TRUE(aWrapper.Bind(aNode));
+
+  // Add shape to document
+  Handle(XCAFDoc_ShapeTool) aShapeTool  = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
+  TDF_Label                 aShapeLabel = aShapeTool->AddShape(myTriangularFace);
+  EXPECT_FALSE(aShapeLabel.IsNull());
+
+  // Test document write with DE_Wrapper
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("doc.vrml", anOStream));
+
+  EXPECT_TRUE(aWrapper.Write(aWriteStreams, myDocument));
+
+  std::string aVrmlContent = anOStream.str();
+  EXPECT_FALSE(aVrmlContent.empty());
+  EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
+
+  if (!aVrmlContent.empty())
+  {
+    // Test document read with DE_Wrapper
+    Handle(TDocStd_Application) anApp = new TDocStd_Application();
+    Handle(TDocStd_Document)    aNewDocument;
+    anApp->NewDocument("BinXCAF", aNewDocument);
+
+    std::istringstream          anIStream(aVrmlContent);
+    DE_Provider::ReadStreamList aReadStreams;
+    aReadStreams.Append(DE_Provider::ReadStreamNode("doc.vrml", anIStream));
+
+    bool aWrapperDocResult = aWrapper.Read(aReadStreams, aNewDocument);
+
+    // Validate document content if read succeeded
+    if (aWrapperDocResult)
+    {
+      Handle(XCAFDoc_ShapeTool) aNewShapeTool =
+        XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
+      TDF_LabelSequence aLabels;
+      aNewShapeTool->GetShapes(aLabels);
+      EXPECT_GT(aLabels.Length(), 0);
+    }
+    else
+    {
+      // If DE_Wrapper document read fails, verify direct provider works as fallback
+      Handle(TDocStd_Application) anApp2 = new TDocStd_Application();
+      Handle(TDocStd_Document)    aTestDocument;
+      anApp2->NewDocument("BinXCAF", aTestDocument);
+
+      std::istringstream          anIStream2(aVrmlContent);
+      DE_Provider::ReadStreamList aReadStreams2;
+      aReadStreams2.Append(DE_Provider::ReadStreamNode("doc.vrml", anIStream2));
+
+      Handle(DEVRML_Provider) aDirectProvider = new DEVRML_Provider(aNode);
+      bool aDirectDocResult                   = aDirectProvider->Read(aReadStreams2, aTestDocument);
+
+      if (aDirectDocResult)
+      {
+        Handle(XCAFDoc_ShapeTool) aTestShapeTool =
+          XCAFDoc_DocumentTool::ShapeTool(aTestDocument->Main());
+        TDF_LabelSequence aTestLabels;
+        aTestShapeTool->GetShapes(aTestLabels);
+        EXPECT_GT(aTestLabels.Length(), 0);
+      }
+    }
+  }
+}
+
+// Test error conditions and edge cases
+TEST_F(DEVRML_ProviderTest, ErrorHandling)
+{
+  // Test with empty streams
+  DE_Provider::WriteStreamList anEmptyWriteStreams;
+  EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myBox));
+
+  DE_Provider::ReadStreamList anEmptyReadStreams;
+  TopoDS_Shape                aShape;
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
+
+  // Test with null shape
+  std::ostringstream           anOStream;
+  DE_Provider::WriteStreamList aWriteStreams;
+  aWriteStreams.Append(DE_Provider::WriteStreamNode("null_test.vrml", anOStream));
+  TopoDS_Shape aNullShape;
+
+  // Writing null shape might succeed but produce empty or minimal content
+  myProvider->Write(aWriteStreams, aNullShape);
+  std::string aContent = anOStream.str();
+  EXPECT_FALSE(aContent.empty()); // Should at least have VRML header
+
+  // Test reading invalid VRML content
+  std::string                 anInvalidContent = "This is not valid VRML content";
+  std::istringstream          anInvalidStream(anInvalidContent);
+  DE_Provider::ReadStreamList anInvalidReadStreams;
+  anInvalidReadStreams.Append(DE_Provider::ReadStreamNode("invalid.vrml", anInvalidStream));
+
+  TopoDS_Shape anInvalidShape;
+  EXPECT_FALSE(myProvider->Read(anInvalidReadStreams, anInvalidShape));
+
+  // Test with null document
+  Handle(TDocStd_Document) aNullDoc;
+  EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullDoc));
+  EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aNullDoc));
+}
+
+// Test different VRML configuration modes
+TEST_F(DEVRML_ProviderTest, ConfigurationModes)
+{
+  Handle(DEVRML_ConfigurationNode) aNode =
+    Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
+
+  // Test wireframe mode configuration
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Wireframe;
+  EXPECT_EQ(aNode->InternalParameters.WriteRepresentationType,
+            DEVRML_ConfigurationNode::WriteMode_RepresentationType_Wireframe);
+
+  // Test shaded mode configuration
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
+  EXPECT_EQ(aNode->InternalParameters.WriteRepresentationType,
+            DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded);
+
+  // Test both mode configuration
+  aNode->InternalParameters.WriteRepresentationType =
+    DEVRML_ConfigurationNode::WriteMode_RepresentationType_Both;
+  EXPECT_EQ(aNode->InternalParameters.WriteRepresentationType,
+            DEVRML_ConfigurationNode::WriteMode_RepresentationType_Both);
+
+  // Test writer version configuration
+  aNode->InternalParameters.WriterVersion = DEVRML_ConfigurationNode::WriteMode_WriterVersion_1;
+  EXPECT_EQ(aNode->InternalParameters.WriterVersion,
+            DEVRML_ConfigurationNode::WriteMode_WriterVersion_1);
+
+  aNode->InternalParameters.WriterVersion = DEVRML_ConfigurationNode::WriteMode_WriterVersion_2;
+  EXPECT_EQ(aNode->InternalParameters.WriterVersion,
+            DEVRML_ConfigurationNode::WriteMode_WriterVersion_2);
+
+  // Test that provider format and vendor are correct
+  EXPECT_STREQ("VRML", myProvider->GetFormat().ToCString());
+  EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
+}
\ No newline at end of file
index 9a43166d3687309ef32aa461cfc294696796359b..72ec3085637608b2ccdb28a92067d79a375b392e 100644 (file)
@@ -2,4 +2,5 @@
 set(OCCT_TKDEVRML_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
 
 set(OCCT_TKDEVRML_GTests_FILES
+  DEVRML_Provider_Test.cxx
 )
index 34b0667b6657e3eef68b182395ebc48692824e55..6a471801c54cc1de4e1caac4ee25713b8b84c60b 100644 (file)
@@ -242,29 +242,66 @@ Standard_Boolean VrmlAPI_Writer::Write(const TopoDS_Shape&    aShape,
                                        const Standard_CString aFile,
                                        const Standard_Integer aVersion) const
 {
-  if (aVersion == 1)
-    return write_v1(aShape, aFile);
-  else if (aVersion == 2)
-    return write_v2(aShape, aFile);
+  const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
+  std::shared_ptr<std::ostream> anOutStream =
+    aFileSystem->OpenOStream(aFile, std::ios::out | std::ios::binary);
+  if (anOutStream.get() == NULL)
+  {
+    return Standard_False;
+  }
 
-  return Standard_False;
+  return Write(aShape, *anOutStream, aVersion);
 }
 
-Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape&    aShape,
-                                          const Standard_CString aFile) const
+Standard_Boolean VrmlAPI_Writer::WriteDoc(const Handle(TDocStd_Document)& theDoc,
+                                          const Standard_CString          theFile,
+                                          const Standard_Real             theScale) const
 {
-  OSD_Path                thePath(aFile);
-  TCollection_AsciiString theFile;
-  thePath.SystemName(theFile);
   const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
-  std::shared_ptr<std::ostream> anOutFile =
+  std::shared_ptr<std::ostream> anOutStream =
     aFileSystem->OpenOStream(theFile, std::ios::out | std::ios::binary);
-  if (anOutFile.get() == NULL)
+  if (anOutStream.get() == NULL)
   {
     return Standard_False;
   }
-  Handle(VrmlConverter_IsoAspect) ia  = new VrmlConverter_IsoAspect; // UIso
-  Handle(VrmlConverter_IsoAspect) ia1 = new VrmlConverter_IsoAspect; // VIso
+
+  return WriteDoc(theDoc, *anOutStream, theScale);
+}
+
+//=================================================================================================
+
+Standard_Boolean VrmlAPI_Writer::Write(const TopoDS_Shape&    aShape,
+                                       Standard_OStream&      theOStream,
+                                       const Standard_Integer aVersion) const
+{
+  if (aVersion == 1)
+    return write_v1(aShape, theOStream);
+  else if (aVersion == 2)
+    return write_v2(aShape, theOStream);
+
+  return Standard_False;
+}
+
+//=================================================================================================
+
+Standard_Boolean VrmlAPI_Writer::WriteDoc(const Handle(TDocStd_Document)& theDoc,
+                                          Standard_OStream&               theOStream,
+                                          const Standard_Real             theScale) const
+{
+  VrmlData_Scene        aScene;
+  VrmlData_ShapeConvert aConv(aScene, theScale);
+  aConv.ConvertDocument(theDoc);
+
+  theOStream << aScene;
+  theOStream.flush();
+  return theOStream.good();
+}
+
+Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape& aShape,
+                                          Standard_OStream&   theOStream) const
+{
+  Handle(VrmlConverter_IsoAspect) ia  = new VrmlConverter_IsoAspect;
+  Handle(VrmlConverter_IsoAspect) ia1 = new VrmlConverter_IsoAspect;
   ia->SetMaterial(myUisoMaterial);
   ia->SetHasMaterial(Standard_True);
   myDrawer->SetUIsoAspect(ia);
@@ -318,7 +355,6 @@ Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape&    aShape,
     if (!aFace.IsNull())
     {
       Handle(Poly_Triangulation) aTri = BRep_Tool::Triangulation(aFace, aLoc);
-
       if (!aTri.IsNull())
       {
         hasTriangles = Standard_True;
@@ -335,57 +371,61 @@ Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape&    aShape,
   VrmlConverter_TypeOfCamera      Camera = VrmlConverter_PerspectiveCamera;
   Handle(VrmlConverter_Projector) projector =
     new VrmlConverter_Projector(Shapes, Focus, DX, DY, DZ, XUp, YUp, ZUp, Camera, Light);
-  Vrml::VrmlHeaderWriter(*anOutFile);
+
+  Vrml::VrmlHeaderWriter(theOStream);
   if (myRepresentation == VrmlAPI_BothRepresentation)
     Vrml::CommentWriter(
       " This file contents both Shaded and Wire Frame representation of selected Shape ",
-      *anOutFile);
+      theOStream);
   if (myRepresentation == VrmlAPI_ShadedRepresentation)
     Vrml::CommentWriter(" This file contents only Shaded representation of selected Shape ",
-                        *anOutFile);
+                        theOStream);
   if (myRepresentation == VrmlAPI_WireFrameRepresentation)
     Vrml::CommentWriter(" This file contents only Wire Frame representation of selected Shape ",
-                        *anOutFile);
+                        theOStream);
+
   Vrml_Separator S1;
-  S1.Print(*anOutFile);
-  projector->Add(*anOutFile);
+  S1.Print(theOStream);
+  projector->Add(theOStream);
+
   Light  = VrmlConverter_DirectionLight;
   Camera = VrmlConverter_OrthographicCamera;
   Handle(VrmlConverter_Projector) projector1 =
     new VrmlConverter_Projector(Shapes, Focus, DX, DY, DZ, XUp, YUp, ZUp, Camera, Light);
-  projector1->Add(*anOutFile);
+  projector1->Add(theOStream);
+
   Vrml_Separator S2;
-  S2.Print(*anOutFile);
+  S2.Print(theOStream);
   if ((myRepresentation == VrmlAPI_ShadedRepresentation
        || myRepresentation == VrmlAPI_BothRepresentation)
       && hasTriangles)
   {
     Vrml_Group Group1;
-    Group1.Print(*anOutFile);
+    Group1.Print(theOStream);
     Vrml_Instancing I2("Shaded representation of shape");
-    I2.DEF(*anOutFile);
-    VrmlConverter_ShadedShape::Add(*anOutFile, aShape, myDrawer);
-    Group1.Print(*anOutFile);
+    I2.DEF(theOStream);
+    VrmlConverter_ShadedShape::Add(theOStream, aShape, myDrawer);
+    Group1.Print(theOStream);
   }
   if (myRepresentation == VrmlAPI_WireFrameRepresentation
       || myRepresentation == VrmlAPI_BothRepresentation)
   {
     Vrml_Group Group2;
-    Group2.Print(*anOutFile);
+    Group2.Print(theOStream);
     Vrml_Instancing I3("Wire Frame representation of shape");
-    I3.DEF(*anOutFile);
-    VrmlConverter_WFDeflectionShape::Add(*anOutFile, aShape, myDrawer);
-    Group2.Print(*anOutFile);
+    I3.DEF(theOStream);
+    VrmlConverter_WFDeflectionShape::Add(theOStream, aShape, myDrawer);
+    Group2.Print(theOStream);
   }
-  S2.Print(*anOutFile);
-  S1.Print(*anOutFile);
+  S2.Print(theOStream);
+  S1.Print(theOStream);
 
-  anOutFile->flush();
-  return anOutFile->good();
+  theOStream.flush();
+  return theOStream.good();
 }
 
-Standard_Boolean VrmlAPI_Writer::write_v2(const TopoDS_Shape&    aShape,
-                                          const Standard_CString aFile) const
+Standard_Boolean VrmlAPI_Writer::write_v2(const TopoDS_Shape& aShape,
+                                          Standard_OStream&   theOStream) const
 {
   Standard_Boolean anExtFace = Standard_False;
   if (myRepresentation == VrmlAPI_ShadedRepresentation
@@ -402,38 +442,7 @@ Standard_Boolean VrmlAPI_Writer::write_v2(const TopoDS_Shape&    aShape,
   aConv.AddShape(aShape);
   aConv.Convert(anExtFace, anExtEdge);
 
-  const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
-  std::shared_ptr<std::ostream> anOutStream =
-    aFileSystem->OpenOStream(aFile, std::ios::out | std::ios::binary);
-  if (anOutStream.get() != NULL)
-  {
-    *anOutStream << aScene;
-    anOutStream->flush();
-    return anOutStream->good();
-  }
-  anOutStream.reset();
-  return Standard_False;
-}
-
-//=================================================================================================
-
-Standard_Boolean VrmlAPI_Writer::WriteDoc(const Handle(TDocStd_Document)& theDoc,
-                                          const Standard_CString          theFile,
-                                          const Standard_Real             theScale) const
-{
-  VrmlData_Scene        aScene;
-  VrmlData_ShapeConvert aConv(aScene, theScale);
-  aConv.ConvertDocument(theDoc);
-
-  const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
-  std::shared_ptr<std::ostream> anOutStream =
-    aFileSystem->OpenOStream(theFile, std::ios::out | std::ios::binary);
-  if (anOutStream.get() != NULL)
-  {
-    *anOutStream << aScene;
-    anOutStream->flush();
-    return anOutStream->good();
-  }
-  anOutStream.reset();
-  return Standard_False;
+  theOStream << aScene;
+  theOStream.flush();
+  return theOStream.good();
 }
index 0a5e23bf30e7e5233844e877fe1d8a31ef39ab0b..6be4349ede49c0ad7966be8762f4f7e3f8838448 100644 (file)
@@ -116,16 +116,28 @@ public:
                                             const Standard_CString          theFile,
                                             const Standard_Real             theScale) const;
 
+  //! Converts the shape aShape to
+  //! VRML format of the passed version and writes it to the given stream.
+  Standard_EXPORT Standard_Boolean Write(const TopoDS_Shape&    aShape,
+                                         Standard_OStream&      theOStream,
+                                         const Standard_Integer aVersion = 2) const;
+
+  //! Converts the document to VRML format of the passed version
+  //! and writes it to the given stream.
+  Standard_EXPORT Standard_Boolean WriteDoc(const Handle(TDocStd_Document)& theDoc,
+                                            Standard_OStream&               theOStream,
+                                            const Standard_Real             theScale) const;
+
 protected:
   //! Converts the shape aShape to VRML format of version 1.0 and writes it
-  //! to the file identified by aFileName using default parameters.
-  Standard_EXPORT Standard_Boolean write_v1(const TopoDS_Shape&    aShape,
-                                            const Standard_CString aFileName) const;
+  //! to the given stream using default parameters.
+  Standard_EXPORT Standard_Boolean write_v1(const TopoDS_Shape& aShape,
+                                            Standard_OStream&   theOStream) const;
 
   //! Converts the shape aShape to VRML format of version 2.0 and writes it
-  //! to the file identified by aFileName using default parameters.
-  Standard_EXPORT Standard_Boolean write_v2(const TopoDS_Shape&    aShape,
-                                            const Standard_CString aFileName) const;
+  //! to the given stream using default parameters.
+  Standard_EXPORT Standard_Boolean write_v2(const TopoDS_Shape& aShape,
+                                            Standard_OStream&   theOStream) const;
 
 private:
   VrmlAPI_RepresentationOfShape   myRepresentation;