0031313: Foundation Classes - Dump improvement for classes
[occt.git] / src / Standard / Standard_Dump.cxx
1 // Copyright (c) 2019 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <Standard_Dump.hxx>
15
16 #include <stdarg.h>
17
18 // =======================================================================
19 // function : AddValuesSeparator
20 // purpose :
21 // =======================================================================
22 void Standard_Dump::AddValuesSeparator (Standard_OStream& theOStream)
23 {
24   Standard_SStream aStream;
25   aStream << theOStream.rdbuf();
26   TCollection_AsciiString aStreamStr = Standard_Dump::Text (aStream);
27   if (!aStreamStr.IsEmpty() && !aStreamStr.EndsWith ("{"))
28     theOStream << ", ";
29 }
30
31 //=======================================================================
32 //function : DumpKeyToClass
33 //purpose  : 
34 //=======================================================================
35 void Standard_Dump::DumpKeyToClass (Standard_OStream& theOStream,
36                                     const TCollection_AsciiString& theKey,
37                                     const TCollection_AsciiString& theField)
38 {
39   AddValuesSeparator (theOStream);
40   theOStream << "\"" << theKey << "\": {" << theField << "}";
41 }
42
43 //=======================================================================
44 //function : DumpCharacterValues
45 //purpose  : 
46 //=======================================================================
47 void Standard_Dump::DumpCharacterValues (Standard_OStream& theOStream, int theCount, ...)
48 {
49   va_list  vl;
50   va_start(vl, theCount);
51   for(int i = 0; i < theCount; ++i)
52   {
53     if (i > 0)
54       theOStream << ", ";
55     theOStream << "\"" << va_arg(vl, char*) << "\"";
56   }
57   va_end(vl);
58 }
59
60 //=======================================================================
61 //function : DumpRealValues
62 //purpose  : 
63 //=======================================================================
64 void Standard_Dump::DumpRealValues (Standard_OStream& theOStream, int theCount, ...)
65 {
66   va_list  vl;
67   va_start(vl, theCount);
68   for(int i = 0; i < theCount; ++i)
69   {
70     if (i > 0)
71       theOStream << ", ";
72     theOStream << va_arg(vl, Standard_Real);
73   }
74   va_end(vl);
75 }
76
77 // =======================================================================
78 // function : GetPointerInfo
79 // purpose :
80 // =======================================================================
81 TCollection_AsciiString Standard_Dump::GetPointerInfo (const Handle(Standard_Transient)& thePointer,
82                                                        const bool isShortInfo)
83 {
84   if (thePointer.IsNull())
85     return TCollection_AsciiString();
86
87   return GetPointerInfo (thePointer.get(), isShortInfo);
88 }
89
90 // =======================================================================
91 // function : GetPointerInfo
92 // purpose :
93 // =======================================================================
94 TCollection_AsciiString Standard_Dump::GetPointerInfo (const void* thePointer, const bool isShortInfo)
95 {
96   if (!thePointer)
97     return TCollection_AsciiString();
98
99   std::ostringstream aPtrStr;
100   aPtrStr << thePointer;
101   if (!isShortInfo)
102     return aPtrStr.str().c_str();
103
104   TCollection_AsciiString anInfoPtr (aPtrStr.str().c_str());
105   for (int aSymbolId = 1; aSymbolId < anInfoPtr.Length(); aSymbolId++)
106   {
107     if (anInfoPtr.Value(aSymbolId) != '0')
108     {
109       anInfoPtr = anInfoPtr.SubString (aSymbolId, anInfoPtr.Length());
110       anInfoPtr.Prepend (GetPointerPrefix());
111       return anInfoPtr;
112     }
113   }
114   return aPtrStr.str().c_str();
115 }
116
117 // =======================================================================
118 // DumpFieldToName
119 // =======================================================================
120 TCollection_AsciiString Standard_Dump::DumpFieldToName (const TCollection_AsciiString& theField)
121 {
122   TCollection_AsciiString aName = theField;
123   if (theField.StartsWith ('&'))
124   {
125     aName.Remove (1, 1);
126   }
127
128   if (aName.Length() > 1 && aName.Value (1) == 'a')
129   {
130     if (aName.Length() > 2 && aName.Value (2) == 'n')
131     {
132       aName.Remove (1, 2);
133     }
134     else
135      aName.Remove (1, 1);
136   }
137   else if (aName.Length() > 2 && ::LowerCase (aName.Value (1)) == 'm' && aName.Value (2) == 'y')
138   {
139     aName.Remove (1, 2);
140   }
141
142   if (aName.EndsWith (".get()"))
143   {
144     aName = aName.SubString (1, aName.Length() - TCollection_AsciiString (".get()").Length());
145   }
146   else if (aName.EndsWith ("()"))
147   {
148     aName = aName.SubString (1, aName.Length() - TCollection_AsciiString ("()").Length());
149   }
150   return aName;
151 }
152
153 // =======================================================================
154 // Text
155 // =======================================================================
156 TCollection_AsciiString Standard_Dump::Text (const Standard_SStream& theStream)
157 {
158   return TCollection_AsciiString (theStream.str().c_str());
159 }
160
161 // =======================================================================
162 // FormatJson
163 // =======================================================================
164 TCollection_AsciiString Standard_Dump::FormatJson (const Standard_SStream& theStream,
165                                                    const Standard_Integer theIndent)
166 {
167   TCollection_AsciiString aStreamStr = Text (theStream);
168   TCollection_AsciiString anIndentStr;
169   for (Standard_Integer anIndentId = 0; anIndentId < theIndent; anIndentId++)
170     anIndentStr.AssignCat (' ');
171
172   TCollection_AsciiString aText;
173
174   Standard_Integer anIndentCount = 0;
175   Standard_Boolean isMassiveValues = Standard_False;
176   for (Standard_Integer anIndex = 1; anIndex < aStreamStr.Length(); anIndex++)
177   {
178     Standard_Character aSymbol = aStreamStr.Value (anIndex);
179     if (aSymbol == '{')
180     {
181       anIndentCount++;
182
183       aText += aSymbol;
184       aText += '\n';
185
186       for (int anIndent = 0; anIndent < anIndentCount; anIndent++)
187         aText += anIndentStr;
188     }
189     else if (aSymbol == '}')
190     {
191       anIndentCount--;
192
193       aText += '\n';
194       for (int anIndent = 0; anIndent < anIndentCount; anIndent++)
195         aText += anIndentStr;
196       aText += aSymbol;
197     }
198     else if (aSymbol == '[')
199     {
200       isMassiveValues = Standard_True;
201       aText += aSymbol;
202     }
203     else if (aSymbol == ']')
204     {
205       isMassiveValues = Standard_False;
206       aText += aSymbol;
207     }
208     else if (aSymbol == ',')
209     {
210       if (!isMassiveValues)
211       {
212         aText += aSymbol;
213         aText += '\n';
214         for (int anIndent = 0; anIndent < anIndentCount; anIndent++)
215           aText += anIndentStr;
216         if (anIndex + 1 < aStreamStr.Length() && aStreamStr.Value (anIndex + 1) == ' ')
217           anIndex++; // skip empty value after comma
218       }
219       else
220         aText += aSymbol;
221     }
222     else
223       aText += aSymbol;
224   }
225   return aText;
226 }
227
228 // =======================================================================
229 // SplitJson
230 // =======================================================================
231 Standard_Boolean Standard_Dump::SplitJson (const TCollection_AsciiString& theStreamStr,
232                                            NCollection_IndexedDataMap<TCollection_AsciiString, Standard_DumpValue>& theKeyToValues)
233 {
234   Standard_Integer aNextIndex = 1;
235   while (aNextIndex < theStreamStr.Length())
236   {
237     Standard_JsonKey aKey = Standard_JsonKey_None;
238     if (!jsonKey (theStreamStr, aNextIndex, aNextIndex, aKey))
239       return Standard_False;
240
241     Standard_Boolean aProcessed = Standard_False;
242     switch (aKey)
243     {
244       case Standard_JsonKey_Quote:
245       {
246         aProcessed = splitKeyToValue (theStreamStr, aNextIndex, aNextIndex, theKeyToValues);
247         break;
248       }
249       case Standard_JsonKey_OpenChild:
250       {
251         Standard_Integer aStartIndex = aNextIndex;
252         Standard_Integer aClosePos = nextClosePosition (theStreamStr, aStartIndex, Standard_JsonKey_OpenChild, Standard_JsonKey_CloseChild);
253         if (aClosePos == 0)
254           return Standard_False;
255
256         TCollection_AsciiString aSubStreamStr = theStreamStr.SubString (aStartIndex + JsonKeyLength (aKey), aNextIndex - 2);
257         if (!SplitJson (aSubStreamStr, theKeyToValues))
258           return Standard_False;
259
260         aNextIndex = aClosePos + Standard_Integer (JsonKeyLength (Standard_JsonKey_CloseChild));
261         break;
262       }
263       case Standard_JsonKey_SeparatorValueToValue:
264       {
265         continue;
266       }
267       default:
268         break;
269     }
270     if (!aProcessed)
271       return Standard_False;
272   }
273   return Standard_True;
274 }
275
276 // =======================================================================
277 // HierarchicalValueIndices
278 // =======================================================================
279 NCollection_List<Standard_Integer> Standard_Dump::HierarchicalValueIndices (
280     const NCollection_IndexedDataMap<TCollection_AsciiString, TCollection_AsciiString>& theValues)
281 {
282   NCollection_List<Standard_Integer> anIndices;
283
284   for (Standard_Integer anIndex = 1; anIndex <= theValues.Extent(); anIndex++)
285   {
286     if (HasChildKey (theValues.FindFromIndex (anIndex)))
287       anIndices.Append (anIndex);
288   }
289   return anIndices;
290 }
291
292 // =======================================================================
293 // splitKeyToValue
294 // =======================================================================
295 Standard_Boolean Standard_Dump::splitKeyToValue (const TCollection_AsciiString& theStreamStr,
296                                                  Standard_Integer theStartIndex,
297                                                  Standard_Integer& theNextIndex,
298                                                  NCollection_IndexedDataMap<TCollection_AsciiString, Standard_DumpValue>& theValues)
299 {
300   // find key value: "key"
301   Standard_Integer aStartIndex = theStartIndex;
302   Standard_Integer aCloseIndex = nextClosePosition (theStreamStr, aStartIndex + 1, Standard_JsonKey_None, Standard_JsonKey_Quote);
303   if (aCloseIndex == 0)
304     return Standard_False;
305
306   TCollection_AsciiString aSplitKey = theStreamStr.SubString (aStartIndex, aCloseIndex - 1);
307
308   // key to value
309   aStartIndex = aCloseIndex + 1;
310   Standard_JsonKey aKey = Standard_JsonKey_None;
311   if (!jsonKey (theStreamStr, aStartIndex, aCloseIndex, aKey))
312     return Standard_False;
313
314   // find value
315   aStartIndex = aCloseIndex;
316   aKey = Standard_JsonKey_None;
317   jsonKey (theStreamStr, aStartIndex, aCloseIndex, aKey);
318   aStartIndex = aCloseIndex;
319
320   TCollection_AsciiString aSplitValue;
321   theNextIndex = -1;
322   switch (aKey)
323   {
324     case Standard_JsonKey_OpenChild:
325     {
326       aCloseIndex = nextClosePosition (theStreamStr, aStartIndex, Standard_JsonKey_OpenChild, Standard_JsonKey_CloseChild);
327       if (aCloseIndex > aStartIndex)
328         aSplitValue = theStreamStr.SubString (aStartIndex, aCloseIndex);
329       theNextIndex = aCloseIndex + 1;
330       break;
331     }
332     case Standard_JsonKey_OpenContainer:
333     {
334       aCloseIndex = nextClosePosition (theStreamStr, aStartIndex, Standard_JsonKey_OpenContainer, Standard_JsonKey_CloseContainer);
335       if (aCloseIndex > aStartIndex)
336         aSplitValue = theStreamStr.SubString (aStartIndex, aCloseIndex - 1);
337       theNextIndex = aCloseIndex + 1;
338       break;
339     }
340     case Standard_JsonKey_Quote:
341     {
342       Standard_JsonKey aKeyTmp = Standard_JsonKey_None;
343       if (jsonKey (theStreamStr, aStartIndex, aCloseIndex, aKeyTmp) && aKeyTmp == Standard_JsonKey_Quote) // emptyValue
344       {
345         aSplitValue = "";
346         theNextIndex = aCloseIndex;
347       }
348       else
349       {
350         aCloseIndex = nextClosePosition (theStreamStr, aStartIndex + 1, Standard_JsonKey_None, Standard_JsonKey_Quote);
351         aSplitValue = theStreamStr.SubString (aStartIndex, aCloseIndex - 1);
352         theNextIndex = aCloseIndex + 1;
353       }
354       break;
355     }
356     case Standard_JsonKey_None:
357     {
358       if (aStartIndex == theStreamStr.Length())
359       {
360         aSplitValue = aStartIndex <= aCloseIndex ? theStreamStr.SubString (aStartIndex, aCloseIndex) : "";
361         aSplitValue = theStreamStr.SubString (aStartIndex, aCloseIndex);
362         aCloseIndex = aStartIndex;
363       }
364       else
365       {
366         Standard_Integer aCloseIndex1 = nextClosePosition (theStreamStr, aStartIndex, Standard_JsonKey_None, Standard_JsonKey_CloseChild) - 1;
367         Standard_Integer aCloseIndex2 = nextClosePosition (theStreamStr, aStartIndex, Standard_JsonKey_None, Standard_JsonKey_SeparatorValueToValue) - 1;
368         aCloseIndex = aCloseIndex1 < aCloseIndex2 ? aCloseIndex1 : aCloseIndex2;
369         aSplitValue = aStartIndex <= aCloseIndex ? theStreamStr.SubString (aStartIndex, aCloseIndex) : "";
370       }
371       theNextIndex = aCloseIndex + 1;
372       break;
373     }
374     default:
375       return Standard_False;
376   }
377
378   Standard_DumpValue aValue;
379   if (theValues.FindFromKey (aSplitKey, aValue))
380   {
381     Standard_Integer anIndex = 1;
382     // increment key until the new key does not exist in the container
383     TCollection_AsciiString anIndexedSuffix = TCollection_AsciiString ("_") + TCollection_AsciiString (anIndex);
384     while (theValues.FindFromKey (TCollection_AsciiString (aSplitKey + anIndexedSuffix), aValue))
385     {
386       anIndex++;
387       anIndexedSuffix = TCollection_AsciiString ("_") + TCollection_AsciiString (anIndex);
388     }
389     aSplitKey = aSplitKey + anIndexedSuffix;
390   }
391
392   theValues.Add (aSplitKey, Standard_DumpValue (aSplitValue, aStartIndex));
393   return Standard_True;
394 }
395
396 // =======================================================================
397 // jsonKey
398 // =======================================================================
399 Standard_Boolean Standard_Dump::jsonKey (const TCollection_AsciiString& theStreamStr,
400                                          Standard_Integer theStartIndex,
401                                          Standard_Integer& theNextIndex,
402                                          Standard_JsonKey& theKey)
403 {
404   TCollection_AsciiString aSubStreamStr = theStreamStr.SubString (theStartIndex, theStreamStr.Length());
405   for (Standard_Integer aKeyId = (Standard_Integer)Standard_JsonKey_OpenChild; aKeyId <= Standard_JsonKey_SeparatorValueToValue; aKeyId++)
406   {
407     Standard_JsonKey aKey = (Standard_JsonKey)aKeyId;
408     Standard_CString aKeyToStr = JsonKeyToString (aKey);
409     if (!aSubStreamStr.StartsWith (aKeyToStr))
410       continue;
411
412     theNextIndex = theStartIndex + Standard_Integer (JsonKeyLength (aKey));
413     theKey = aKey;
414     return Standard_True;
415   }
416   return Standard_False;
417 }
418
419 // =======================================================================
420 // HasChildKey
421 // =======================================================================
422 Standard_Boolean Standard_Dump::HasChildKey (const TCollection_AsciiString& theSourceValue)
423 {
424   return theSourceValue.Search (JsonKeyToString (Standard_JsonKey_SeparatorKeyToValue)) >= 0;
425 }
426
427 // =======================================================================
428 // JsonKeyToString
429 // =======================================================================
430 Standard_CString Standard_Dump::JsonKeyToString (const Standard_JsonKey theKey)
431 {
432   switch (theKey)
433   {
434     case Standard_JsonKey_None: return "";
435     case Standard_JsonKey_OpenChild: return "{";
436     case Standard_JsonKey_CloseChild: return "}";
437     case Standard_JsonKey_OpenContainer: return "[";
438     case Standard_JsonKey_CloseContainer: return "]";
439     case Standard_JsonKey_Quote: return "\"";
440     case Standard_JsonKey_SeparatorKeyToValue: return ": ";
441     case Standard_JsonKey_SeparatorValueToValue: return ", ";
442   }
443
444   return "";
445 }
446
447 // =======================================================================
448 // JsonKeyLength
449 // =======================================================================
450 Standard_Integer Standard_Dump::JsonKeyLength (const Standard_JsonKey theKey)
451 {
452   return (Standard_Integer)strlen (JsonKeyToString (theKey));
453 }
454
455 // =======================================================================
456 // nextClosePosition
457 // =======================================================================
458 Standard_Integer Standard_Dump::nextClosePosition (const TCollection_AsciiString& theSourceValue,
459                                                    const Standard_Integer theStartPosition,
460                                                    const Standard_JsonKey theOpenKey,
461                                                    const Standard_JsonKey theCloseKey)
462 {
463   Standard_CString anOpenKey = JsonKeyToString (theOpenKey);
464   Standard_CString aCloseKeyStr = JsonKeyToString (theCloseKey);
465
466   Standard_Integer aStartPos = theStartPosition;
467   Standard_Integer aDepthKey = 0;
468
469   while (aStartPos < theSourceValue.Length())
470   {
471     Standard_Integer anOpenKeyPos = theSourceValue.Location (anOpenKey, aStartPos, theSourceValue.Length());
472     Standard_Integer aCloseKeyPos = theSourceValue.Location (aCloseKeyStr, aStartPos, theSourceValue.Length());
473     if (aCloseKeyPos == 0)
474       break;
475
476     if (anOpenKeyPos != 0 && anOpenKeyPos <= aCloseKeyPos)
477     {
478       aDepthKey++;
479       aStartPos = anOpenKeyPos + 1;
480     }
481     else
482     {
483       if (aDepthKey == 0)
484         return aCloseKeyPos;
485       else
486       {
487         aDepthKey--;
488         aStartPos = aCloseKeyPos + 1;
489       }
490     }
491   }
492   return theSourceValue.Length();
493 }