0022961: Dangerous usage of 'buf' (strncpy doesn't always 0-terminate it) (cppcheck...
[occt.git] / src / VrmlData / VrmlData_Scene.cxx
1 // File:      VrmlData_Scene.cxx
2 // Created:   25.05.06 16:33:25
3 // Author:    Alexander GRIGORIEV
4 // Copyright: Open Cascade 2006
5
6 #include <VrmlData_Scene.hxx>
7 #include <VrmlData_InBuffer.hxx>
8 #include <VrmlData_Appearance.hxx>
9 #include <VrmlData_Box.hxx>
10 #include <VrmlData_Color.hxx>
11 #include <VrmlData_Cone.hxx>
12 #include <VrmlData_Coordinate.hxx>
13 #include <VrmlData_Cylinder.hxx>
14 #include <VrmlData_DataMapOfShapeAppearance.hxx>
15 #include <VrmlData_Group.hxx>
16 #include <VrmlData_ImageTexture.hxx>
17 #include <VrmlData_InBuffer.hxx>
18 #include <VrmlData_IndexedFaceSet.hxx>
19 #include <VrmlData_IndexedLineSet.hxx>
20 #include <VrmlData_Material.hxx>
21 #include <VrmlData_Normal.hxx>
22 #include <VrmlData_Scene.hxx>
23 #include <VrmlData_ShapeNode.hxx>
24 #include <VrmlData_Sphere.hxx>
25 #include <VrmlData_TextureCoordinate.hxx>
26 #include <VrmlData_UnknownNode.hxx>
27 //#include <VrmlData_WorldInfo.hxx>
28 #include <NCollection_Vector.hxx>
29 #include <TopoDS_TFace.hxx>
30 #include <TopoDS.hxx>
31 #include <TopoDS_Face.hxx>
32 #include <TopExp_Explorer.hxx>
33 #include <BRep_Builder.hxx>
34 #include <Precision.hxx>
35
36 #ifdef WNT
37 #define _CRT_SECURE_NO_DEPRECATE
38 #pragma warning (disable:4996)
39 #endif
40
41 static void     dumpNode        (Standard_OStream&              theStream,
42                                  const Handle(VrmlData_Node)&   theNode,
43                                  const TCollection_AsciiString& theIndent);
44
45 static void     dumpNodeHeader  (Standard_OStream&              theStream,
46                                  const TCollection_AsciiString& theIndent,
47                                  const char *                   theType,
48                                  const char *                   theName);
49
50 //=======================================================================
51 //function : VrmlData_Scene
52 //purpose  : Constructor
53 //=======================================================================
54
55 VrmlData_Scene::VrmlData_Scene
56         (const Handle(NCollection_IncAllocator)& theAlloc)
57   : myLinearScale     (1.),
58     myStatus          (VrmlData_StatusOK),
59     myAllocator       (theAlloc.IsNull() ?
60                        new NCollection_IncAllocator : theAlloc.operator->()),
61     myLineError       (0),
62     myOutput          (0L),
63     myIndent          (2),
64     myCurrentIndent   (0),
65     myAutoNameCounter (0)
66 {
67   myWorldInfo = new VrmlData_WorldInfo (* this);
68   myWorldInfo->AddInfo ("Created by OPEN CASCADE (tm) VrmlData API");
69   myLstNodes.Append (myWorldInfo);
70   myAllNodes.Append (myWorldInfo);
71 }
72
73 //=======================================================================
74 //function : AddNode
75 //purpose  : 
76 //=======================================================================
77
78 const Handle(VrmlData_Node)& VrmlData_Scene::AddNode
79                                 (const Handle(VrmlData_Node)& theN,
80                                  const Standard_Boolean       isTopLevel)
81 {
82   if (theN.IsNull() == Standard_False)
83     if (theN->IsKind (STANDARD_TYPE(VrmlData_WorldInfo)) == Standard_False) {
84       myMutex.Lock();
85       const Handle(VrmlData_Node)& aNode =
86         myAllNodes.Append ((&theN->Scene()== this) ? theN : theN->Clone (NULL));
87       // Name is checked for uniqueness. If not, letter 'D' is appended until
88       // the name proves to be unique.
89       if (aNode->Name()[0] != '\0')
90         while (myNamedNodes.Add (aNode) == Standard_False)
91           aNode->setName (aNode->Name(), "D");
92       if (isTopLevel)
93         myLstNodes.Append (aNode);
94       myMutex.Unlock();
95       return aNode;
96     }
97   static Handle(VrmlData_Node) aNullNode;
98   aNullNode.Nullify();
99   return aNullNode;
100 }
101
102 //=======================================================================
103 //function : operator <<
104 //purpose  : Export to text stream (file or else)
105 //=======================================================================
106
107 Standard_OStream& operator << (Standard_OStream&     theOutput,
108                                const VrmlData_Scene& theScene)
109 {
110   VrmlData_Scene& aScene = const_cast <VrmlData_Scene&> (theScene);
111   aScene.myMutex.Lock();
112   aScene.myCurrentIndent = 0;
113   aScene.myLineError = 0;
114   aScene.myOutput = 0L;
115   aScene.myNamedNodesOut.Clear();
116   aScene.myUnnamedNodesOut.Clear();
117   aScene.myAutoNameCounter = 0;
118
119   // Dummy write
120
121   VrmlData_Scene::Iterator anIterD(aScene.myLstNodes);
122   for (; anIterD.More(); anIterD.Next()) {
123     const Handle(VrmlData_Node)& aNode = anIterD.Value();
124     if (aNode.IsNull() == Standard_False) {
125       const VrmlData_ErrorStatus aStatus = aScene.WriteNode (0L, aNode);
126       if (aStatus != VrmlData_StatusOK &&
127           aStatus != VrmlData_NotImplemented)
128         break;
129     }
130   }
131
132   aScene.myOutput = &theOutput;
133   aScene.myNamedNodesOut.Clear();
134   theOutput << "#VRML V2.0 utf8" << endl << endl;
135
136   // Real write
137
138   VrmlData_Scene::Iterator anIter(aScene.myLstNodes);
139   for (; anIter.More(); anIter.Next()) {
140     const Handle(VrmlData_Node)& aNode = anIter.Value();
141     if (aNode.IsNull() == Standard_False) {
142       const VrmlData_ErrorStatus aStatus = aScene.WriteNode (0L, aNode);
143       if (aStatus != VrmlData_StatusOK &&
144           aStatus != VrmlData_NotImplemented)
145         break;
146     }
147   }
148   aScene.myOutput = 0L;
149   aScene.myNamedNodesOut.Clear();
150   aScene.myUnnamedNodesOut.Clear();
151   aScene.myMutex.Unlock();
152   return theOutput;
153 }
154
155 //=======================================================================
156 //function : SetVrmlDir
157 //purpose  : 
158 //=======================================================================
159
160 void VrmlData_Scene::SetVrmlDir (const TCollection_ExtendedString& theDir)
161 {
162   TCollection_ExtendedString& aDir = myVrmlDir.Append (theDir);
163   const Standard_ExtCharacter aTerminator = aDir.Value(aDir.Length());
164   if (aTerminator != Standard_ExtCharacter('\\') &&
165       aTerminator != Standard_ExtCharacter('/'))
166 #ifdef WNT
167     aDir += TCollection_ExtendedString ("\\");
168 #else
169     aDir += TCollection_ExtendedString ("/");
170 #endif
171 }
172
173 //=======================================================================
174 //function : WorldInfo
175 //purpose  : 
176 //=======================================================================
177
178 const Handle_VrmlData_WorldInfo& VrmlData_Scene::WorldInfo() const
179 {
180   return myWorldInfo;
181 }
182
183 //=======================================================================
184 //function : readLine
185 //purpose  : 
186 //=======================================================================
187
188 VrmlData_ErrorStatus VrmlData_Scene::readLine (VrmlData_InBuffer& theBuffer)
189 {
190   VrmlData_ErrorStatus aStatus = VrmlData_StatusOK;
191   if (theBuffer.Input.eof())
192     aStatus = VrmlData_EndOfFile;
193   else {
194     theBuffer.Input.getline (theBuffer.Line, sizeof(theBuffer.Line));
195     theBuffer.LineCount++;
196     const int stat = theBuffer.Input.rdstate();
197     if (stat & ios::badbit)
198       aStatus = VrmlData_UnrecoverableError;
199     else if (stat & ios::failbit)
200       if (stat & ios::eofbit)
201         aStatus = VrmlData_EndOfFile;
202       else
203         aStatus = VrmlData_GeneralError;
204     theBuffer.LinePtr = &theBuffer.Line[0];
205     theBuffer.IsProcessed = Standard_False;
206   }
207   return aStatus;
208 }
209
210 //=======================================================================
211 //function : ReadLine
212 //purpose  : 
213 //=======================================================================
214
215 VrmlData_ErrorStatus VrmlData_Scene::ReadLine (VrmlData_InBuffer& theBuffer)
216 {
217   VrmlData_ErrorStatus aStatus (VrmlData_StatusOK); 
218
219   while (aStatus == VrmlData_StatusOK) {
220     // Find the first significant character of the line
221     for (; * theBuffer.LinePtr != '\0'; theBuffer.LinePtr++) {
222       if (* theBuffer.LinePtr != ' ' && * theBuffer.LinePtr != '\t'
223           && * theBuffer.LinePtr != ',')
224       {
225         if (* theBuffer.LinePtr == '\n' || * theBuffer.LinePtr == '\r' ||
226             * theBuffer.LinePtr == '#')
227           // go requesting the next line
228           break;
229         goto nonempty_line;
230       }
231     }
232     // the line is empty here (no significant characters). Read the next one.
233     aStatus = readLine (theBuffer);
234   }
235
236   // error or EOF detected
237   return aStatus;
238
239  nonempty_line:
240   // Try to detect comment
241   if (theBuffer.IsProcessed == Standard_False) {
242     Standard_Boolean isQuoted (Standard_False);
243     Standard_Integer anOffset (0);
244     char * ptr = theBuffer.LinePtr;
245     for (; * ptr != '\0'; ptr++) {
246       if (anOffset)
247         * ptr = ptr[anOffset];
248       if (* ptr == '\n' || * ptr == '\r' || * ptr == '#') {
249         if (isQuoted == Standard_False) {
250           * ptr = '\0';
251           break;
252         }
253       } else if (* ptr == '\\' && isQuoted)
254         ptr[0] = ptr[++anOffset];
255       else if (* ptr == '\"')
256         isQuoted = !isQuoted;
257     }
258     theBuffer.IsProcessed = Standard_True;
259   }
260   return aStatus;
261 }
262
263 //=======================================================================
264 //function : readHeader
265 //purpose  : 
266 //=======================================================================
267
268 VrmlData_ErrorStatus VrmlData_Scene::readHeader (VrmlData_InBuffer& theBuffer)
269 {
270   VrmlData_ErrorStatus aStat = readLine (theBuffer);
271   if (aStat == VrmlData_StatusOK &&
272       !VRMLDATA_LCOMPARE(theBuffer.LinePtr, "#VRML V2.0"))
273     aStat = VrmlData_NotVrmlFile;
274   else 
275     aStat = readLine(theBuffer);
276   return aStat;
277 }
278
279 //=======================================================================
280 //function : operator <<
281 //purpose  : Import from text stream (file or else)
282 //=======================================================================
283
284 VrmlData_Scene& VrmlData_Scene::operator << (Standard_IStream& theInput)
285 {
286   VrmlData_InBuffer aBuffer (theInput);
287   myMutex.Lock();
288   // Read the VRML header
289   myStatus = readHeader (aBuffer);
290   const Handle(VrmlData_UnknownNode) aNullNode= new VrmlData_UnknownNode(*this);
291 //   if (myStatus == StatusOK)
292 //     myStatus = ReadLine (aBuffer);
293   // Read VRML data by nodes
294   while (~0) {
295     if (!VrmlData_Node::OK(myStatus, ReadLine(aBuffer))) {
296       if (myStatus == VrmlData_EndOfFile)
297         myStatus = VrmlData_StatusOK;
298       break;
299     }
300     // this line provides the method ReadNode in the present context
301     Handle(VrmlData_Node) aNode;
302     myStatus = aNullNode->ReadNode (aBuffer, aNode);
303     // Unknown nodes are not stored however they do not generate error
304     if (myStatus != VrmlData_StatusOK)
305       break;
306     if (aNode.IsNull() == Standard_False /*&&
307         !aNode->IsKind (STANDARD_TYPE(VrmlData_UnknownNode))*/)
308     {
309       if (aNode->IsKind (STANDARD_TYPE(VrmlData_WorldInfo)) == Standard_False)
310         myLstNodes.Append (aNode);
311       else if (aNode->IsDefault() == Standard_False) {
312         const Handle(VrmlData_WorldInfo) aInfo =
313           Handle(VrmlData_WorldInfo)::DownCast (aNode);
314         myWorldInfo->SetTitle (aInfo->Title());
315         NCollection_List <const char *>::Iterator anIterInfo =
316           aInfo->InfoIterator();
317         for (; anIterInfo.More(); anIterInfo.Next())
318           myWorldInfo->AddInfo (anIterInfo.Value());
319       }
320     }
321   }
322   if (myStatus != VrmlData_StatusOK)
323     myLineError = aBuffer.LineCount;
324   myMutex.Unlock();
325   return * this;
326 }
327
328 //=======================================================================
329 //function : FindNode
330 //purpose  : 
331 //=======================================================================
332
333 Handle(VrmlData_Node) VrmlData_Scene::FindNode
334                                 (const char                   * theName,
335                                  const Handle(Standard_Type)& theType) const
336 {
337   Handle(VrmlData_Node) aResult;
338 #ifdef USE_LIST_API
339   Iterator anIter (myAllNodes);
340   for (; anIter.More(); anIter.Next())
341     if (!strcmp (anIter.Value()->Name(), theName)) {
342       aResult = anIter.Value();
343       if (theType.IsNull())
344         break;
345       if (aResult->IsKind(theType))
346         break;
347       aResult.Nullify();
348     }
349 #else
350   const Handle(VrmlData_UnknownNode) aDummyNode = new VrmlData_UnknownNode;
351   aDummyNode->myName = theName;
352   if (myNamedNodes.Contains (aDummyNode))
353     aResult = const_cast<VrmlData_MapOfNode&>(myNamedNodes).Added(aDummyNode);
354 #endif
355   return aResult;
356 }
357
358 //=======================================================================
359 //function : FindNode
360 //purpose  : 
361 //=======================================================================
362
363 Handle(VrmlData_Node) VrmlData_Scene::FindNode
364                                         (const char *   theName,
365                                          gp_Trsf&       theLocation) const
366 {
367   gp_Trsf aLoc;
368   Handle(VrmlData_Node) aResult;
369   Iterator anIter (myLstNodes);
370   for (; anIter.More(); anIter.Next()) {
371     const Handle(VrmlData_Node)& aNode = anIter.Value();
372     if (aNode.IsNull())
373       continue;
374     // Match a top-level node name
375     if (strcmp(aNode->Name(), theName) == 0) {
376       aResult = aNode;
377       theLocation = aLoc;
378       break;
379     }
380     // Try a Group type of node
381     if (aNode->IsKind(STANDARD_TYPE(VrmlData_Group)))
382     {
383       const Handle(VrmlData_Group) aGroup =
384         Handle(VrmlData_Group)::DownCast (aNode);
385       if (aGroup.IsNull() == Standard_False) {
386         aResult = aGroup->FindNode(theName, theLocation);
387         if (aResult.IsNull() == Standard_False)
388           break;
389       }
390     }
391   }
392   return aResult;
393 }
394
395 //=======================================================================
396 //function : ReadWord
397 //purpose  : 
398 //=======================================================================
399
400 VrmlData_ErrorStatus VrmlData_Scene::ReadWord
401                                       (VrmlData_InBuffer&           theBuffer,
402                                        TCollection_AsciiString&     theWord)
403 {
404   VrmlData_ErrorStatus aStatus = ReadLine(theBuffer);
405   if (aStatus == VrmlData_StatusOK) {
406     char * ptr = theBuffer.LinePtr;
407     while (* ptr != '\0' && * ptr != '\n' && * ptr != '\r' &&
408            * ptr != ' '  && * ptr != '\t' && * ptr != '{' && * ptr != '}' &&
409            * ptr != ','  && * ptr != '['  && * ptr != ']')
410       ptr++;
411     const Standard_Integer aLen = Standard_Integer(ptr - theBuffer.LinePtr);
412     if (aLen <= 0)
413       aStatus = VrmlData_StringInputError;
414     else {
415       theWord = TCollection_AsciiString ((Standard_CString)theBuffer.LinePtr,
416                                          aLen);
417       theBuffer.LinePtr = ptr;
418     }
419   }
420   return aStatus;
421 }
422
423 //=======================================================================
424 //function : createNode
425 //purpose  : 
426 //=======================================================================
427
428 VrmlData_ErrorStatus VrmlData_Scene::createNode
429                                       (VrmlData_InBuffer&           theBuffer,
430                                        Handle(VrmlData_Node)&       theNode,
431                                        const Handle(Standard_Type)& theType)
432 {
433   VrmlData_ErrorStatus    aStatus;
434   Handle(VrmlData_Node)   aNode;
435   TCollection_AsciiString aName;
436
437   // Read the DEF token to assign the node name
438   if (VrmlData_Node::OK(aStatus, ReadLine(theBuffer)))
439     if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "DEF")) {
440       if (VrmlData_Node::OK(aStatus, ReadWord (theBuffer, aName)))
441         aStatus = ReadLine(theBuffer);
442     } else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "NULL")) {
443       theNode.Nullify();
444       return aStatus;
445     }
446
447   const char * strName = aName.ToCString();
448   if (aStatus == VrmlData_StatusOK) {
449     // create the new node
450     if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Appearance"))
451       aNode = new VrmlData_Appearance     (* this, strName);
452     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Shape"))
453       aNode = new VrmlData_ShapeNode      (* this, strName);
454     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Box"))
455       aNode = new VrmlData_Box            (* this, strName);
456     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Color"))
457       aNode = new VrmlData_Color          (* this, strName);
458     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Cone"))
459       aNode = new VrmlData_Cone           (* this, strName);
460     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Coordinate")) {
461       aNode = new VrmlData_Coordinate     (* this, strName);
462       
463       // Check for "Coordinate3"
464       if (VRMLDATA_LCOMPARE (theBuffer.LinePtr, "3"))
465         theBuffer.LinePtr++;
466     }
467     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Cylinder"))
468       aNode = new VrmlData_Cylinder       (* this, strName);
469     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Group"))
470       aNode = new VrmlData_Group          (* this, strName,
471                                            Standard_False);
472     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Transform"))
473       aNode = new VrmlData_Group          (* this, strName,
474                                            Standard_True);
475     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Inline"))
476       aNode = new VrmlData_Group          (* this, strName,
477                                            Standard_False);
478     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Separator"))
479       aNode = new VrmlData_Group          (* this, strName,
480                                            Standard_False);
481     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Switch"))
482       aNode = new VrmlData_Group          (* this, strName,
483                                            Standard_False);
484     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "ImageTexture"))
485       aNode = new VrmlData_ImageTexture   (* this, strName);
486     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "IndexedFaceSet"))
487       aNode = new VrmlData_IndexedFaceSet (* this, strName);
488     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "IndexedLineSet"))
489       aNode = new VrmlData_IndexedLineSet (* this, strName);
490     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Material"))
491       aNode = new VrmlData_Material       (* this, strName);
492     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Normal"))
493       aNode = new VrmlData_Normal         (* this, strName);
494     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Sphere"))
495       aNode = new VrmlData_Sphere         (* this, strName);
496     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "TextureCoordinate"))
497       aNode = new VrmlData_TextureCoordinate(* this, strName);
498     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "WorldInfo"))
499       aNode = new VrmlData_WorldInfo      (* this, strName);
500     else {
501       void * isProto = VRMLDATA_LCOMPARE(theBuffer.LinePtr, "PROTO");
502       TCollection_AsciiString aTitle;
503       aStatus = ReadWord (theBuffer, aTitle);
504       if (isProto) {
505         aStatus = ReadLine(theBuffer);
506         if (aStatus == VrmlData_StatusOK)
507           if (theBuffer.LinePtr[0] != '[')
508             aStatus = VrmlData_VrmlFormatError;
509           else {
510             theBuffer.LinePtr++;
511             Standard_Integer aLevelCounter(0);
512    // This loop searches for any opening bracket '['.
513    // Such bracket increments the level counter. A closing bracket decrements
514    // the counter. The loop terminates when the counter becomes negative.
515             while (aLevelCounter >= 0 &&
516                    (aStatus = ReadLine(theBuffer)) == VrmlData_StatusOK) {
517               int aChar;
518               while ((aChar = theBuffer.LinePtr[0]) != '\0') {
519                 theBuffer.LinePtr++;
520                 if        (aChar == '[') {
521                   aLevelCounter++;
522                   break;
523                 } else if (aChar == ']') {
524                   aLevelCounter--;
525                   break;
526                 }
527               }
528             }
529           }
530       }
531       if (aStatus == VrmlData_StatusOK)
532         aNode = new VrmlData_UnknownNode(* this,
533                                          strName,
534                                          aTitle.ToCString());
535     }
536   }
537   aStatus = ReadLine(theBuffer);
538   if (aNode.IsNull() == Standard_False) {
539     if (aNode->Name()[0] != '\0')
540       myNamedNodes.Add (aNode);
541     if (theType.IsNull() == Standard_False)
542       if (aNode->IsKind(theType) == Standard_False)
543         aStatus = VrmlData_VrmlFormatError;
544   }
545   if (aStatus == VrmlData_StatusOK)
546     if (theBuffer.LinePtr[0] == '{') {
547       theBuffer.LinePtr++;
548       theNode = aNode;
549       myAllNodes.Append(aNode);
550     } else
551       aStatus = VrmlData_VrmlFormatError;
552   return aStatus;
553 }
554
555 //=======================================================================
556 //function : operator TopoDS_Shape
557 //purpose  : 
558 //=======================================================================
559
560 VrmlData_Scene::operator TopoDS_Shape () const
561 {
562   TopoDS_Shape aShape;
563   VrmlData_Scene::createShape (aShape, myLstNodes, 0L);
564   return aShape;
565 }
566
567 //=======================================================================
568 //function : GetShape
569 //purpose  : 
570 //=======================================================================
571
572 TopoDS_Shape VrmlData_Scene::GetShape (VrmlData_DataMapOfShapeAppearance& aMap)
573 {
574   TopoDS_Shape aShape;
575   VrmlData_Scene::createShape (aShape, myLstNodes, &aMap);
576   return aShape;
577 }
578
579 //=======================================================================
580 //function : createShape
581 //purpose  : 
582 //=======================================================================
583
584 void VrmlData_Scene::createShape
585                 (TopoDS_Shape&                      outShape,
586                  const VrmlData_ListOfNode&         lstNodes,
587                  VrmlData_DataMapOfShapeAppearance* pMapShapeApp)
588 {
589   TopoDS_Shape aSingleShape;  // used when there is a single ShapeNode
590   Standard_Boolean isSingleShape (Standard_True);
591   BRep_Builder aBuilder;
592   outShape.Nullify();
593   aBuilder.MakeCompound(TopoDS::Compound(outShape));
594   aSingleShape.Orientation(TopAbs_FORWARD);
595
596   Iterator anIter (lstNodes);
597   for (; anIter.More(); anIter.Next()) {
598     // Try a Shape type of node
599     const Handle(VrmlData_ShapeNode) aNodeShape =
600       Handle(VrmlData_ShapeNode)::DownCast (anIter.Value());
601     if (aNodeShape.IsNull() == Standard_False) {
602       const Handle(VrmlData_Geometry) aNodeGeom =
603         Handle(VrmlData_Geometry)::DownCast(aNodeShape->Geometry());
604       if (aNodeGeom.IsNull() == Standard_False) {
605         if (aSingleShape.IsNull() == Standard_False)
606           isSingleShape = Standard_False;
607         const Handle(TopoDS_TShape) aTShape = aNodeGeom->TShape();
608         aSingleShape.TShape(aTShape);
609         if (aSingleShape.IsNull() == Standard_False) {
610           aBuilder.Add (outShape, aSingleShape);
611           if (pMapShapeApp != 0L) {
612             const Handle(VrmlData_Appearance)& anAppearance =
613               aNodeShape->Appearance();
614             if (anAppearance.IsNull() == Standard_False) {
615               // Check if the current topology is a single face
616               if (aTShape->IsKind(STANDARD_TYPE(TopoDS_TFace)))
617                 pMapShapeApp->Bind(aTShape, anAppearance);
618               else {
619                 // This is not a face, explode it in faces and bind each face
620                 TopoDS_Shape aCurShape;
621                 aCurShape.TShape(aTShape);
622                 TopExp_Explorer anExp(aCurShape, TopAbs_FACE);
623                 for (; anExp.More(); anExp.Next()) {
624                   const TopoDS_Face& aFace = TopoDS::Face(anExp.Current());
625                   pMapShapeApp->Bind(aFace.TShape(), anAppearance);
626                 }
627               }
628             }
629           }
630         }
631       }
632       continue;
633     }
634     // Try a Group type of node
635     const Handle(VrmlData_Group) aNodeGroup =
636       Handle(VrmlData_Group)::DownCast (anIter.Value());
637     if (aNodeGroup.IsNull() == Standard_False) {
638       TopoDS_Shape aShape;
639       aNodeGroup->Shape(aShape, pMapShapeApp);
640       if (aShape.IsNull() == Standard_False) {
641         aBuilder.Add (outShape, aShape);
642         isSingleShape = Standard_False;
643       }
644     }
645   }
646   if (isSingleShape)
647     outShape = aSingleShape;
648 }
649
650 //=======================================================================
651 //function : ReadReal
652 //purpose  : 
653 //=======================================================================
654
655 VrmlData_ErrorStatus VrmlData_Scene::ReadReal
656                                 (VrmlData_InBuffer& theBuffer,
657                                  Standard_Real&     theResult,
658                                  Standard_Boolean   isScale,
659                                  Standard_Boolean   isOnlyPositive) const
660 {
661   Standard_Real aResult(0.);
662   VrmlData_ErrorStatus aStatus;
663   if (VrmlData_Node::OK(aStatus, VrmlData_Scene::ReadLine(theBuffer))) {
664     char * endptr;
665     aResult = strtod (theBuffer.LinePtr, &endptr);
666     if (endptr == theBuffer.LinePtr)
667       aStatus = VrmlData_NumericInputError;
668     else if (isOnlyPositive && aResult < 0.001*Precision::Confusion())
669       aStatus = VrmlData_IrrelevantNumber;
670     else {
671       theResult = isScale ? (aResult * myLinearScale) : aResult;
672       theBuffer.LinePtr = endptr;
673     }
674   }
675   return aStatus;
676 }
677
678 //=======================================================================
679 //function : ReadXYZ
680 //purpose  : 
681 //=======================================================================
682
683 VrmlData_ErrorStatus VrmlData_Scene::ReadXYZ
684                                 (VrmlData_InBuffer&     theBuffer,
685                                  gp_XYZ&                theXYZ,
686                                  Standard_Boolean       isScale,
687                                  Standard_Boolean       isOnlyPos) const
688 {
689   Standard_Real aVal[3] = {0., 0., 0.};
690   VrmlData_ErrorStatus aStatus;
691   for (Standard_Integer i = 0; i < 3; i++) {
692     if (!VrmlData_Node::OK(aStatus, VrmlData_Scene::ReadLine(theBuffer)))
693       break;
694     char * endptr;
695     aVal[i] = strtod (theBuffer.LinePtr, &endptr);
696     if (endptr == theBuffer.LinePtr) {
697       aStatus = VrmlData_NumericInputError;
698       break;
699     } else {
700       if (isOnlyPos && aVal[i] < 0.001*Precision::Confusion()) {
701         aStatus = VrmlData_IrrelevantNumber;
702         break;
703       }
704       theBuffer.LinePtr = endptr;
705     }
706   }
707   if (aStatus == VrmlData_StatusOK)
708     if (isScale)
709       theXYZ.SetCoord (aVal[0] * myLinearScale,
710                        aVal[1] * myLinearScale,
711                        aVal[2] * myLinearScale);
712     else
713       theXYZ.SetCoord (aVal[0], aVal[1], aVal[2]);
714   return aStatus;
715 }
716
717 //=======================================================================
718 //function : ReadXY
719 //purpose  : 
720 //=======================================================================
721
722 VrmlData_ErrorStatus VrmlData_Scene::ReadXY
723                                 (VrmlData_InBuffer&     theBuffer,
724                                  gp_XY&                 theXY,
725                                  Standard_Boolean       isScale,
726                                  Standard_Boolean       isOnlyPos) const
727 {
728   Standard_Real aVal[2] = {0., 0.};
729   VrmlData_ErrorStatus aStatus;
730   for (Standard_Integer i = 0; i < 2; i++) {
731     if (!VrmlData_Node::OK(aStatus, VrmlData_Scene::ReadLine(theBuffer)))
732       break;
733     char * endptr;
734     aVal[i] = strtod (theBuffer.LinePtr, &endptr);
735     if (endptr == theBuffer.LinePtr) {
736       aStatus = VrmlData_NumericInputError;
737       break;
738     } else {
739       if (isOnlyPos && aVal[i] < 0.001*Precision::Confusion()) {
740         aStatus = VrmlData_IrrelevantNumber;
741         break;
742       }
743       theBuffer.LinePtr = endptr;
744     }
745   }
746   if (aStatus == VrmlData_StatusOK)
747     if (isScale)
748       theXY.SetCoord (aVal[0] * myLinearScale, aVal[1] * myLinearScale);
749     else
750       theXY.SetCoord (aVal[0], aVal[1]);
751   return aStatus;
752 }
753
754 //=======================================================================
755 //function : ReadArrIndex
756 //purpose  : Read the body of the data node (comma-separated list of int
757 //           multiplets)
758 //=======================================================================
759
760 VrmlData_ErrorStatus VrmlData_Scene::ReadArrIndex
761                                   (VrmlData_InBuffer&         theBuffer,
762                                    const Standard_Integer **& theArray,
763                                    Standard_Size&             theNBlocks) const
764 {
765   VrmlData_ErrorStatus aStatus;
766   theNBlocks = 0;
767   if (VrmlData_Node::OK(aStatus, ReadLine(theBuffer)))
768     if (theBuffer.LinePtr[0] != '[')  // opening bracket
769       aStatus = VrmlData_VrmlFormatError;
770     else {
771       theBuffer.LinePtr++;
772       NCollection_Vector<const Standard_Integer *> vecIndice;
773       NCollection_Vector<Standard_Integer>         vecInt;
774       Standard_Boolean isMore (Standard_True);
775       long             anIntValue;
776
777       // Loop reading integers from the stream
778       while (isMore && VrmlData_Node::OK(aStatus, ReadLine(theBuffer)))
779       {
780         // closing bracket, in case that it follows a comma
781         if (theBuffer.LinePtr[0] == ']') {
782           theBuffer.LinePtr++;
783           break;
784         }
785         if (!VrmlData_Node::OK(aStatus, VrmlData_Node::ReadInteger(theBuffer,
786                                                                    anIntValue)))
787           break;
788         // Check for valid delimiter (']' or ',') 
789         if (!VrmlData_Node::OK(aStatus, ReadLine(theBuffer)))
790           break;
791         if (theBuffer.LinePtr[0] == ']') {
792           theBuffer.LinePtr++;
793           isMore = Standard_False;
794         }
795         if (anIntValue >= 0)
796           // The input value is a node index, store it in the buffer vector
797           vecInt.Append (static_cast<Standard_Integer> (anIntValue));
798         if ((anIntValue < 0 || isMore == Standard_False)
799             && vecInt.Length() > 0)
800         {
801           const Standard_Integer aLen = vecInt.Length();
802           // The input is the end-of-face, store and close this face
803           Standard_Integer * bufFace = static_cast <Standard_Integer *>
804             (myAllocator->Allocate((aLen+1) * sizeof(Standard_Integer)));
805           if (bufFace == 0L) {
806             aStatus = VrmlData_UnrecoverableError;
807             break;
808           }
809           bufFace[0] = aLen;
810           for (Standard_Integer i = 0; i < aLen; i++)
811             bufFace[i+1] = vecInt(i);
812           vecInt.Clear();
813           vecIndice.Append(bufFace);
814         }
815       }
816       if (aStatus == VrmlData_StatusOK) {
817         const Standard_Size aNbBlocks =
818           static_cast <Standard_Size> (vecIndice.Length());
819         if (aNbBlocks) {
820           const Standard_Integer ** anArray =
821             static_cast <const Standard_Integer **>
822             (myAllocator->Allocate (aNbBlocks * sizeof(Standard_Integer *)));
823           if (anArray == 0L)
824             aStatus = VrmlData_UnrecoverableError;
825           else {
826             for (size_t i = 0; i < aNbBlocks; i++)
827               anArray[i] = vecIndice(i);
828             theNBlocks = aNbBlocks;
829             theArray = anArray;
830           } 
831         }
832       }
833     }
834   return aStatus;
835 }
836
837 //=======================================================================
838 //function : writeArrIndex
839 //purpose  : 
840 //=======================================================================
841
842 VrmlData_ErrorStatus VrmlData_Scene::WriteArrIndex
843                                 (const char *              thePrefix,
844                                  const Standard_Integer ** theArrIndex,
845                                  const Standard_Size       theNbBlocks) const
846 {
847   VrmlData_ErrorStatus aStatus (VrmlData_StatusOK);
848   if (theNbBlocks && (IsDummyWrite() == Standard_False)) {
849     if (VrmlData_Node::OK (aStatus,
850                            WriteLine (thePrefix, "[", 1)))
851     {
852       const size_t aLineLimit = (myCurrentIndent < 41) ? 36 : 100;
853       char buf[256];
854       for (Standard_Size iBlock = 0; iBlock < theNbBlocks; iBlock++) {
855         const Standard_Integer nVal (* theArrIndex[iBlock]);
856         const Standard_Integer * arrVal = theArrIndex[iBlock]+1;
857         switch (nVal) {
858         case 1:
859           sprintf (buf, "%d,", arrVal[0]);
860           break;
861         case 2:
862           sprintf (buf, "%d,%d,", arrVal[0], arrVal[1]);
863           break;
864         case 3:
865           sprintf (buf, "%d,%d,%d,", arrVal[0], arrVal[1], arrVal[2]);
866           break;
867         case 4:
868           sprintf (buf, "%d,%d,%d,%d,",
869                    arrVal[0], arrVal[1], arrVal[2], arrVal[3]);
870           break;
871         default:
872           if (nVal > 0) {
873             char * ptr = &buf[0];
874             for (Standard_Integer i = 0; i < nVal; i++) {
875               sprintf (ptr, "%d,", arrVal[i]);
876               ptr = strchr (ptr, ',') + 1;
877               if ((ptr - &buf[0]) > (ptrdiff_t)aLineLimit) {
878                 WriteLine(buf);
879                 ptr = &buf[0];
880               }
881             }
882           }
883         }
884         WriteLine (buf, iBlock < theNbBlocks-1 ? "-1," : "-1");
885       }
886       if (aStatus == VrmlData_StatusOK)
887         aStatus = WriteLine ("]", 0L, -1);
888     }
889   }
890   return aStatus;
891 }
892
893 //=======================================================================
894 //function : WriteXYZ
895 //purpose  : 
896 //=======================================================================
897
898 VrmlData_ErrorStatus VrmlData_Scene::WriteXYZ
899                                 (const gp_XYZ&          theXYZ,
900                                  const Standard_Boolean isApplyScale,
901                                  const char             * thePostfix) const
902 {
903   char buf[240];
904   if (IsDummyWrite() == Standard_False)
905     if (isApplyScale && myLinearScale > Precision::Confusion())
906       sprintf (buf, "%.12g %.12g %.12g%s", theXYZ.X() / myLinearScale,
907                theXYZ.Y() / myLinearScale, theXYZ.Z() / myLinearScale,
908                thePostfix ? thePostfix : "");
909     else
910       sprintf (buf, "%.12g %.12g %.12g%s", theXYZ.X(), theXYZ.Y(), theXYZ.Z(),
911                thePostfix ? thePostfix : "");
912   return WriteLine (buf);
913 }
914
915 //=======================================================================
916 //function : WriteLine
917 //purpose  : write the given string prepending the current indentation
918 //=======================================================================
919
920 VrmlData_ErrorStatus VrmlData_Scene::WriteLine
921                                         (const char             * theLin0,
922                                          const char             * theLin1,
923                                          const Standard_Integer theIndent) const
924 {
925   static const char spaces[] = "                                        "
926                                "                                        ";
927   VrmlData_ErrorStatus& aStatus =
928     const_cast <VrmlData_ErrorStatus&> (myStatus);
929   if (IsDummyWrite())
930     aStatus = VrmlData_StatusOK;
931   else {
932     Standard_Integer& aCurrentIndent =
933       const_cast <Standard_Integer&> (myCurrentIndent);
934     if (theIndent < 0)
935       aCurrentIndent -= myIndent;
936     if (aCurrentIndent < 0)
937       aCurrentIndent = 0;
938     if (theLin0 == 0L && theLin1 == 0L)
939       (* myOutput) << endl;
940     else {
941       const Standard_Integer nSpaces = Min (aCurrentIndent, sizeof(spaces)-1);
942       (* myOutput) << &spaces[sizeof(spaces)-1 - nSpaces];
943       if (theLin0) {
944         (* myOutput) << theLin0;
945         if (theLin1)
946           (* myOutput) << ' ' << theLin1;
947       } else
948         (* myOutput) << theLin1;
949       (* myOutput) << endl;
950     }
951     const int stat = myOutput->rdstate();
952     if (stat & ios::badbit)
953       aStatus = VrmlData_UnrecoverableError;
954     else if (stat & ios::failbit)
955 //       if (stat & ios::eofbit)
956 //         aStatus = VrmlData_EndOfFile;
957 //       else
958       aStatus = VrmlData_GeneralError;
959     if (theIndent > 0)
960       aCurrentIndent += myIndent;
961   }
962   return myStatus;
963 }
964
965 //=======================================================================
966 //function : WriteNode
967 //purpose  : 
968 //=======================================================================
969
970 VrmlData_ErrorStatus VrmlData_Scene::WriteNode
971                                 (const char *                 thePrefix,
972                                  const Handle(VrmlData_Node)& theNode) const
973 {
974   VrmlData_ErrorStatus aStatus (VrmlData_StatusOK);
975   Standard_Boolean isNoName (Standard_False);
976   if (theNode->Name() == 0L)
977     isNoName = Standard_True;
978   else if (theNode->Name()[0] == '\0')
979     isNoName = Standard_True;
980
981   if (theNode.IsNull() == Standard_False)
982     if (theNode->IsDefault() == Standard_False) {
983       if (isNoName && IsDummyWrite()) {
984         // We are in a tentative 'write' session (nothing is written).
985         // The goal is to identify multiply referred nodes.
986         Standard_Address addrNode = theNode.operator->();
987         if (!const_cast<NCollection_Map<Standard_Address>&>(myUnnamedNodesOut)
988             .Add (addrNode))
989         {
990           Handle(VrmlData_UnknownNode) bidNode = new VrmlData_UnknownNode;
991           char buf[32];
992           do {
993             sprintf (buf, "_%d",
994                      ++const_cast<Standard_Integer&>(myAutoNameCounter));
995             bidNode->myName = &buf[0];
996           } while (myNamedNodes.Contains (bidNode));
997           // We found the vacant automatic name, let us assign to it.
998           theNode->setName (&buf[0]);
999           const_cast<VrmlData_MapOfNode&>(myNamedNodes).Add (theNode);
1000           return aStatus; // do not search under already duplicated node
1001         }
1002       }
1003       if (isNoName)
1004         aStatus = theNode->Write (thePrefix);
1005       else {
1006         // If the node name consists of blank characters, we do not write it
1007         const char * nptr = theNode->Name();
1008         for (; * nptr != '\0'; nptr++)
1009           if (* nptr != ' ' && * nptr != '\t')
1010             break;
1011         if (* nptr == '\0')
1012           aStatus = theNode->Write (thePrefix);
1013         else {
1014           // Name is written under DEF clause
1015           TCollection_AsciiString buf;
1016           if (myNamedNodesOut.Contains (theNode))
1017           {
1018             buf += "USE ";
1019             buf += theNode->Name();
1020             aStatus = WriteLine (thePrefix, buf.ToCString());
1021           } 
1022           else 
1023           {
1024             if (thePrefix)
1025             {
1026               buf += thePrefix;
1027               buf += ' ';
1028             }
1029             buf += "DEF ";
1030             buf += theNode->Name();
1031             aStatus = theNode->Write (buf.ToCString());
1032             const_cast<VrmlData_MapOfNode&>(myNamedNodesOut).Add (theNode);
1033           }
1034         }
1035       }
1036     }
1037   return aStatus;
1038 }
1039
1040 //=======================================================================
1041 //function : Dump
1042 //purpose  : 
1043 //=======================================================================
1044
1045 void VrmlData_Scene::Dump (Standard_OStream& theStream) const
1046 {
1047   theStream << " ===== Diagnostic Dump of a Scene (" << myAllNodes.Extent()
1048             << " nodes)" << endl;
1049
1050   /*
1051   Iterator anIterA(myAllNodes);
1052   for (; anIterA.More(); anIterA.Next())
1053     dumpNode(theStream, anIterA.Value(), "");
1054   */
1055   Iterator anIter(myLstNodes);
1056   for (; anIter.More(); anIter.Next())
1057     dumpNode(theStream, anIter.Value(), "  ");
1058 }
1059
1060 //=======================================================================
1061 //function : dumpNode
1062 //purpose  : static (local) function
1063 //=======================================================================
1064
1065 void dumpNode (Standard_OStream&                theStream,
1066                const Handle(VrmlData_Node)&     theNode,
1067                const TCollection_AsciiString&   theIndent)
1068 {
1069   if (theNode.IsNull())
1070     return;
1071   TCollection_AsciiString aNewIndent = 
1072     theIndent.IsEmpty() ? theIndent : theIndent + "  "; 
1073   if (theNode->IsKind(STANDARD_TYPE(VrmlData_Appearance))) {
1074     const Handle(VrmlData_Appearance) anAppearance = 
1075       Handle(VrmlData_Appearance)::DownCast (theNode);
1076     dumpNodeHeader (theStream, theIndent, "Appearance", theNode->Name());
1077     if (theIndent.IsEmpty() == Standard_False) {
1078       dumpNode (theStream, anAppearance->Material(), aNewIndent);
1079       dumpNode (theStream, anAppearance->Texture(), aNewIndent);
1080       dumpNode (theStream, anAppearance->TextureTransform(), aNewIndent);
1081     }
1082   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_ShapeNode))) {
1083     const Handle(VrmlData_ShapeNode) aShape = 
1084       Handle(VrmlData_ShapeNode)::DownCast (theNode);
1085     dumpNodeHeader (theStream, theIndent, "Shape", theNode->Name());
1086     if (theIndent.IsEmpty() == Standard_False) {
1087       dumpNode (theStream, aShape->Appearance(), aNewIndent);
1088       dumpNode (theStream, aShape->Geometry(), aNewIndent);
1089     }
1090   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Box)))
1091     dumpNodeHeader (theStream, theIndent, "Box", theNode->Name());
1092   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Cylinder)))
1093     dumpNodeHeader (theStream, theIndent, "Cylinder", theNode->Name());
1094   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Sphere)))
1095     dumpNodeHeader (theStream, theIndent, "Sphere", theNode->Name());
1096   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Cone)))
1097     dumpNodeHeader (theStream, theIndent, "Cone", theNode->Name());
1098   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Coordinate)))
1099     dumpNodeHeader (theStream, theIndent, "Coordinate", theNode->Name());
1100   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Group))) {
1101     const Handle(VrmlData_Group) aGroup = 
1102       Handle(VrmlData_Group)::DownCast (theNode);
1103     char buf[64];
1104     sprintf (buf, "Group (%s)",
1105              aGroup->IsTransform() ? "Transform" : "Group");
1106     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1107     if (theIndent.IsEmpty() == Standard_False) {
1108       VrmlData_ListOfNode::Iterator anIter = aGroup->NodeIterator();
1109       for (; anIter.More(); anIter.Next())
1110         dumpNode (theStream, anIter.Value(), aNewIndent);
1111     }
1112   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_ImageTexture)))
1113     dumpNodeHeader (theStream, theIndent, "ImageTexture", theNode->Name());
1114   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_IndexedFaceSet))) {
1115     const Handle(VrmlData_IndexedFaceSet) aNode =
1116       Handle(VrmlData_IndexedFaceSet)::DownCast(theNode);
1117     const Standard_Integer ** ppDummy; 
1118     const Standard_Size nCoord = aNode->Coordinates()->Length();
1119     const Standard_Size nPoly  = aNode->Polygons (ppDummy);
1120     char buf[64];
1121     sprintf (buf, "IndexedFaceSet (%d vertices, %d polygons)", nCoord, nPoly);
1122     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1123   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_IndexedLineSet))) {
1124     const Handle(VrmlData_IndexedLineSet) aNode =
1125       Handle(VrmlData_IndexedLineSet)::DownCast(theNode);
1126     const Standard_Integer ** ppDummy; 
1127     const Standard_Size nCoord = aNode->Coordinates()->Length();
1128     const Standard_Size nPoly  = aNode->Polygons (ppDummy);
1129     char buf[64];
1130     sprintf (buf, "IndexedLineSet (%d vertices, %d polygons)", nCoord, nPoly);
1131     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1132   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Material))) {
1133 //     const Handle(VrmlData_Material) aMaterial = 
1134 //       Handle(VrmlData_Material)::DownCast (theNode);
1135     dumpNodeHeader (theStream, theIndent, "Material", theNode->Name());
1136   }
1137   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Normal)))
1138     dumpNodeHeader (theStream, theIndent, "Normal", theNode->Name());
1139   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_TextureCoordinate)))
1140     dumpNodeHeader (theStream, theIndent, "TextureCoordinate", theNode->Name());
1141   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_WorldInfo)))
1142     dumpNodeHeader (theStream, theIndent, "WorldInfo", theNode->Name());
1143   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_UnknownNode))) {
1144     const Handle(VrmlData_UnknownNode) anUnknown = 
1145       Handle(VrmlData_UnknownNode)::DownCast (theNode);
1146     char buf[64];
1147     sprintf (buf, "Unknown (%s)", anUnknown->GetTitle().ToCString());
1148     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1149   }
1150 }
1151
1152 //=======================================================================
1153 //function : dumpNodeHeader
1154 //purpose  : 
1155 //=======================================================================
1156
1157 void dumpNodeHeader (Standard_OStream&                  theStream,
1158                      const TCollection_AsciiString&     theIndent,
1159                      const char *                       theType,
1160                      const char *                       theName)
1161 {
1162   theStream << theIndent << theType <<" node";
1163   if (theName[0] == '\0')
1164     theStream << endl;
1165   else
1166     theStream << ": \"" << theName << '\"' << endl;
1167 }