1 /*  $Id: objostrxml.cpp 608769 2020-05-20 19:18:29Z vasilche $
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Eugene Vasilchenko
27 *
28 * File Description:
29 *   XML object output stream
30 *
31 */
32 
33 #include <ncbi_pch.hpp>
34 #include <corelib/ncbistd.hpp>
35 #include <corelib/ncbi_limits.h>
36 
37 #include <serial/objostrxml.hpp>
38 #include <serial/objistr.hpp>
39 #include <serial/objcopy.hpp>
40 #include <serial/impl/memberid.hpp>
41 #include <serial/impl/memberlist.hpp>
42 #include <serial/enumvalues.hpp>
43 #include <serial/objhook.hpp>
44 #include <serial/impl/classinfo.hpp>
45 #include <serial/impl/choice.hpp>
46 #include <serial/impl/continfo.hpp>
47 #include <serial/impl/aliasinfo.hpp>
48 #include <serial/delaybuf.hpp>
49 #include <serial/impl/ptrinfo.hpp>
50 #include <serial/error_codes.hpp>
51 
52 #include <stdio.h>
53 #include <math.h>
54 
55 
56 #define NCBI_USE_ERRCODE_X   Serial_OStream
57 
58 BEGIN_NCBI_SCOPE
59 
OpenObjectOStreamXml(CNcbiOstream & out,EOwnership deleteOut)60 CObjectOStream* CObjectOStream::OpenObjectOStreamXml(CNcbiOstream& out,
61                                                      EOwnership deleteOut)
62 {
63     return new CObjectOStreamXml(out, deleteOut);
64 }
65 
66 
67 string CObjectOStreamXml::sm_DefaultDTDFilePrefix = "";
68 const char* sm_DefaultNamespacePrefix = "ns";
69 const char* sm_DefaultSchemaNamespace = "http://www.ncbi.nlm.nih.gov";
70 static
71 const char* s_SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
72 
73 
CObjectOStreamXml(CNcbiOstream & out,bool deleteOut)74 CObjectOStreamXml::CObjectOStreamXml(CNcbiOstream& out, bool deleteOut)
75     : CObjectOStream(eSerial_Xml, out, deleteOut ? eTakeOwnership : eNoOwnership),
76       m_LastTagAction(eTagClose), m_SpecRef(eSpecRefNotSet), m_EndTag(true),
77       m_UseDefaultDTDFilePrefix( true),
78       m_UsePublicId( true),
79       m_Attlist( false), m_StdXml( false), m_EnforcedStdXml( false),
80       m_RealFmt( eRealScientificFormat ),
81       m_Encoding( eEncoding_Unknown ), m_StringEncoding( eEncoding_UTF8 ),
82       m_UseXmlDecl( true ), m_UseSchemaLoc( true ),
83       m_DefaultSchemaNamespace( sm_DefaultSchemaNamespace ),
84       m_SkipIndent( false ), m_SkipNextTag( false )
85 {
86     m_Output.SetBackLimit(1);
87 }
88 
CObjectOStreamXml(CNcbiOstream & out,EOwnership deleteOut)89 CObjectOStreamXml::CObjectOStreamXml(CNcbiOstream& out, EOwnership deleteOut)
90     : CObjectOStream(eSerial_Xml, out, deleteOut),
91       m_LastTagAction(eTagClose), m_SpecRef(eSpecRefNotSet), m_EndTag(true),
92       m_UseDefaultDTDFilePrefix( true),
93       m_UsePublicId( true),
94       m_Attlist( false), m_StdXml( false), m_EnforcedStdXml( false),
95       m_RealFmt( eRealScientificFormat ),
96       m_Encoding( eEncoding_Unknown ), m_StringEncoding( eEncoding_UTF8 ),
97       m_UseXmlDecl( true ), m_UseSchemaLoc( true ),
98       m_DefaultSchemaNamespace( sm_DefaultSchemaNamespace ),
99       m_SkipIndent( false ), m_SkipNextTag( false )
100 {
101     m_Output.SetBackLimit(1);
102 }
103 
~CObjectOStreamXml(void)104 CObjectOStreamXml::~CObjectOStreamXml(void)
105 {
106 }
107 
SetEncoding(EEncoding enc)108 void CObjectOStreamXml::SetEncoding(EEncoding enc)
109 {
110     m_Encoding = enc;
111 }
112 
GetEncoding(void) const113 EEncoding CObjectOStreamXml::GetEncoding(void) const
114 {
115     return m_Encoding;
116 }
117 
SetDefaultStringEncoding(EEncoding enc)118 void CObjectOStreamXml::SetDefaultStringEncoding(EEncoding enc)
119 {
120     m_StringEncoding = enc;
121 }
122 
GetDefaultStringEncoding(void) const123 EEncoding CObjectOStreamXml::GetDefaultStringEncoding(void) const
124 {
125     return m_StringEncoding;
126 }
127 
SetReferenceSchema(bool use_schema)128 void CObjectOStreamXml::SetReferenceSchema(bool use_schema)
129 {
130     m_SpecRef = use_schema ? eSpecRefSchema : eSpecRefNone;
131 }
GetReferenceSchema(void) const132 bool CObjectOStreamXml::GetReferenceSchema(void) const
133 {
134     return m_SpecRef == eSpecRefSchema;
135 }
SetReferenceDTD(bool use_dtd)136 void CObjectOStreamXml::SetReferenceDTD(bool use_dtd)
137 {
138     m_SpecRef = use_dtd ? eSpecRefDTD : eSpecRefNone;
139 }
GetReferenceDTD(void) const140 bool CObjectOStreamXml::GetReferenceDTD(void) const
141 {
142     return m_SpecRef == eSpecRefDTD;
143 }
144 
SetUseSchemaLocation(bool use_loc)145 void CObjectOStreamXml::SetUseSchemaLocation(bool use_loc)
146 {
147     m_UseSchemaLoc = use_loc;
148 }
GetUseSchemaLocation(void) const149 bool CObjectOStreamXml::GetUseSchemaLocation(void) const
150 {
151     return m_UseSchemaLoc;
152 }
153 
154 
155 CObjectOStreamXml::ERealValueFormat
GetRealValueFormat(void) const156     CObjectOStreamXml::GetRealValueFormat(void) const
157 {
158     return m_RealFmt;
159 }
SetRealValueFormat(CObjectOStreamXml::ERealValueFormat fmt)160 void CObjectOStreamXml::SetRealValueFormat(
161     CObjectOStreamXml::ERealValueFormat fmt)
162 {
163     m_RealFmt = fmt;
164 }
165 
SetEnforcedStdXml(bool set)166 void CObjectOStreamXml::SetEnforcedStdXml(bool set)
167 {
168     m_EnforcedStdXml = set;
169     if (m_EnforcedStdXml) {
170         m_StdXml = false;
171     }
172 }
173 
SetFormattingFlags(TSerial_Format_Flags flags)174 void CObjectOStreamXml::SetFormattingFlags(TSerial_Format_Flags flags)
175 {
176     TSerial_Format_Flags accepted =
177         fSerial_Xml_NoIndentation | fSerial_Xml_NoEol    |
178         fSerial_Xml_NoXmlDecl     | fSerial_Xml_NoRefDTD |
179         fSerial_Xml_RefSchema     | fSerial_Xml_NoSchemaLoc;
180     if (flags & ~accepted) {
181         ERR_POST_X_ONCE(12, Warning <<
182             "CObjectOStreamXml::SetFormattingFlags: ignoring unknown formatting flags");
183     }
184     m_UseXmlDecl   = (flags & fSerial_Xml_NoXmlDecl)   == 0;
185     if ((flags & fSerial_Xml_NoRefDTD)    != 0) {m_SpecRef = eSpecRefNone;}
186     if ((flags & fSerial_Xml_RefSchema)   != 0) {m_SpecRef = eSpecRefSchema;}
187     m_UseSchemaLoc = (flags & fSerial_Xml_NoSchemaLoc) == 0;
188 
189     CObjectOStream::SetFormattingFlags(
190         flags & (fSerial_Xml_NoIndentation | fSerial_Xml_NoEol));
191 }
192 
193 
GetPosition(void) const194 string CObjectOStreamXml::GetPosition(void) const
195 {
196     return "line "+NStr::SizetToString(m_Output.GetLine());
197 }
198 
GetPublicModuleName(TTypeInfo type)199 static string GetPublicModuleName(TTypeInfo type)
200 {
201     const string& s = type->GetModuleName();
202     string name;
203     for ( string::const_iterator i = s.begin(); i != s.end(); ++i ) {
204         char c = *i;
205         if ( !isalnum((unsigned char) c) )
206             name += ' ';
207         else
208             name += c;
209     }
210     return name;
211 }
212 
GetModuleName(TTypeInfo type)213 string CObjectOStreamXml::GetModuleName(TTypeInfo type)
214 {
215     string name;
216     if( !m_DTDFileName.empty() ) {
217         name = m_DTDFileName;
218     }
219     else {
220         const string& s = type->GetModuleName();
221         for ( string::const_iterator i = s.begin(); i != s.end(); ++i ) {
222             char c = *i;
223             if ( c == '-' )
224                 name += '_';
225             else
226                 name += c;
227         }
228     }
229     return name;
230 }
231 
WriteFileHeader(TTypeInfo type)232 void CObjectOStreamXml::WriteFileHeader(TTypeInfo type)
233 {
234     if (m_UseXmlDecl) {
235         m_Output.PutString("<?xml version=\"1.0");
236         switch (m_Encoding) {
237         default:
238             break;
239         case eEncoding_UTF8:
240             m_Output.PutString("\" encoding=\"UTF-8");
241             break;
242         case eEncoding_ISO8859_1:
243             m_Output.PutString("\" encoding=\"ISO-8859-1");
244             break;
245         case eEncoding_Windows_1252:
246             m_Output.PutString("\" encoding=\"Windows-1252");
247             break;
248         }
249         m_Output.PutString("\"?>");
250     }
251 
252     if (m_SpecRef == eSpecRefNotSet) {
253         CheckStdXml(type);
254         m_SpecRef = (type->GetDataSpec() == EDataSpec::eDTD || !x_IsStdXml()) ? eSpecRefDTD : eSpecRefSchema;
255     }
256     if (GetReferenceDTD()) {
257         if (m_UseXmlDecl) {
258             m_Output.PutEol();
259         }
260         m_Output.PutString("<!DOCTYPE ");
261         m_Output.PutString(type->GetName());
262 
263         if (m_UsePublicId) {
264             m_Output.PutString(" PUBLIC \"");
265             if (m_PublicId.empty()) {
266                 m_Output.PutString("-//NCBI//");
267                 m_Output.PutString(GetPublicModuleName(type));
268                 m_Output.PutString("/EN");
269             } else {
270                 m_Output.PutString(m_PublicId);
271             }
272             m_Output.PutString("\"");
273         } else {
274             m_Output.PutString(" SYSTEM");
275         }
276         m_Output.PutString(" \"");
277         m_Output.PutString(GetDTDFilePrefix() + GetModuleName(type));
278         m_Output.PutString(".dtd\">");
279     } else if (!m_UseXmlDecl) {
280         m_SkipIndent = true;
281     }
282     m_LastTagAction = eTagClose;
283     m_NsNameToPrefix.clear();
284     m_NsPrefixToName.clear();
285 }
286 
EndOfWrite(void)287 void CObjectOStreamXml::EndOfWrite(void)
288 {
289     m_Output.PutEol(false);
290     CObjectOStream::EndOfWrite();
291 }
292 
x_WriteClassNamespace(TTypeInfo type)293 void CObjectOStreamXml::x_WriteClassNamespace(TTypeInfo type)
294 {
295     if (type->GetName().find(':') != string::npos) {
296         return;
297     }
298     if (!m_Attlist) {
299         OpenTagEndBack();
300     }
301 
302     string ns_name( m_NsPrefixToName[m_CurrNsPrefix]);
303     if (ns_name.empty()) {
304         ns_name = GetDefaultSchemaNamespace();
305     }
306     if (type->HasNamespaceName() || ((type->GetDataSpec() != EDataSpec::eXSD) &&
307          m_NsNameToPrefix.find(ns_name) == m_NsNameToPrefix.end())) {
308         if (m_Attlist) {
309             m_Output.PutString(" xmlns");
310         } else {
311             m_Output.PutEol();
312             m_Output.PutString("    xmlns");
313         }
314         if (!m_CurrNsPrefix.empty()) {
315            m_Output.PutChar(':');
316            m_Output.PutString(m_CurrNsPrefix);
317         }
318         m_Output.PutString("=\"");
319         m_Output.PutString(ns_name + "\"");
320         m_NsPrefixToName[m_CurrNsPrefix] = ns_name;
321         m_NsNameToPrefix[ns_name] = m_CurrNsPrefix;
322     }
323 
324     if (m_UseSchemaLoc) {
325         string xs_name(s_SchemaInstanceNamespace);
326         string xs_prefix("xs");
327         if (m_NsNameToPrefix.find(xs_name) == m_NsNameToPrefix.end()) {
328             for (char a='a';
329                 m_NsPrefixToName.find(xs_prefix) != m_NsPrefixToName.end(); ++a) {
330                 xs_prefix += a;
331             }
332             m_NsPrefixToName[xs_prefix] = xs_name;
333             m_NsNameToPrefix[xs_name] = xs_prefix;
334             m_Output.PutEol();
335             m_Output.PutString("    xmlns:");
336             m_Output.PutString(xs_prefix + "=\"");
337             m_Output.PutString(xs_name + "\"");
338             m_Output.PutEol();
339             m_Output.PutString("    ");
340             m_Output.PutString(xs_prefix);
341             m_Output.PutString(":schemaLocation=\"");
342             m_Output.PutString(ns_name + " ");
343             m_Output.PutString(GetDTDFilePrefix() + GetModuleName(type));
344             m_Output.PutString(".xsd\"");
345             m_Output.PutEol();
346         }
347     }
348     if (!m_Attlist) {
349         OpenTagEnd();
350     }
351 }
352 
x_ProcessTypeNamespace(TTypeInfo type)353 bool CObjectOStreamXml::x_ProcessTypeNamespace(TTypeInfo type)
354 {
355     if (GetReferenceSchema()) {
356         if (type->HasNamespaceName()) {
357             string prefix(type->GetNamespacePrefix());
358             if (prefix.empty() && (type->IsNsQualified() == eNSUnqualified || (m_Attlist && type->IsNsQualified() == eNSQualified))) {
359                 prefix = sm_DefaultNamespacePrefix;
360             }
361             return x_BeginNamespace(type->GetNamespaceName(),prefix);
362         }
363         return true;
364     }
365     return false;
366 }
367 
x_EndTypeNamespace(void)368 void CObjectOStreamXml::x_EndTypeNamespace(void)
369 {
370     if (GetReferenceSchema()) {
371         if (TopFrame().HasTypeInfo()) {
372             TTypeInfo type = TopFrame().GetTypeInfo();
373             if (type->HasNamespaceName()) {
374                 x_EndNamespace(type->GetNamespaceName());
375             }
376         }
377     }
378 }
379 
x_BeginNamespace(const string & ns_name,const string & ns_prefix)380 bool CObjectOStreamXml::x_BeginNamespace(const string& ns_name,
381                                          const string& ns_prefix)
382 {
383     if (!GetReferenceSchema() || ns_name.empty()) {
384         return false;
385     }
386     string nsPrefix(ns_prefix);
387     if (m_Attlist || m_NsNameToPrefix.find(ns_name) == m_NsNameToPrefix.end()) {
388         for (char a='a';
389             m_NsPrefixToName.find(nsPrefix) != m_NsPrefixToName.end(); ++a) {
390             nsPrefix += a;
391         }
392         if (m_Attlist && m_NsNameToPrefix.find(ns_name) != m_NsNameToPrefix.end()) {
393             if (!m_NsNameToPrefix.at(ns_name).empty()) {
394                 m_CurrNsPrefix = m_NsNameToPrefix.at(ns_name);
395                 m_NsPrefixes.push_back(m_CurrNsPrefix);
396                 return false;
397             }
398         }
399         m_CurrNsPrefix = nsPrefix;
400         if (!m_Attlist) {
401             m_NsNameToPrefix[ns_name] = nsPrefix;
402         }
403         m_NsPrefixToName[nsPrefix] = ns_name;
404         m_NsPrefixes.push_back(nsPrefix);
405         return true;
406     } else {
407         m_CurrNsPrefix = m_NsNameToPrefix[ns_name];
408         m_NsPrefixes.push_back(m_CurrNsPrefix);
409     }
410     return false;
411 }
412 
x_EndNamespace(const string & ns_name)413 void CObjectOStreamXml::x_EndNamespace(const string& ns_name)
414 {
415     if (!GetReferenceSchema() || ns_name.empty()) {
416         return;
417     }
418     string nsPrefix = m_CurrNsPrefix;
419 // we should erase them according to Namespace Scoping rules
420 // http://www.w3.org/TR/REC-xml-names/#scoping
421     m_NsPrefixes.pop_back();
422     if (find(m_NsPrefixes.begin(), m_NsPrefixes.end(), nsPrefix)
423         == m_NsPrefixes.end()) {
424         if (!m_Attlist) {
425             m_NsNameToPrefix.erase(ns_name);
426         }
427         m_NsPrefixToName.erase(nsPrefix);
428     }
429     m_CurrNsPrefix = m_NsPrefixes.empty() ? kEmptyStr : m_NsPrefixes.back();
430     if (!m_Attlist && GetStackDepth() <= 2) {
431         m_NsNameToPrefix.clear();
432         m_NsPrefixToName.clear();
433     }
434 }
435 
WriteEnum(const CEnumeratedTypeValues & values,TEnumValueType value,const string & valueName)436 void CObjectOStreamXml::WriteEnum(const CEnumeratedTypeValues& values,
437                                   TEnumValueType value,
438                                   const string& valueName)
439 {
440     bool skipname = valueName.empty() ||
441                   (m_WriteNamedIntegersByValue && values.IsInteger());
442     bool valueonly = m_StdXml;
443     if (valueonly) {
444         if ( values.IsInteger() ) {
445             m_Output.PutInt4(value);
446         } else {
447             m_Output.PutString(valueName);
448         }
449         return;
450     }
451     if ( !m_SkipNextTag && !values.GetName().empty() ) {
452         OpenTagStart();
453         m_Output.PutString(values.GetName());
454         if ( !skipname ) {
455             m_Output.PutString(" value=\"");
456             m_Output.PutString(valueName);
457             m_Output.PutChar('\"');
458         }
459         if ( values.IsInteger() ) {
460             OpenTagEnd();
461             m_Output.PutInt4(value);
462             CloseTagStart();
463             m_Output.PutString(values.GetName());
464             CloseTagEnd();
465         }
466         else {
467             _ASSERT(!valueName.empty());
468             SelfCloseTagEnd();
469             m_LastTagAction = eTagClose;
470         }
471     }
472     else {
473         // local enum (member, variant or element)
474         if ( skipname ) {
475             _ASSERT(values.IsInteger());
476             m_Output.PutInt4(value);
477         }
478         else {
479             if (m_LastTagAction == eAttlistTag) {
480                 m_Output.PutString(valueName);
481             } else {
482                 OpenTagEndBack();
483                 m_Output.PutString(" value=\"");
484                 m_Output.PutString(valueName);
485                 m_Output.PutChar('"');
486                 if ( values.IsInteger() ) {
487                     OpenTagEnd();
488                     m_Output.PutInt4(value);
489                 }
490                 else {
491                     SelfCloseTagEnd();
492                 }
493             }
494         }
495     }
496 }
497 
WriteEnum(const CEnumeratedTypeValues & values,TEnumValueType value)498 void CObjectOStreamXml::WriteEnum(const CEnumeratedTypeValues& values,
499                                   TEnumValueType value)
500 {
501     WriteEnum(values, value, values.FindNameEx(value, values.IsInteger()));
502 }
503 
CopyEnum(const CEnumeratedTypeValues & values,CObjectIStream & in)504 void CObjectOStreamXml::CopyEnum(const CEnumeratedTypeValues& values,
505                                  CObjectIStream& in)
506 {
507     TEnumValueType value = in.ReadEnum(values);
508     WriteEnum(values, value, values.FindNameEx(value, values.IsInteger()));
509 }
510 
WriteEscapedChar(char c)511 void CObjectOStreamXml::WriteEscapedChar(char c)
512 {
513 //  http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char
514     switch ( c ) {
515     case '&':
516         m_Output.PutString("&amp;");
517         break;
518     case '<':
519         m_Output.PutString("&lt;");
520         break;
521     case '>':
522         m_Output.PutString("&gt;");
523         break;
524     case '\'':
525         m_Output.PutString("&apos;");
526         break;
527     case '"':
528         m_Output.PutString("&quot;");
529         break;
530     default:
531         if ((unsigned int)c < 0x20) {
532             m_Output.PutString("&#x");
533             Uint1 ch = c;
534             unsigned hi = ch >> 4;
535             unsigned lo = ch & 0xF;
536             if ( hi ) {
537                 m_Output.PutChar("0123456789abcdef"[hi]);
538             }
539             m_Output.PutChar("0123456789abcdef"[lo]);
540             m_Output.PutChar(';');
541         } else {
542             m_Output.PutChar(c);
543         }
544         break;
545     }
546 }
547 
548 /*
549 In XML 1.1, almost all chars are allowed:
550 http://www.w3.org/TR/xml11/#NT-Char
551 BUT, we declare this as xml 1.0:
552     CObjectOStreamXml::WriteFileHeader
553 Once so, some chars are not allowed
554 http://www.w3.org/TR/xml/#charsets
555 
556 */
BAD_CHAR(char x)557 inline bool BAD_CHAR(char x) {
558     return (x < 0x20 && x > 0x0 && x != 0x9 && x != 0xA && x != 0xD);
559 }
x_VerifyChar(char x)560 inline char CObjectOStreamXml::x_VerifyChar(char x) {
561     return BAD_CHAR(x) ?
562         ReplaceVisibleChar(x, x_FixCharsMethod(), this, kEmptyStr, x_FixCharsSubst()) : x;
563 }
564 
WriteEncodedChar(const char * & src,EStringType type)565 void CObjectOStreamXml::WriteEncodedChar(const char*& src, EStringType type)
566 {
567     EEncoding enc_in( type == eStringTypeUTF8 ? eEncoding_UTF8 : m_StringEncoding);
568     EEncoding enc_out(m_Encoding == eEncoding_Unknown ? eEncoding_UTF8 : m_Encoding);
569 
570     if (enc_in == enc_out || enc_in == eEncoding_Unknown || (*src & 0x80) == 0) {
571         char s = x_VerifyChar(*src);
572         if (s != '\0') {
573             WriteEscapedChar(s);
574         }
575     } else if (enc_out != eEncoding_UTF8) {
576         TUnicodeSymbol chU = (enc_in == eEncoding_UTF8) ?
577             CUtf8::Decode(src) : CUtf8::CharToSymbol( *src, enc_in);
578         char s = x_VerifyChar( CUtf8::SymbolToChar( chU, enc_out));
579         if (s != '\0') {
580             WriteEscapedChar(s);
581         }
582     } else {
583         CStringUTF8 tmp( CUtf8::AsUTF8( CTempString(src,1),enc_in));
584         for ( string::const_iterator t = tmp.begin(); t != tmp.end(); ++t ) {
585             char s = x_VerifyChar(*t);
586             if (s != '\0') {
587                 WriteEscapedChar(s);
588             }
589         }
590     }
591 }
592 
x_SpecialCaseWrite(void)593 bool CObjectOStreamXml::x_SpecialCaseWrite(void)
594 {
595     if (m_SpecialCaseWrite == eWriteAsDefault) {
596         OpenTagEndBack();
597         SelfCloseTagEnd();
598         return true;
599     }
600     else if (m_SpecialCaseWrite == eWriteAsNil) {
601         OpenTagEndBack();
602          m_Output.PutChar(' ');
603         if (GetReferenceSchema()) {
604             m_Output.PutString("xs:");
605         }
606         m_Output.PutString("nil=\"true\"");
607         SelfCloseTagEnd();
608         return true;
609     }
610     return false;
611 }
612 
WriteBool(bool data)613 void CObjectOStreamXml::WriteBool(bool data)
614 {
615     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
616         return;
617     }
618     if ( !x_IsStdXml() ) {
619         OpenTagEndBack();
620         if ( data )
621             m_Output.PutString(" value=\"true\"");
622         else
623             m_Output.PutString(" value=\"false\"");
624         SelfCloseTagEnd();
625     } else {
626         if ( data )
627             m_Output.PutString("true");
628         else
629             m_Output.PutString("false");
630     }
631 }
632 
WriteChar(char data)633 void CObjectOStreamXml::WriteChar(char data)
634 {
635     WriteEscapedChar(data);
636 }
637 
WriteInt4(Int4 data)638 void CObjectOStreamXml::WriteInt4(Int4 data)
639 {
640     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
641         return;
642     }
643     m_Output.PutInt4(data);
644 }
645 
WriteUint4(Uint4 data)646 void CObjectOStreamXml::WriteUint4(Uint4 data)
647 {
648     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
649         return;
650     }
651     m_Output.PutUint4(data);
652 }
653 
WriteInt8(Int8 data)654 void CObjectOStreamXml::WriteInt8(Int8 data)
655 {
656     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
657         return;
658     }
659     m_Output.PutInt8(data);
660 }
661 
WriteUint8(Uint8 data)662 void CObjectOStreamXml::WriteUint8(Uint8 data)
663 {
664     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
665         return;
666     }
667     m_Output.PutUint8(data);
668 }
669 
WriteDouble2(double data,unsigned digits)670 void CObjectOStreamXml::WriteDouble2(double data, unsigned digits)
671 {
672     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
673         return;
674     }
675     if (isnan(data)) {
676         m_Output.PutString("NaN", 3);
677         return;
678     }
679     if (!finite(data)) {
680         if (data < 0) {
681             m_Output.PutChar('-');
682         }
683         m_Output.PutString("INF", 3);
684         return;
685     }
686     char buffer[512];
687     SIZE_TYPE width;
688     if (m_RealFmt == eRealFixedFormat) {
689         int shift = int(ceil(log10(fabs(data))));
690         int precision = int(digits - shift);
691         if ( precision < 0 )
692             precision = 0;
693         if ( precision > 64 ) // limit precision of data
694             precision = 64;
695         width = NStr::DoubleToString(data, (unsigned int)precision,
696                                     buffer, sizeof(buffer), NStr::fDoublePosix);
697         if (precision != 0) {
698             while (buffer[width-1] == '0') {
699                 --width;
700             }
701             if (buffer[width-1] == '.') {
702                 --width;
703             }
704         }
705     } else {
706         if (m_FastWriteDouble) {
707             width = NStr::DoubleToStringPosix(data, digits, buffer, sizeof(buffer));
708         } else {
709             width = sprintf(buffer, "%.*g", (int)digits, data);
710             char* dot = strchr(buffer,',');
711             if (dot) {
712                 *dot = '.'; // enforce C locale
713             }
714         }
715     }
716     m_Output.PutString(buffer, width);
717 }
718 
WriteDouble(double data)719 void CObjectOStreamXml::WriteDouble(double data)
720 {
721     WriteDouble2(data, DBL_DIG);
722 }
723 
WriteFloat(float data)724 void CObjectOStreamXml::WriteFloat(float data)
725 {
726     WriteDouble2(data, FLT_DIG);
727 }
728 
WriteNull(void)729 void CObjectOStreamXml::WriteNull(void)
730 {
731     OpenTagEndBack();
732     SelfCloseTagEnd();
733 }
734 
WriteAnyContentObject(const CAnyContentObject & obj)735 void CObjectOStreamXml::WriteAnyContentObject(const CAnyContentObject& obj)
736 {
737     string ns_name(obj.GetNamespaceName());
738     bool needNs = x_BeginNamespace(ns_name,obj.GetNamespacePrefix());
739     string obj_name = obj.GetName();
740     if (obj_name.empty()) {
741         if (!StackIsEmpty() && TopFrame().HasMemberId()) {
742             obj_name = TopFrame().GetMemberId().GetName();
743         }
744     }
745     if (obj_name.empty()) {
746         ThrowError(fInvalidData, "AnyContent object must have name");
747     }
748     OpenTag(obj_name);
749 
750     if (GetReferenceSchema()) {
751         OpenTagEndBack();
752         if (needNs) {
753             m_Output.PutEol();
754             m_Output.PutString("    xmlns");
755             if (!m_CurrNsPrefix.empty()) {
756                 m_Output.PutChar(':');
757                 m_Output.PutString(m_CurrNsPrefix);
758             }
759             m_Output.PutString("=\"");
760             m_Output.PutString(ns_name);
761             m_Output.PutChar('\"');
762         }
763 
764         const vector<CSerialAttribInfoItem>& attlist = obj.GetAttributes();
765         if (!attlist.empty()) {
766             m_Attlist = true;
767             vector<CSerialAttribInfoItem>::const_iterator it;
768             for ( it = attlist.begin(); it != attlist.end(); ++it) {
769                 string ns(it->GetNamespaceName());
770                 string ns_prefix;
771                 if (x_BeginNamespace(ns,kEmptyStr)) {
772                     m_Output.PutEol();
773                     m_Output.PutString("    xmlns");
774                     ns_prefix = m_NsNameToPrefix[ns];
775                     if (!ns_prefix.empty()) {
776                         m_Output.PutChar(':');
777                         m_Output.PutString(ns_prefix);
778                     }
779                     m_Output.PutString("=\"");
780                     m_Output.PutString(ns);
781                     m_Output.PutChar('\"');
782                 }
783                 ns_prefix = m_NsNameToPrefix[ns];
784 
785                 m_Output.PutEol();
786                 m_Output.PutString("    ");
787                 if (!ns_prefix.empty()) {
788                     m_Output.PutString(ns_prefix);
789                     m_Output.PutChar(':');
790                 }
791                 m_Output.PutString(it->GetName());
792                 m_Output.PutString("=\"");
793                 WriteString(it->GetValue(),eStringTypeUTF8);
794                 m_Output.PutChar('\"');
795                 x_EndNamespace(ns);
796             }
797             m_Attlist = false;
798         }
799         OpenTagEnd();
800     }
801 
802 // value
803 // no verification on write!
804     const string& value = obj.GetValue();
805     if (value.empty()) {
806         OpenTagEndBack();
807         SelfCloseTagEnd();
808         m_LastTagAction = eTagClose;
809         x_EndNamespace(ns_name);
810         return;
811     }
812     bool was_open = true, was_close=true;
813     bool is_tag = false;
814     char attr_close ='\0';
815     for (const char* is = value.c_str(); *is; ++is) {
816         if (*is == '/' && *(is+1) == '>') {
817             m_Output.DecIndentLevel();
818             was_open = false;
819         }
820         if (*is == '<') {
821             if (*(is+1) == '/') {
822                 m_Output.DecIndentLevel();
823                 if (!was_open && was_close) {
824                     m_Output.PutEol();
825                 }
826                 is_tag = was_open = false;
827             } else {
828                 if (was_close) {
829                     m_Output.PutEol();
830                 }
831                 m_Output.IncIndentLevel();
832                 is_tag = was_open = true;
833             }
834         }
835         if (*is != '>' && *is != '<' && *is != attr_close) {
836             WriteEncodedChar(is,eStringTypeUTF8);
837         } else {
838             m_Output.PutChar(*is);
839         }
840         if (*is == '<') {
841             if (*(is+1) == '/') {
842                 m_Output.PutChar(*(++is));
843             }
844             if (GetReferenceSchema() && !m_CurrNsPrefix.empty()) {
845                 m_Output.PutString(m_CurrNsPrefix);
846                 m_Output.PutChar(':');
847             }
848         }
849         if (*is == '>') {
850             was_close = true;
851             is_tag = false;
852             attr_close = '\0';
853         } else {
854             was_close = false;
855         }
856         if (is_tag && *is == '=' && (*(is+1) == '\"' || *(is+1) == '\'')) {
857             attr_close = *(is+1);
858         }
859     }
860 
861 // close tag
862     if (!was_open) {
863         m_EndTag = true;
864     }
865     m_SkipIndent = !was_close;
866     CloseTag(obj_name);
867     x_EndNamespace(ns_name);
868 }
869 
CopyAnyContentObject(CObjectIStream & in)870 void CObjectOStreamXml::CopyAnyContentObject(CObjectIStream& in)
871 {
872     CAnyContentObject obj;
873     in.ReadAnyContentObject(obj);
874     WriteAnyContentObject(obj);
875 }
876 
WriteBitString(const CBitString & obj)877 void CObjectOStreamXml::WriteBitString(const CBitString& obj)
878 {
879 #if BITSTRING_AS_VECTOR
880     static const char ToHex[] = "0123456789ABCDEF";
881     Uint1 data, mask;
882     bool done = false;
883     for ( CBitString::const_iterator i = obj.begin(); !done; ) {
884         for (data=0, mask=0x8; !done && mask!=0; mask >>= 1) {
885             if (*i) {
886                 data |= mask;
887             }
888             done = (++i == obj.end());
889         }
890         m_Output.PutChar(ToHex[data]);
891     }
892 #else
893     if (IsCompressed()) {
894         bm::word_t* tmp_block = (bm::word_t*)bm::aligned_new_malloc(bm::set_block_alloc_size);
895         CBitString::statistics st;
896         obj.calc_stat(&st);
897         char* buf = (char*)malloc(st.max_serialize_mem);
898         size_t len = bm::serialize(obj, (unsigned char*)buf, tmp_block);
899         WriteBytes(buf,len);
900         free(buf);
901         bm::aligned_free(tmp_block);
902         return;
903     }
904     CBitString::size_type i=0;
905     CBitString::size_type ilast = obj.size();
906     CBitString::enumerator e = obj.first();
907     for (; i < ilast; ++i) {
908         m_Output.PutChar( (i == *e) ? '1' : '0');
909         if (i == *e) {
910             ++e;
911         }
912     }
913 #endif
914 }
915 
CopyBitString(CObjectIStream & in)916 void CObjectOStreamXml::CopyBitString(CObjectIStream& in)
917 {
918     CBitString obj;
919     in.ReadBitString(obj);
920     WriteBitString(obj);
921 }
922 
WriteCString(const char * str)923 void CObjectOStreamXml::WriteCString(const char* str)
924 {
925     if ( str == 0 ) {
926         OpenTagEndBack();
927         SelfCloseTagEnd();
928     }
929     else {
930         for ( ; *str; ++str) {
931             WriteEncodedChar(str);
932         }
933     }
934 }
935 
WriteString(const string & str,EStringType type)936 void CObjectOStreamXml::WriteString(const string& str, EStringType type)
937 {
938     if (m_SpecialCaseWrite && x_SpecialCaseWrite()) {
939         return;
940     }
941     for ( const char* src = str.c_str(); *src; ++src ) {
942         WriteEncodedChar(src,type);
943     }
944 }
945 
WriteStringStore(const string & str)946 void CObjectOStreamXml::WriteStringStore(const string& str)
947 {
948     for ( string::const_iterator i = str.begin(); i != str.end(); ++i ) {
949         WriteEscapedChar(*i);
950     }
951 }
952 
CopyString(CObjectIStream & in,EStringType type)953 void CObjectOStreamXml::CopyString(CObjectIStream& in,
954                                    EStringType type)
955 {
956     string str;
957     in.ReadString(str, type);
958     SetSpecialCaseWrite( (CObjectOStream::ESpecialCaseWrite)in.GetSpecialCaseUsed());
959     in.SetSpecialCaseUsed(CObjectIStream::eReadAsNormal);
960     WriteString(str, type);
961     SetSpecialCaseWrite(CObjectOStream::eWriteAsNormal);
962 }
963 
CopyStringStore(CObjectIStream & in)964 void CObjectOStreamXml::CopyStringStore(CObjectIStream& in)
965 {
966     string str;
967     in.ReadStringStore(str);
968     for ( string::const_iterator i = str.begin(); i != str.end(); ++i ) {
969         WriteEscapedChar(*i);
970     }
971 }
972 
WriteNullPointer(void)973 void CObjectOStreamXml::WriteNullPointer(void)
974 {
975     bool notag = TopFrame().HasMemberId() && TopFrame().GetMemberId().HasNotag();
976     bool nillable =  TopFrame().HasMemberId() && TopFrame().GetMemberId().IsNillable();
977     if (TopFrame().GetNotag() && !notag) {
978         if (m_LastTagAction == eTagClose) {
979             OpenStackTag(0);
980             m_SpecialCaseWrite = eWriteAsNil;
981             x_SpecialCaseWrite();
982             m_SpecialCaseWrite = eWriteAsNormal;
983             CloseStackTag(0);
984         }
985         return;
986     }
987     m_SpecialCaseWrite = nillable ? eWriteAsNil : eWriteAsDefault;
988     x_SpecialCaseWrite();
989     m_SpecialCaseWrite = eWriteAsNormal;
990 }
991 
WriteObjectReference(TObjectIndex index)992 void CObjectOStreamXml::WriteObjectReference(TObjectIndex index)
993 {
994     m_Output.PutString("<object index=");
995     if ( sizeof(TObjectIndex) == sizeof(Int4) )
996         m_Output.PutInt4(Int4(index));
997     else if ( sizeof(TObjectIndex) == sizeof(Int8) )
998         m_Output.PutInt8(index);
999     else
1000         ThrowError(fIllegalCall, "invalid size of TObjectIndex"
1001             "must be either sizeof(Int4) or sizeof(Int8)");
1002     m_Output.PutString("/>");
1003     m_EndTag = true;
1004 }
1005 
WriteTag(const string & name)1006 void CObjectOStreamXml::WriteTag(const string& name)
1007 {
1008     if (!m_CurrNsPrefix.empty() && IsNsQualified()) {
1009         m_Output.PutString(m_CurrNsPrefix);
1010         m_Output.PutChar(':');
1011     }
1012     m_Output.PutString(name);
1013 }
1014 
OpenTagStart(void)1015 void CObjectOStreamXml::OpenTagStart(void)
1016 {
1017     if (m_Attlist) {
1018         if ( m_LastTagAction == eTagOpen ) {
1019             m_Output.PutChar(' ');
1020             m_LastTagAction = eAttlistTag;
1021         }
1022     } else {
1023         if (m_SkipIndent) {
1024             m_SkipIndent = false;
1025         } else {
1026             m_Output.PutEol(false);
1027             m_Output.PutIndent();
1028         }
1029         m_Output.PutChar('<');
1030         m_LastTagAction = eTagOpen;
1031     }
1032     m_EndTag = false;
1033 }
1034 
OpenTagEnd(void)1035 void CObjectOStreamXml::OpenTagEnd(void)
1036 {
1037     if (m_Attlist) {
1038         if ( m_LastTagAction == eAttlistTag ) {
1039             m_Output.PutString("=\"");
1040         }
1041     } else {
1042         if ( m_LastTagAction == eTagOpen ) {
1043             m_Output.PutChar('>');
1044             m_Output.IncIndentLevel();
1045             m_LastTagAction = eTagClose;
1046         }
1047     }
1048 }
1049 
OpenTagEndBack(void)1050 void CObjectOStreamXml::OpenTagEndBack(void)
1051 {
1052     _ASSERT(m_LastTagAction == eTagClose);
1053     m_LastTagAction = eTagOpen;
1054     m_Output.BackChar('>');
1055     m_Output.DecIndentLevel();
1056 }
1057 
SelfCloseTagEnd(void)1058 void CObjectOStreamXml::SelfCloseTagEnd(void)
1059 {
1060     _ASSERT(m_LastTagAction == eTagOpen);
1061     m_Output.PutString("/>");
1062     m_LastTagAction = eTagSelfClosed;
1063     m_EndTag = true;
1064     m_SkipIndent = false;
1065 }
1066 
EolIfEmptyTag(void)1067 void CObjectOStreamXml::EolIfEmptyTag(void)
1068 {
1069     _ASSERT(m_LastTagAction != eTagSelfClosed);
1070     if ( m_LastTagAction == eTagOpen ) {
1071         m_LastTagAction = eTagClose;
1072     }
1073 }
1074 
CloseTagStart(void)1075 void CObjectOStreamXml::CloseTagStart(void)
1076 {
1077     _ASSERT(m_LastTagAction != eTagSelfClosed);
1078     m_Output.DecIndentLevel();
1079     if (m_EndTag && !m_SkipIndent) {
1080         m_Output.PutEol(false);
1081         m_Output.PutIndent();
1082     }
1083     m_Output.PutString("</");
1084     m_LastTagAction = eTagOpen;
1085 }
1086 
CloseTagEnd(void)1087 void CObjectOStreamXml::CloseTagEnd(void)
1088 {
1089     m_Output.PutChar('>');
1090     m_LastTagAction = eTagClose;
1091     m_EndTag = true;
1092     m_SkipIndent = false;
1093 }
1094 
PrintTagName(size_t level)1095 void CObjectOStreamXml::PrintTagName(size_t level)
1096 {
1097     const TFrame& frame = FetchFrameFromTop(level);
1098     switch ( frame.GetFrameType() ) {
1099     case TFrame::eFrameNamed:
1100     case TFrame::eFrameArray:
1101     case TFrame::eFrameClass:
1102     case TFrame::eFrameChoice:
1103         {
1104             _ASSERT(frame.GetTypeInfo());
1105             const string& name = frame.GetTypeInfo()->GetName();
1106             if ( !name.empty() ) {
1107                 WriteTag(name);
1108 #if defined(NCBI_SERIAL_IO_TRACE)
1109     TraceTag(name);
1110 #endif
1111             } else {
1112                 PrintTagName(level + 1);
1113             }
1114             return;
1115         }
1116     case TFrame::eFrameClassMember:
1117     case TFrame::eFrameChoiceVariant:
1118         {
1119             bool attlist = m_Attlist;
1120             if (!x_IsStdXml()) {
1121                 PrintTagName(level + 1);
1122                 m_Output.PutChar('_');
1123                 m_Attlist = true;
1124             }
1125             WriteTag(frame.GetMemberId().GetName());
1126             m_Attlist = attlist;
1127 #if defined(NCBI_SERIAL_IO_TRACE)
1128     TraceTag(frame.GetMemberId().GetName());
1129 #endif
1130             return;
1131         }
1132     case TFrame::eFrameArrayElement:
1133         {
1134             PrintTagName(level + 1);
1135             if (!x_IsStdXml()) {
1136                 m_Output.PutString("_E");
1137             }
1138             return;
1139         }
1140     default:
1141         break;
1142     }
1143     ThrowError(fIllegalCall, "illegal frame type");
1144 }
1145 
WriteOtherBegin(TTypeInfo typeInfo)1146 void CObjectOStreamXml::WriteOtherBegin(TTypeInfo typeInfo)
1147 {
1148     OpenTag(typeInfo);
1149 }
1150 
WriteOtherEnd(TTypeInfo typeInfo)1151 void CObjectOStreamXml::WriteOtherEnd(TTypeInfo typeInfo)
1152 {
1153     CloseTag(typeInfo);
1154 }
1155 
WriteOther(TConstObjectPtr object,TTypeInfo typeInfo)1156 void CObjectOStreamXml::WriteOther(TConstObjectPtr object,
1157                                    TTypeInfo typeInfo)
1158 {
1159     OpenTag(typeInfo);
1160     WriteObject(object, typeInfo);
1161     CloseTag(typeInfo);
1162 }
1163 
BeginContainer(const CContainerTypeInfo * containerType)1164 void CObjectOStreamXml::BeginContainer(const CContainerTypeInfo* containerType)
1165 {
1166     bool needNs = x_ProcessTypeNamespace(containerType);
1167     if (!m_StdXml) {
1168         if (TopFrame().GetFrameType() == CObjectStackFrame::eFrameArray &&
1169             FetchFrameFromTop(1).GetFrameType() == CObjectStackFrame::eFrameNamed) {
1170             const CClassTypeInfo* clType =
1171                 dynamic_cast<const CClassTypeInfo*>(FetchFrameFromTop(1).GetTypeInfo());
1172             if (clType && clType->Implicit()) {
1173                 TopFrame().SetNotag();
1174                 return;
1175             }
1176         }
1177         OpenTagIfNamed(containerType);
1178     }
1179     if (needNs) {
1180         x_WriteClassNamespace(containerType);
1181     }
1182 }
1183 
EndContainer(void)1184 void CObjectOStreamXml::EndContainer(void)
1185 {
1186     if (!m_StdXml && !TopFrame().GetNotag()) {
1187         CloseTagIfNamed(TopFrame().GetTypeInfo());
1188     }
1189     x_EndTypeNamespace();
1190 }
1191 
WillHaveName(TTypeInfo elementType)1192 bool CObjectOStreamXml::WillHaveName(TTypeInfo elementType)
1193 {
1194     while ( elementType->GetName().empty() ) {
1195         if ( elementType->GetTypeFamily() != eTypeFamilyPointer )
1196             return false;
1197         elementType = CTypeConverter<CPointerTypeInfo>::SafeCast(elementType)->GetPointedType();
1198     }
1199     // found named type
1200     return true;
1201 }
1202 
BeginContainerElement(TTypeInfo elementType)1203 void CObjectOStreamXml::BeginContainerElement(TTypeInfo elementType)
1204 {
1205     if ( !WillHaveName(elementType) ) {
1206         BeginArrayElement(elementType);
1207     }
1208 }
1209 
EndContainerElement(void)1210 void CObjectOStreamXml::EndContainerElement(void)
1211 {
1212     if ( !WillHaveName(TopFrame().GetTypeInfo()) ) {
1213         EndArrayElement();
1214     }
1215 }
1216 
1217 #ifdef VIRTUAL_MID_LEVEL_IO
WriteContainer(const CContainerTypeInfo * cType,TConstObjectPtr containerPtr)1218 void CObjectOStreamXml::WriteContainer(const CContainerTypeInfo* cType,
1219                                        TConstObjectPtr containerPtr)
1220 {
1221     if ( !cType->GetName().empty() ) {
1222         BEGIN_OBJECT_FRAME2(eFrameArray, cType);
1223         OpenTag(cType);
1224 
1225         WriteContainerContents(cType, containerPtr);
1226 
1227         EolIfEmptyTag();
1228         CloseTag(cType);
1229         END_OBJECT_FRAME();
1230     }
1231     else {
1232         WriteContainerContents(cType, containerPtr);
1233     }
1234 }
1235 #endif
BeginArrayElement(TTypeInfo elementType)1236 void CObjectOStreamXml::BeginArrayElement(TTypeInfo elementType)
1237 {
1238     if (x_IsStdXml()) {
1239         CObjectTypeInfo type(GetRealTypeInfo(elementType));
1240         if (type.GetTypeFamily() != eTypeFamilyPrimitive ||
1241             type.GetPrimitiveValueType() == ePrimitiveValueAny) {
1242             TopFrame().SetNotag();
1243             return;
1244         }
1245         if (m_SkipNextTag && type.GetTypeFamily() == eTypeFamilyPrimitive) {
1246             m_Output.PutChar(' ');
1247             TopFrame().SetNotag();
1248             return;
1249         }
1250     }
1251     OpenStackTag(0);
1252 }
1253 
EndArrayElement(void)1254 void CObjectOStreamXml::EndArrayElement(void)
1255 {
1256     if (TopFrame().GetNotag()) {
1257         TopFrame().SetNotag(false);
1258     } else {
1259         CloseStackTag(0);
1260     }
1261 }
1262 
WriteContainerContents(const CContainerTypeInfo * cType,TConstObjectPtr containerPtr)1263 void CObjectOStreamXml::WriteContainerContents(const CContainerTypeInfo* cType,
1264                                                TConstObjectPtr containerPtr)
1265 {
1266     TTypeInfo elementType = cType->GetElementType();
1267     CContainerTypeInfo::CConstIterator i;
1268     if ( WillHaveName(elementType) ) {
1269         if ( cType->InitIterator(i, containerPtr) ) {
1270 
1271             const CPointerTypeInfo* pointerType =
1272                 dynamic_cast<const CPointerTypeInfo*>(elementType);
1273             do {
1274                 TConstObjectPtr elementPtr = cType->GetElementPtr(i);
1275                 if ( pointerType &&
1276                      !pointerType->GetObjectPointer(elementPtr) ) {
1277                     if ( GetVerifyData() == eSerialVerifyData_Yes ) {
1278                         ThrowError(fUnassigned,
1279                                    "NULL element while writing container "+
1280                                    cType->GetName());
1281                     }
1282                     continue;
1283                 }
1284 
1285                 WriteObject(elementPtr, elementType);
1286 
1287             } while ( cType->NextElement(i) );
1288         }
1289     }
1290     else {
1291         BEGIN_OBJECT_FRAME2(eFrameArrayElement, elementType);
1292 
1293         if ( cType->InitIterator(i, containerPtr) ) {
1294 
1295             const CPointerTypeInfo* pointerType =
1296                 dynamic_cast<const CPointerTypeInfo*>(elementType);
1297             do {
1298                 TConstObjectPtr elementPtr = cType->GetElementPtr(i);
1299                 if ( pointerType &&
1300                      !pointerType->GetObjectPointer(elementPtr) ) {
1301                     if ( GetVerifyData() == eSerialVerifyData_Yes ) {
1302                         ThrowError(fUnassigned,
1303                                    "NULL element while writing container "+
1304                                    cType->GetName());
1305                     }
1306                     continue;
1307                 }
1308 
1309                 BeginArrayElement(elementType);
1310                 WriteObject(elementPtr, elementType);
1311                 EndArrayElement();
1312             } while ( cType->NextElement(i) );
1313         } else {
1314             const TFrame& frame = FetchFrameFromTop(1);
1315             if (frame.GetFrameType() == CObjectStackFrame::eFrameNamed) {
1316                 const CClassTypeInfo* clType =
1317                     dynamic_cast<const CClassTypeInfo*>(frame.GetTypeInfo());
1318                 if (clType && clType->Implicit() && clType->IsImplicitNonEmpty()) {
1319                     ThrowError(fInvalidData, "container is empty");
1320                 }
1321             }
1322         }
1323 
1324         END_OBJECT_FRAME();
1325     }
1326 }
1327 
BeginNamedType(TTypeInfo namedTypeInfo)1328 void CObjectOStreamXml::BeginNamedType(TTypeInfo namedTypeInfo)
1329 {
1330     CheckStdXml(namedTypeInfo);
1331     if (m_SkipNextTag || namedTypeInfo->GetName().empty()) {
1332         TopFrame().SetNotag();
1333         m_SkipNextTag = false;
1334     } else {
1335         TTypeInfo realtype = GetRealTypeInfo(namedTypeInfo);
1336         if (realtype->GetTypeFamily() == eTypeFamilyPrimitive &&
1337             GetStackDepth() > 2 && m_StdXml) {
1338             TopFrame().SetNotag();
1339             m_SkipNextTag = false;
1340             return;
1341         }
1342         bool needNs = x_ProcessTypeNamespace(namedTypeInfo);
1343         OpenTag(namedTypeInfo);
1344         if (needNs) {
1345             x_WriteClassNamespace(namedTypeInfo);
1346         }
1347     }
1348     const CAliasTypeInfo* aliasType =
1349         dynamic_cast<const CAliasTypeInfo*>(namedTypeInfo);
1350     if (aliasType) {
1351         m_SkipNextTag = aliasType->IsFullAlias();
1352     }
1353     else if (m_StdXml) {
1354         const CClassTypeInfo* classType = dynamic_cast<const CClassTypeInfo*>(namedTypeInfo);
1355         m_SkipNextTag = (classType && classType->Implicit());
1356     }
1357 }
1358 
EndNamedType(void)1359 void CObjectOStreamXml::EndNamedType(void)
1360 {
1361     m_SkipNextTag = false;
1362     if (TopFrame().GetNotag()) {
1363         TopFrame().SetNotag(false);
1364         return;
1365     }
1366     CloseTag(TopFrame().GetTypeInfo());
1367     x_EndTypeNamespace();
1368 }
1369 
1370 #ifdef VIRTUAL_MID_LEVEL_IO
1371 
WriteNamedType(TTypeInfo namedTypeInfo,TTypeInfo typeInfo,TConstObjectPtr object)1372 void CObjectOStreamXml::WriteNamedType(TTypeInfo namedTypeInfo,
1373                                        TTypeInfo typeInfo,
1374                                        TConstObjectPtr object)
1375 {
1376     BEGIN_OBJECT_FRAME2(eFrameNamed, namedTypeInfo);
1377 
1378     BeginNamedType(namedTypeInfo);
1379     WriteObject(object, typeInfo);
1380     EndNamedType();
1381 
1382     END_OBJECT_FRAME();
1383 }
1384 
CopyNamedType(TTypeInfo namedTypeInfo,TTypeInfo objectType,CObjectStreamCopier & copier)1385 void CObjectOStreamXml::CopyNamedType(TTypeInfo namedTypeInfo,
1386                                       TTypeInfo objectType,
1387                                       CObjectStreamCopier& copier)
1388 {
1389     BEGIN_OBJECT_2FRAMES_OF2(copier, eFrameNamed, namedTypeInfo);
1390     copier.In().BeginNamedType(namedTypeInfo);
1391     BeginNamedType(namedTypeInfo);
1392     CopyObject(objectType, copier);
1393     EndNamedType();
1394     copier.In().EndNamedType();
1395     END_OBJECT_2FRAMES_OF(copier);
1396 }
1397 #endif
1398 
CheckStdXml(TTypeInfo typeinfo)1399 void CObjectOStreamXml::CheckStdXml(TTypeInfo typeinfo)
1400 {
1401     if (typeinfo->GetCodeVersion() > 21600) {
1402         m_StdXml = typeinfo->GetDataSpec() != EDataSpec::eASN;
1403     } else {
1404         const CClassTypeInfo* classType =
1405             dynamic_cast<const CClassTypeInfo*>(typeinfo);
1406         if (classType) {
1407             TMemberIndex first = classType->GetItems().FirstIndex();
1408             m_StdXml = classType->GetItems().GetItemInfo(first)->GetId().HaveNoPrefix();
1409         }
1410     }
1411 }
1412 
BeginClass(const CClassTypeInfo * classInfo)1413 void CObjectOStreamXml::BeginClass(const CClassTypeInfo* classInfo)
1414 {
1415     CheckStdXml(classInfo);
1416     if (m_SkipNextTag) {
1417         TopFrame().SetNotag();
1418         m_SkipNextTag = false;
1419         return;
1420     }
1421     bool needNs = x_ProcessTypeNamespace(classInfo);
1422     OpenTagIfNamed(classInfo);
1423     if (needNs) {
1424         x_WriteClassNamespace(classInfo);
1425     }
1426 }
1427 
EndClass(void)1428 void CObjectOStreamXml::EndClass(void)
1429 {
1430     if (TopFrame().GetNotag()) {
1431         TopFrame().SetNotag(false);
1432         return;
1433     }
1434     if (!m_Attlist && m_LastTagAction != eTagSelfClosed) {
1435         EolIfEmptyTag();
1436     }
1437     if (m_LastTagAction == eTagSelfClosed) {
1438         m_LastTagAction = eTagClose;
1439     } else {
1440         CloseTagIfNamed(TopFrame().GetTypeInfo());
1441     }
1442     x_EndTypeNamespace();
1443 }
1444 
BeginClassMember(const CMemberId & id)1445 void CObjectOStreamXml::BeginClassMember(const CMemberId& id)
1446 {
1447     const CClassTypeInfoBase* classType = dynamic_cast<const CClassTypeInfoBase*>
1448         (FetchFrameFromTop(1).GetTypeInfo());
1449     _ASSERT(classType);
1450     BeginClassMember(classType->GetItemInfo(id.GetName())->GetTypeInfo(),id);
1451 }
1452 
BeginClassMember(TTypeInfo memberType,const CMemberId & id)1453 void CObjectOStreamXml::BeginClassMember(TTypeInfo memberType,
1454                                          const CMemberId& id)
1455 {
1456     if (x_IsStdXml()) {
1457         if(id.IsAttlist()) {
1458             if(m_LastTagAction == eTagClose) {
1459                 OpenTagEndBack();
1460             }
1461             m_Attlist = true;
1462             TopFrame().SetNotag();
1463         } else {
1464             ETypeFamily type = GetRealTypeFamily(memberType);
1465             bool needTag = true;
1466             if (GetEnforcedStdXml()) {
1467                 if (type == eTypeFamilyContainer) {
1468                     TTypeInfo mem_type  = GetRealTypeInfo(memberType);
1469                     TTypeInfo elem_type = GetContainerElementTypeInfo(mem_type);
1470                     needTag = (elem_type->GetTypeFamily() != eTypeFamilyPrimitive ||
1471                         elem_type->GetName() != mem_type->GetName());
1472                 }
1473             } else {
1474                 needTag = !id.HasNotag() && !id.HasAnyContent() && type != eTypeFamilyContainer;
1475                 m_SkipNextTag = type != eTypeFamilyPrimitive && type != eTypeFamilyContainer;
1476             }
1477             if (needTag) {
1478                 OpenStackTag(0);
1479             } else {
1480                 TopFrame().SetNotag();
1481             }
1482             if (type == eTypeFamilyPrimitive) {
1483                 m_SkipIndent = id.HasNotag();
1484             }
1485         }
1486     } else {
1487         OpenStackTag(0);
1488     }
1489 }
1490 
EndClassMember(void)1491 void CObjectOStreamXml::EndClassMember(void)
1492 {
1493     m_SkipNextTag = false;
1494     if (TopFrame().GetNotag()) {
1495         TopFrame().SetNotag(false);
1496         m_Attlist = false;
1497         if(m_LastTagAction == eTagOpen) {
1498             OpenTagEnd();
1499         }
1500     } else {
1501         CloseStackTag(0);
1502     }
1503 }
1504 
1505 
1506 #ifdef VIRTUAL_MID_LEVEL_IO
1507 
WriteClass(const CClassTypeInfo * classType,TConstObjectPtr classPtr)1508 void CObjectOStreamXml::WriteClass(const CClassTypeInfo* classType,
1509                                    TConstObjectPtr classPtr)
1510 {
1511     if ( (m_Attlist && classType->IsNsQualified() == eNSQualified) || !classType->GetName().empty() ) {
1512         BEGIN_OBJECT_FRAME2(eFrameClass, classType);
1513 
1514         BeginClass(classType);
1515         for ( CClassTypeInfo::CIterator i(classType); i.Valid(); ++i ) {
1516             classType->GetMemberInfo(i)->WriteMember(*this, classPtr);
1517         }
1518         EndClass();
1519 
1520         END_OBJECT_FRAME();
1521     }
1522     else {
1523         for ( CClassTypeInfo::CIterator i(classType); i.Valid(); ++i ) {
1524             classType->GetMemberInfo(i)->WriteMember(*this, classPtr);
1525         }
1526     }
1527 }
1528 
WriteClassMember(const CMemberId & memberId,TTypeInfo memberType,TConstObjectPtr memberPtr)1529 void CObjectOStreamXml::WriteClassMember(const CMemberId& memberId,
1530                                          TTypeInfo memberType,
1531                                          TConstObjectPtr memberPtr)
1532 {
1533     BEGIN_OBJECT_FRAME2(eFrameClassMember,memberId);
1534 
1535     BeginClassMember(memberType,memberId);
1536     WriteObject(memberPtr, memberType);
1537     EndClassMember();
1538 
1539     END_OBJECT_FRAME();
1540 }
1541 
WriteClassMember(const CMemberId & memberId,const CDelayBuffer & buffer)1542 bool CObjectOStreamXml::WriteClassMember(const CMemberId& memberId,
1543                                          const CDelayBuffer& buffer)
1544 {
1545     if ( !buffer.HaveFormat(eSerial_Xml) )
1546         return false;
1547 
1548     BEGIN_OBJECT_FRAME2(eFrameClassMember, memberId);
1549     OpenStackTag(0);
1550 
1551     Write(buffer.GetSource());
1552 
1553     CloseStackTag(0);
1554     END_OBJECT_FRAME();
1555 
1556     return true;
1557 }
1558 
WriteClassMemberSpecialCase(const CMemberId & memberId,TTypeInfo,TConstObjectPtr,ESpecialCaseWrite how)1559 void CObjectOStreamXml::WriteClassMemberSpecialCase(const CMemberId& memberId,
1560     TTypeInfo /*memberType*/, TConstObjectPtr /*memberPtr*/, ESpecialCaseWrite how)
1561 {
1562     if (m_Attlist) {
1563         return;
1564     }
1565     m_SpecialCaseWrite = how;
1566     if (memberId.HasNotag() || m_SkipNextTag) {
1567         x_SpecialCaseWrite();
1568     } else {
1569         BEGIN_OBJECT_FRAME2(eFrameClassMember, memberId);
1570         OpenStackTag(0);
1571         x_SpecialCaseWrite();
1572         m_SpecialCaseWrite = eWriteAsNormal;
1573         CloseStackTag(0);
1574         END_OBJECT_FRAME();
1575     }
1576     m_SpecialCaseWrite = eWriteAsNormal;
1577 }
1578 #endif
1579 
BeginChoice(const CChoiceTypeInfo * choiceType)1580 void CObjectOStreamXml::BeginChoice(const CChoiceTypeInfo* choiceType)
1581 {
1582     CheckStdXml(choiceType);
1583     if (m_SkipNextTag) {
1584         TopFrame().SetNotag();
1585         m_SkipNextTag = false;
1586         return;
1587     }
1588     bool needNs = x_ProcessTypeNamespace(choiceType);
1589     OpenTagIfNamed(choiceType);
1590     if (needNs) {
1591         x_WriteClassNamespace(choiceType);
1592     }
1593 }
1594 
EndChoice(void)1595 void CObjectOStreamXml::EndChoice(void)
1596 {
1597     if (TopFrame().GetNotag()) {
1598         TopFrame().SetNotag(false);
1599         return;
1600     }
1601     CloseTagIfNamed(TopFrame().GetTypeInfo());
1602     x_EndTypeNamespace();
1603 }
1604 
BeginChoiceVariant(const CChoiceTypeInfo * choiceType,const CMemberId & id)1605 void CObjectOStreamXml::BeginChoiceVariant(const CChoiceTypeInfo* choiceType,
1606                                            const CMemberId& id)
1607 {
1608     if (x_IsStdXml()) {
1609         const CVariantInfo* var_info = choiceType->GetVariantInfo(id.GetName());
1610         ETypeFamily type = GetRealTypeFamily(var_info->GetTypeInfo());
1611         bool needTag = true;
1612         if (GetEnforcedStdXml()) {
1613             if (type == eTypeFamilyContainer) {
1614                 TTypeInfo var_type  = GetRealTypeInfo(var_info->GetTypeInfo());
1615                 TTypeInfo elem_type = GetContainerElementTypeInfo(var_type);
1616                 needTag = (elem_type->GetTypeFamily() != eTypeFamilyPrimitive ||
1617                     elem_type->GetName() != var_type->GetName());
1618             }
1619         } else {
1620             needTag = !id.HasNotag() && !id.HasAnyContent() && type != eTypeFamilyContainer;
1621             m_SkipNextTag = type != eTypeFamilyPrimitive && type != eTypeFamilyContainer;
1622         }
1623         if (needTag) {
1624             OpenStackTag(0);
1625         } else {
1626             TopFrame().SetNotag();
1627         }
1628         if (type == eTypeFamilyPrimitive) {
1629             m_SkipIndent = id.HasNotag();
1630         }
1631     } else {
1632         OpenStackTag(0);
1633     }
1634 }
1635 
EndChoiceVariant(void)1636 void CObjectOStreamXml::EndChoiceVariant(void)
1637 {
1638     m_SkipNextTag = false;
1639     if (TopFrame().GetNotag()) {
1640         TopFrame().SetNotag(false);
1641     } else {
1642         CloseStackTag(0);
1643     }
1644 }
1645 
WriteChoiceContents(const CChoiceTypeInfo * choiceType,TConstObjectPtr choicePtr)1646 void CObjectOStreamXml::WriteChoiceContents(const CChoiceTypeInfo* choiceType,
1647                                             TConstObjectPtr choicePtr)
1648 {
1649     TMemberIndex index = choiceType->GetIndex(choicePtr);
1650     const CVariantInfo* variantInfo = choiceType->GetVariantInfo(index);
1651     BEGIN_OBJECT_FRAME2(eFrameChoiceVariant, variantInfo->GetId());
1652     OpenStackTag(0);
1653 
1654     variantInfo->WriteVariant(*this, choicePtr);
1655 
1656     CloseStackTag(0);
1657     END_OBJECT_FRAME();
1658 }
1659 
1660 static const char* const HEX = "0123456789ABCDEF";
1661 
WriteBytes(const ByteBlock &,const char * bytes,size_t length)1662 void CObjectOStreamXml::WriteBytes(const ByteBlock& ,
1663                                    const char* bytes, size_t length)
1664 {
1665     if (IsCompressed()) {
1666         WriteBase64Bytes(bytes,length);
1667         return;
1668     }
1669     WriteBytes(bytes,length);
1670 }
1671 
WriteBase64Bytes(const char * bytes,size_t length)1672 void CObjectOStreamXml::WriteBase64Bytes(const char* bytes, size_t length)
1673 {
1674     const size_t chunk_in  = 57;
1675     const size_t chunk_out = 80;
1676     if (length > chunk_in) {
1677         m_Output.PutEol(false);
1678     }
1679     char dst_buf[chunk_out];
1680     size_t bytes_left = length;
1681     size_t  src_read=0, dst_written=0, line_len=0;
1682     while (bytes_left > 0 && bytes_left <= length) {
1683         BASE64_Encode(bytes,  min(bytes_left,chunk_in),  &src_read,
1684                         dst_buf, chunk_out, &dst_written, &line_len);
1685         m_Output.PutString(dst_buf,dst_written);
1686         bytes_left -= src_read;
1687         bytes += src_read;
1688         if (bytes_left > 0) {
1689             m_Output.PutEol(false);
1690         }
1691     }
1692     if (length > chunk_in) {
1693         m_Output.PutEol(false);
1694     }
1695 }
1696 
WriteBytes(const char * bytes,size_t length)1697 void CObjectOStreamXml::WriteBytes(const char* bytes, size_t length)
1698 {
1699     while ( length-- > 0 ) {
1700         char c = *bytes++;
1701         m_Output.PutChar(HEX[(c >> 4) & 0xf]);
1702         m_Output.PutChar(HEX[c & 0xf]);
1703     }
1704 }
1705 
WriteChars(const CharBlock &,const char * chars,size_t length)1706 void CObjectOStreamXml::WriteChars(const CharBlock& ,
1707                                    const char* chars, size_t length)
1708 {
1709     while ( length-- > 0 ) {
1710         char c = *chars++;
1711         WriteEscapedChar(c);
1712     }
1713 }
1714 
1715 
WriteSeparator(void)1716 void CObjectOStreamXml::WriteSeparator(void)
1717 {
1718     m_Output.PutString(GetSeparator());
1719     FlushBuffer();
1720 }
1721 
1722 #if defined(NCBI_SERIAL_IO_TRACE)
1723 
TraceTag(const string & name)1724 void CObjectOStreamXml::TraceTag(const string& name)
1725 {
1726     cout << ", Tag=" << name;
1727 }
1728 
1729 #endif
1730 
1731 END_NCBI_SCOPE
1732