1 /*  $Id: blocktype.cpp 569649 2018-08-27 14:47:05Z gouriano $
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 *   Type description for compound types: SET, SEQUENCE and CHOICE
30 */
31 
32 #include <ncbi_pch.hpp>
33 #include "exceptions.hpp"
34 #include "blocktype.hpp"
35 #include "unitype.hpp"
36 #include "reftype.hpp"
37 #include "statictype.hpp"
38 #include <serial/impl/autoptrinfo.hpp>
39 #include "value.hpp"
40 #include "classstr.hpp"
41 #include "module.hpp"
42 #include "srcutil.hpp"
43 #include <serial/impl/classinfo.hpp>
44 #include <typeinfo>
45 #include "aliasstr.hpp"
46 
47 BEGIN_NCBI_SCOPE
48 
49 class CAnyTypeClassInfo : public CClassTypeInfo
50 {
51 public:
CAnyTypeClassInfo(const string & name,size_t count)52     CAnyTypeClassInfo(const string& name, size_t count)
53         : CClassTypeInfo(sizeof(AnyType) * count, name,
54                          TObjectPtr(0), &CreateAnyTypeClass,
55                          typeid(bool), &GetAnyTypeClassId)
56         {
57         }
58 
GetAnyTypePtr(size_t index) const59     const AnyType* GetAnyTypePtr(size_t index) const
60         {
61             return &static_cast<AnyType*>(0)[index];
62         }
GetSetFlagPtr(size_t index)63     const bool* GetSetFlagPtr(size_t index)
64         {
65             return &GetAnyTypePtr(index)->booleanValue;
66         }
67 
68 protected:
CreateAnyTypeClass(TTypeInfo objectType,CObjectMemoryPool *)69     static TObjectPtr CreateAnyTypeClass(TTypeInfo objectType,
70                                          CObjectMemoryPool* /*memoryPool*/)
71         {
72             size_t size = objectType->GetSize();
73             TObjectPtr obj = new char[size];
74             memset(obj, 0, size);
75             return obj;
76         }
GetAnyTypeClassId(TConstObjectPtr)77     static const type_info* GetAnyTypeClassId(TConstObjectPtr /*objectPtr*/)
78         {
79             return 0;
80         }
81 };
82 
AddMember(const AutoPtr<CDataMember> & member)83 void CDataMemberContainerType::AddMember(const AutoPtr<CDataMember>& member)
84 {
85     m_Members.push_back(member);
86 }
87 
PrintASN(CNcbiOstream & out,int indent) const88 void CDataMemberContainerType::PrintASN(CNcbiOstream& out, int indent) const
89 {
90     PrintASNTag(out);
91     out << GetASNKeyword() << " {";
92     ++indent;
93     ITERATE ( TMembers, i, m_Members ) {
94         PrintASNNewLine(out, indent);
95         const CDataMember& member = **i;
96         TMembers::const_iterator next = i;
97         bool last = ++next == m_Members.end();
98         member.PrintASN(out, indent, last);
99     }
100     --indent;
101     PrintASNNewLine(out, indent);
102     m_LastComments.PrintASN(out, indent, CComments::eMultiline);
103     out << "}";
104 }
105 
PrintSpecDumpExtra(CNcbiOstream & out,int indent) const106 void CDataMemberContainerType::PrintSpecDumpExtra(CNcbiOstream& out, int indent) const
107 {
108     bool isAttlist = GetDataMember() && GetDataMember()->Attlist();
109     if (!GetParentType() || !isAttlist) {
110         ++indent;
111     }
112     ITERATE ( TMembers, i, m_Members ) {
113         i->get()->PrintSpecDump(out, indent, isAttlist ? "A" : "F");
114     }
115     m_LastComments.PrintASN(out, indent, CComments::eNoEOL);
116 }
117 
PrintJSONSchema(CNcbiOstream & out,int indent,list<string> & required,bool contents_only) const118 void CDataMemberContainerType::PrintJSONSchema(CNcbiOstream& out, int indent, list<string>& required, bool contents_only) const
119 {
120     const CDataMember* data = GetDataMember();
121     bool hasNotag = data && data->Notag();
122     list<string> this_req;
123     list<string>& req = (hasNotag || contents_only) ? required : this_req;
124     bool hasAttlist = find_if(m_Members.begin(), m_Members.end(),
125         [](const TMembers::value_type& e) { return e->Attlist();}) != m_Members.end();
126     bool first = true;
127 
128     if (!contents_only && m_Members.size()==1 &&
129             (m_Members.front()->GetType()->IsContainer() || m_Members.front()->GetType()->IsUniSeq())) {
130         m_Members.front()->PrintJSONSchema(out, indent, required);
131         return;
132     }
133 
134 #if 0
135 // allOf is kind of... wrong...
136 // it is impossible to restrict additionalProperties
137 // so, instance validation either fails or allows undefined elements
138 // I do not know...
139 // I leave this code for future, in a hope that JSON schema will be improved.
140 
141     bool hasUnnamedContainer = find_if(m_Members.begin(), m_Members.end(),
142         [](const TMembers::value_type& e) { return e->Notag() && e->GetType()->IsContainer();}) != m_Members.end();
143     if (hasUnnamedContainer) {
144         first = true;
145         PrintASNNewLine(out, indent++) << "\"definitions\": {";
146         ITERATE ( TMembers, i, m_Members ) {
147             const CDataMember& member = **i;
148             if (member.Notag() && member.GetType()->IsContainer()) {
149                 if (!first) {
150                     out << ",";
151                 } else {
152                     first = false;
153                 }
154                 PrintASNNewLine(out, indent++) << "\"" << "$" << member.GetName() << "\": {";
155                 member.PrintJSONSchema(out, indent, req, true);
156                 PrintASNNewLine(out, --indent) << "}";
157             }
158         }
159         PrintASNNewLine(out, --indent) << "},";
160         PrintASNNewLine(out, indent++) << "\"allOf\": [";
161         first = true;
162         ITERATE ( TMembers, i, m_Members ) {
163             const CDataMember& member = **i;
164             if (member.Notag() && member.GetType()->IsContainer()) {
165                 if (!first) {
166                     out << ",";
167                 } else {
168                     first = false;
169                 }
170                 PrintASNNewLine(out, indent) << "{\"$ref\": \"#/definitions/" << GetMemberName() << "/definitions/$" << member.GetName() << "\"}";
171             }
172         }
173         out << ",";
174         PrintASNNewLine(out, indent++) << "{";
175     }
176 #endif
177 
178     if (!contents_only) {
179         PrintASNNewLine(out, indent) << "\"type\": \"object\",";
180         PrintASNNewLine(out, indent++) << "\"properties\": {";
181     }
182 
183     first = true;
184     ITERATE ( TMembers, i, m_Members ) {
185         const CDataMember& member = **i;
186         if (hasAttlist && member.Notag() && dynamic_cast<const CNullDataType*>(member.GetType()) != nullptr) {
187 // special case: null with attributes
188             continue;
189         }
190 #if 0
191         if (member.Notag() && member.GetType()->IsContainer()) {
192             continue;
193         }
194 #endif
195         if (!first) {
196             out << ",";
197         } else {
198             first = false;
199         }
200         if (member.Attlist()) {
201             member.PrintJSONSchema(out, indent, req, true);
202         }
203         else if (member.Notag()) {
204             if (member.GetType()->IsContainer()) {
205                 member.PrintJSONSchema(out, indent, req, true);
206             } else {
207                 PrintASNNewLine(out, indent++) << "\"" << "#" << member.GetName() << "\": {";
208                 member.PrintJSONSchema(out, indent, req);
209                 PrintASNNewLine(out, --indent) << "}";
210             }
211         }
212         else { //if (!member.Notag() && !member.Attlist()) {
213             PrintASNNewLine(out, indent++) << "\"" << member.GetName() << "\": {";
214             member.PrintJSONSchema(out, indent, req);
215             PrintASNNewLine(out, --indent) << "}";
216         }
217     }
218 
219     if (!contents_only) {
220         PrintASNNewLine(out, --indent) << "}";
221         if (!req.empty()) {
222             out << ',';
223             PrintASNNewLine(out, indent) << "\"required\": [";
224             transform(req.begin(), req.end(), Dt_ostream_iterator<string>(out, ", "),
225                 [](const string& e) {
226                     return string("\"").append(e).append("\"");
227                 });
228             req.clear();
229             out << "]";
230         }
231         out << ",";
232         PrintASNNewLine(out, indent) << "\"additionalProperties\": false";
233     }
234 
235 #if 0
236     if (hasUnnamedContainer) {
237         PrintASNNewLine(out, --indent) << "}";
238         PrintASNNewLine(out, --indent) << "]";
239     }
240 #endif
241     if (data && !data->Notag() && !data->Optional() && !data->Attlist()) {
242         required.push_back(data->GetName());
243     }
244 }
245 
246 // XML schema generator submitted by
247 // Marc Dumontier, Blueprint initiative, dumontier@mshri.on.ca
248 // modified by Andrei Gourianov, gouriano@ncbi
PrintXMLSchema(CNcbiOstream & out,int indent,bool contents_only) const249 void CDataMemberContainerType::PrintXMLSchema(CNcbiOstream& out,
250     int indent, bool contents_only) const
251 {
252     string tag = XmlTagName();
253     string asnk = GetASNKeyword();
254     string xsdk, tmp;
255     bool hasAttlist= false, isAttlist= false;
256     bool hasNotag= false, isOptionalMember= false, isOptionalContent= false;
257     bool isSimple= false, isSimpleSeq= false, isSeq= false, isMixed=false;
258     bool isSimpleContainer= false, parent_isSeq= false;
259     bool defineAsType = false;
260     bool isGlobalType  = GetGlobalType() == CDataType::eType;
261     bool isGlobalGroup = GetGlobalType() == CDataType::eGroup;
262     string simpleType;
263     list<string> opentag, closetag1, closetag2;
264     CNcbiOstream* os = &out;
265     CNcbiOstrstream otype;
266 
267     parent_isSeq = (dynamic_cast<const CUniSequenceDataType*>(GetParentType()) != 0);
268     if (GetEnforcedStdXml()) {
269         defineAsType = GetParentType() && IsReferenced() &&
270             GetReferences().front()->IsRefToParent();
271 
272         ITERATE ( TMembers, i, m_Members ) {
273             if (i->get()->Attlist()) {
274                 hasAttlist = true;
275                 break;
276             }
277         }
278         if (( hasAttlist && GetMembers().size() > 2) ||
279             (!hasAttlist && GetMembers().size() > 1)) {
280             ITERATE ( TMembers, i, m_Members ) {
281                 if (i->get()->Notag()) {
282                     const CStringDataType* str =
283                         dynamic_cast<const CStringDataType*>(i->get()->GetType());
284                     if (str != 0) {
285                         isMixed = true;
286                         break;
287                     }
288                 }
289             }
290         }
291         if (GetDataMember()) {
292             isAttlist = GetDataMember()->Attlist();
293             hasNotag   = GetDataMember()->Notag();
294             isOptionalMember= GetDataMember()->Optional();
295         }
296         if (hasNotag && GetMembers().size()==1) {
297             const CDataMember* member = GetMembers().front().get();
298             isOptionalMember = member->Optional();
299             const CUniSequenceDataType* typeSeq =
300                 dynamic_cast<const CUniSequenceDataType*>(member->GetType());
301             isSeq = (typeSeq != 0);
302             if (isSeq) {
303                 const CDataMemberContainerType* data =
304                     dynamic_cast<const CDataMemberContainerType*>(typeSeq->GetElementType());
305                 if (data) {
306                     asnk = data->GetASNKeyword();
307                 }
308             }
309         }
310         if ((hasAttlist && GetMembers().size()==2) ||
311             (!hasAttlist && GetMembers().size()==1)) {
312             ITERATE ( TMembers, i, GetMembers() ) {
313                 if (i->get()->Attlist()) {
314                     continue;
315                 }
316                 if (i->get()->SimpleType()) {
317                     isSimple = true;
318                     simpleType = i->get()->GetType()->GetSchemaTypeString();
319                 } else {
320                     const CUniSequenceDataType* typeSeq =
321                         dynamic_cast<const CUniSequenceDataType*>(i->get()->GetType());
322                     bool any = (typeSeq != 0) &&
323                         dynamic_cast<const CAnyContentDataType*>(
324                             typeSeq->GetElementType()) != 0;
325                     const CDataMemberContainerType* data =
326                         dynamic_cast<const CDataMemberContainerType*>(i->get()->GetType());
327                     isSimpleSeq = !any && (typeSeq != 0 || data != 0);
328                     isSimpleContainer = data != 0;
329                     if (isSimpleSeq) {
330                         isSeq = typeSeq != 0;
331                         const CDataMember *mem = i->get()->GetType()->GetDataMember();
332                         if (mem) {
333                             if (typeSeq) {
334                                 data = dynamic_cast<const CDataMemberContainerType*>(typeSeq->GetElementType());
335                             }
336                             if (data) {
337                                 asnk = data->GetASNKeyword();
338                                 ITERATE ( TMembers, m, data->m_Members ) {
339                                     if (m->get()->Notag()) {
340                                         const CStringDataType* str =
341                                             dynamic_cast<const CStringDataType*>(m->get()->GetType());
342                                         if (str != 0) {
343                                             isMixed = true;
344                                             break;
345                                         }
346                                     }
347                                 }
348                             }
349                             if (mem->Notag()) {
350                                 isOptionalContent = mem->Optional();
351                             } else {
352                                 isSeq = isSimpleSeq = false;
353                             }
354                         }
355                     } else {
356                         if (i->get()->Notag()) {
357                             const CStringDataType* str =
358                                 dynamic_cast<const CStringDataType*>(i->get()->GetType());
359                             if (str != 0) {
360                                 isMixed = true;
361                             }
362                         }
363                     }
364                 }
365             }
366         }
367     } else {
368         if (GetDataMember()) {
369             isOptionalMember= GetDataMember()->Optional();
370         }
371     }
372 
373 
374     if (!isAttlist && !parent_isSeq) {
375         if (!hasNotag) {
376             if (!contents_only) {
377                 string tname;
378                 if (isGlobalType || isGlobalGroup) {
379                     tname = tag;
380                 } else {
381                     tmp = "<xs:element name=\"" + tag + "\"";
382                     if (isOptionalMember) {
383                         tmp += " minOccurs=\"0\"";
384                     }
385                     if (IsNillable()) {
386                         tmp += " nillable=\"true\"";
387                     }
388                     if (defineAsType) {
389                         const CDataType* par = GetParentType();
390                         while ( par->GetParentType() ) {
391                             par = par->GetParentType();
392                         }
393                         tname = par->GetMemberName() + tag + "_Type";
394                         tmp += " type=\"" + tname + "\"/>";
395                         PrintASNNewLine(out, indent) << tmp;
396                     } else {
397 #if _DATATOOL_USE_SCHEMA_STYLE_COMMENTS
398                         PrintASNNewLine(out, indent) << tmp;
399                         if (!Comments().PrintSchemaComments(out, indent+1)) {
400                             out << '>';
401                         }
402                         ++indent;
403 #else
404                         tmp += ">";
405                         opentag.push_back(tmp);
406 #endif
407                         closetag2.push_front("</xs:element>");
408                     }
409                 }
410 
411                 tmp = isGlobalGroup ? "<xs:group" : "<xs:complexType";
412                 if (isMixed) {
413                     tmp += " mixed=\"true\"";
414                 }
415                 if (defineAsType || isGlobalType || isGlobalGroup) {
416                     tmp += " name=\"" + tname + "\"";
417                     if (!isGlobalType && !isGlobalGroup) {
418                         os = &otype;
419                     }
420                     indent = 0;
421                 }
422                 opentag.push_back(tmp + ">");
423                 closetag2.push_front(isGlobalGroup ? "</xs:group" : "</xs:complexType>");
424             }
425         }
426         if (!isGlobalGroup) {
427             if (!isSimple && !isSimpleContainer) {
428                 if (!contents_only || hasNotag) {
429                     if(NStr::CompareCase(asnk,"CHOICE")==0) {
430                         xsdk = "choice";
431                     } else if(NStr::CompareCase(asnk,"SEQUENCE")==0) {
432                         xsdk = "sequence";
433                     } else if(NStr::CompareCase(asnk,"SET")==0) {
434                         xsdk = "all";
435                     }
436                     tmp = "<xs:" + xsdk;
437                     if (isOptionalContent || (hasNotag && isOptionalMember)) {
438                         tmp += " minOccurs=\"0\"";
439                     }
440                     if (isSeq) {
441                         tmp += " maxOccurs=\"unbounded\"";
442                     }
443                     opentag.push_back(tmp + ">");
444                     closetag1.push_front("</xs:" + xsdk + ">");
445                 }
446             } else if (!simpleType.empty()) {
447                 opentag.push_back("<xs:simpleContent>");
448                 closetag2.push_front("</xs:simpleContent>");
449                 opentag.push_back("<xs:extension base=\"" + simpleType + "\">");
450                 closetag2.push_front("</xs:extension>");
451             }
452         }
453     }
454     ITERATE ( list<string>, s, opentag ) {
455         PrintASNNewLine(*os, indent++) << *s;
456     }
457     if (isAttlist) {
458         ITERATE ( TMembers, i, m_Members ) {
459             const CDataMember& member = **i;
460             member.PrintXMLSchema(*os, indent);
461         }
462     } else if (!isSimple) {
463         ITERATE ( TMembers, i, m_Members ) {
464             const CDataMember& member = **i;
465             if (member.Attlist()) {
466                 continue;
467             }
468             if (isMixed && member.Notag()) {
469                 if (dynamic_cast<const CStringDataType*>(member.GetType())) {
470                     continue;
471                 }
472             }
473             member.PrintXMLSchema(*os, indent, isSimpleSeq);
474         }
475     }
476     ITERATE ( list<string>, s, closetag1 ) {
477         PrintASNNewLine(*os, --indent) << *s;
478     }
479     if (hasAttlist) {
480         ITERATE ( TMembers, i, m_Members ) {
481             const CDataMember& member = **i;
482             if (member.Attlist()) {
483                 member.PrintXMLSchema(*os, indent);
484             }
485         }
486     }
487     ITERATE ( list<string>, s, closetag2 ) {
488         PrintASNNewLine(*os, --indent) << *s;
489     }
490     if (defineAsType) {
491         GetModule()->AddExtraSchemaOutput( CNcbiOstrstreamToString(otype) );
492     }
493     m_LastComments.PrintDTD(out, CComments::eMultiline);
494 }
495 
PrintDTDElement(CNcbiOstream & out,bool contents_only) const496 void CDataMemberContainerType::PrintDTDElement(CNcbiOstream& out, bool contents_only) const
497 {
498     string tag = XmlTagName();
499     bool hasAttlist= false, isAttlist= false;
500     bool isSimple= false, isSeq= false;
501 
502     if (GetEnforcedStdXml()) {
503         ITERATE ( TMembers, i, m_Members ) {
504             if (i->get()->Attlist()) {
505                 hasAttlist = true;
506                 break;
507             }
508         }
509         if (GetDataMember()) {
510             isAttlist = GetDataMember()->Attlist();
511         }
512         if (GetMembers().size()==1) {
513             const CDataMember* member = GetMembers().front().get();
514             const CUniSequenceDataType* uniType =
515                 dynamic_cast<const CUniSequenceDataType*>(member->GetType());
516             if (uniType && member->Notag()) {
517                 isSeq = true;
518             }
519         }
520         if (hasAttlist && GetMembers().size()==2) {
521             ITERATE ( TMembers, i, GetMembers() ) {
522                 if (i->get()->Attlist()) {
523                     continue;
524                 }
525                 if (i->get()->SimpleType()) {
526                     isSimple = true;
527                     i->get()->GetType()->PrintDTDElement(out);
528                 } else {
529                     const CUniSequenceDataType* uniType =
530                         dynamic_cast<const CUniSequenceDataType*>(i->get()->GetType());
531                     if (uniType && i->get()->Notag()) {
532                         isSeq = true;
533                     }
534                 }
535             }
536         }
537     }
538 
539     if (isAttlist) {
540         ITERATE ( TMembers, i, m_Members ) {
541             (*i)->Comments().PrintDTD(out,CComments::eNoEOL);
542             i->get()->GetType()->PrintDTDElement(out);
543         }
544         return;
545     }
546     if (!isSimple) {
547         if (!contents_only) {
548             out << "\n<!ELEMENT " << tag << " ";
549             if (!isSeq) {
550                 out << "(";
551             }
552         }
553         bool need_separator = false;
554         ITERATE ( TMembers, i, m_Members ) {
555             if (need_separator) {
556                 out << XmlMemberSeparator();
557             }
558             need_separator = true;
559             const CDataMember& member = **i;
560             string member_name( member.GetType()->XmlTagName());
561             const CUniSequenceDataType* uniType =
562                 dynamic_cast<const CUniSequenceDataType*>(member.GetType());
563             bool isOptional = member.Optional();
564             if (GetEnforcedStdXml()) {
565                 if (member.Attlist()) {
566                     need_separator = false;
567                     continue;
568                 }
569                 if (member.Notag()) {
570                     const CStaticDataType* statType =
571                         dynamic_cast<const CStaticDataType*>(member.GetType());
572                     bool need_open = !statType;
573                     bool need_newline = !need_open;
574                     const CDataMemberContainerType* data =
575                         dynamic_cast<const CDataMemberContainerType*>(member.GetType());
576                     if (data) {
577                         const CDataMember* data_member = data->GetMembers().front().get();
578                         if (data_member && data_member->Notag() &&
579                             data_member->GetType()->IsUniSeq()) {
580                             isOptional = false;
581                             need_open = false;
582                             need_newline = false;
583                         }
584                     }
585                     if (need_open) {
586                         out << "(";
587                     }
588                     if (need_newline) {
589                         out << "\n        ";
590                     }
591                     member.GetType()->PrintDTDElement(out,true);
592                     if (need_open) {
593                         out << ")";
594                     }
595                 } else {
596                     out << "\n        " << member_name;
597                 }
598             } else {
599                 out << "\n        " << member_name;
600             }
601             if (uniType) {
602                 const CStaticDataType* elemType =
603                     dynamic_cast<const CStaticDataType*>(uniType->GetElementType());
604                 if ((elemType || member.NoPrefix()) && GetEnforcedStdXml()) {
605                     if ( isOptional ) {
606                         out << '*';
607                     } else {
608                         out << '+';
609                     }
610                 } else {
611                     if ( isOptional ) {
612                         out << '?';
613                     }
614                 }
615             } else {
616                 if ( isOptional ) {
617                     out << '?';
618                 }
619             }
620         }
621         if (!contents_only) {
622             if (!isSeq) {
623                 out << ")";
624             }
625             out << ">";
626         }
627     }
628     if (hasAttlist) {
629         ITERATE ( TMembers, i, m_Members ) {
630             const CDataMember& member = **i;
631             if (member.Attlist()) {
632                 member.GetComments().PrintDTD(out, CComments::eNoEOL);
633                 out << "\n<!ATTLIST " << tag;
634                 member.GetType()->PrintDTDElement(out);
635                 break;
636             }
637         }
638         out << ">";
639     }
640 }
641 
PrintDTDExtra(CNcbiOstream & out) const642 void CDataMemberContainerType::PrintDTDExtra(CNcbiOstream& out) const
643 {
644     ITERATE ( TMembers, i, m_Members ) {
645         const CDataMember& member = **i;
646         if (member.Notag()) {
647             member.GetType()->PrintDTDExtra(out);
648         } else {
649             member.PrintDTD(out);
650         }
651     }
652     m_LastComments.PrintDTD(out, CComments::eMultiline);
653 }
654 
FixTypeTree(void) const655 void CDataMemberContainerType::FixTypeTree(void) const
656 {
657     CParent::FixTypeTree();
658     ITERATE ( TMembers, i, m_Members ) {
659         (*i)->GetType()->SetParent(this, (*i)->GetName());
660     }
661 }
662 
CheckType(void) const663 bool CDataMemberContainerType::CheckType(void) const
664 {
665     bool ok = true;
666     ITERATE ( TMembers, i, m_Members ) {
667         if ( !(*i)->Check() )
668             ok = false;
669     }
670 
671 //check tags
672 // All tags must be different
673     bool hasUntagged = false;
674     set< pair< CAsnBinaryDefs::TLongTag, CAsnBinaryDefs::ETagClass> > tag_inuse;
675     CAsnBinaryDefs::ETagType tagtype = GetTagType();
676     bool ischoice = NStr::CompareCase(GetASNKeyword(),"CHOICE")==0;
677 
678     if (tagtype == CAsnBinaryDefs::eAutomatic ||
679         ischoice) {
680 
681         ITERATE ( TMembers, i, m_Members ) {
682             const CDataType* itype = (*i)->GetType();
683             bool hastag = itype->HasTag();
684             CAsnBinaryDefs::TLongTag itag = itype->GetTag();
685             CAsnBinaryDefs::ETagClass tagclass = itype->GetTagClass();
686             if (!hastag && itype->IsReference()) {
687                 const CDataType* rtype = itype->Resolve();
688                 if (rtype) {
689                     hastag = rtype->HasTag();
690                     itag = rtype->GetTag();
691                     tagclass = rtype->GetTagClass();
692                 }
693             }
694             if (hastag) {
695                 pair< CAsnBinaryDefs::TLongTag, CAsnBinaryDefs::ETagClass>
696                     tag = make_pair(itag, tagclass);
697                 if (tag_inuse.find(tag) != tag_inuse.end()) {
698                     NCBI_THROW(CDatatoolException,eInvalidData,
699                                "Duplicate tag: " + itype->IdName() +
700                                " [" + NStr::NumericToString(itag) + "] in " +
701                                GetModule()->GetName());
702                 }
703                 tag_inuse.insert(tag);
704                 if (hasUntagged) {
705                     NCBI_THROW(CDatatoolException,eInvalidData,
706                                "No explicit tag for " + itype->IdName() + " in " +
707                                GetModule()->GetName());
708                 }
709             }
710             else {
711                 hasUntagged = true;
712                 if (!tag_inuse.empty()) {
713                     NCBI_THROW(CDatatoolException,eInvalidData,
714                                "No explicit tag for " + itype->IdName() + " in " +
715                                GetModule()->GetName());
716                 }
717             }
718         }
719     }
720     return ok;
721 }
722 
CreateDefault(const CDataValue &) const723 TObjectPtr CDataMemberContainerType::CreateDefault(const CDataValue& ) const
724 {
725     NCBI_THROW(CDatatoolException,eNotImplemented,
726                  GetASNKeyword() + string(" default not implemented"));
727 }
728 
UniElementNameExists(const string & name) const729 bool CDataMemberContainerType::UniElementNameExists(const string& name) const
730 {
731     bool res = false;
732     for (TMembers::const_iterator i = m_Members.begin();
733         !res && i != m_Members.end(); ++i) {
734         const CUniSequenceDataType* mem =
735             dynamic_cast<const CUniSequenceDataType*>((*i)->GetType());
736         if (mem != 0) {
737             const CDataMemberContainerType* elem =
738                 dynamic_cast<const CDataMemberContainerType*>(mem->GetElementType());
739             res = (elem != 0 && elem->GetMemberName() == name);
740         }
741     }
742     return res;
743 }
744 
XmlMemberSeparator(void) const745 const char* CDataContainerType::XmlMemberSeparator(void) const
746 {
747     return ", ";
748 }
749 
CreateTypeInfo(void)750 CTypeInfo* CDataContainerType::CreateTypeInfo(void)
751 {
752     return CreateClassInfo();
753 }
754 
CreateClassInfo(void)755 CClassTypeInfo* CDataContainerType::CreateClassInfo(void)
756 {
757     size_t itemCount = 0;
758     // add place for 'isSet' flags
759     ITERATE ( TMembers, i, GetMembers() ) {
760         ++itemCount;
761         CDataMember* mem = i->get();
762         if ( mem->Optional() || mem->GetDefault() || mem->Nillable() )
763             ++itemCount;
764     }
765     unique_ptr<CAnyTypeClassInfo> typeInfo(new CAnyTypeClassInfo(
766         HasExternalName() ? GlobalName() : kEmptyStr, itemCount));
767     size_t index = 0;
768     for ( TMembers::const_iterator i = GetMembers().begin();
769           i != GetMembers().end(); ++i ) {
770         CDataMember* mem = i->get();
771         CDataType* memType = mem->GetType();
772         TConstObjectPtr memberPtr = typeInfo->GetAnyTypePtr(index++);
773         CMemberInfo* memInfo =
774             typeInfo->AddMember(mem->GetName(), memberPtr,
775                                 memType->GetTypeInfo());
776         if ( mem->GetDefault() ) {
777             TObjectPtr defPtr = memType->CreateDefault(*mem->GetDefault());
778             memInfo->SetDefault(defPtr);
779         }
780         if ( mem->Optional() ) {
781             memInfo->SetOptional();
782         }
783         if ( mem->Optional() || mem->GetDefault() || mem->Nillable() ) {
784             memInfo->SetSetFlag(typeInfo->GetSetFlagPtr(index++));
785         }
786         if (!IsASNDataSpec()) {
787             memInfo->SetNoPrefix();
788         }
789         if (mem->Attlist()) {
790             memInfo->SetAttlist();
791         }
792         if (mem->Notag()) {
793             memInfo->SetNotag();
794         }
795         if (dynamic_cast<const CAnyContentDataType*>(mem->GetType()) != 0) {
796             memInfo->SetAnyContent();
797         }
798         if (mem->Nillable()) {
799             memInfo->SetNillable();
800         }
801     }
802     typeInfo->AssignItemsTags();
803     if ( HaveModuleName() )
804         typeInfo->SetModuleName(GetModule()->GetName());
805     return typeInfo.release();
806 }
807 
GenerateCode(void) const808 AutoPtr<CTypeStrings> CDataContainerType::GenerateCode(void) const
809 {
810 #if 0
811     return GetFullCType();
812 #else
813     string alias = GetVar("_fullalias");
814     if (alias.empty()) {
815         return GetFullCType();
816     }
817     const CDataType* aliastype = ResolveGlobal(alias);
818     if (!aliastype) {
819         NCBI_THROW(CDatatoolException,eWrongInput,
820             "cannot create type info of _fullalias " + alias);
821     }
822     AutoPtr<CTypeStrings> dType = aliastype->GetRefCType();
823     dType->SetDataType(aliastype);
824     AutoPtr<CAliasTypeStrings> code(new CAliasTypeStrings(GlobalName(),
825                                                           ClassName(),
826                                                           *dType.release(),
827                                                           Comments()));
828     code->SetNamespaceName( GetNamespaceName());
829     code->SetFullAlias();
830     return AutoPtr<CTypeStrings>(code.release());
831 #endif
832 }
833 
GetFullCType(void) const834 AutoPtr<CTypeStrings> CDataContainerType::GetFullCType(void) const
835 {
836     AutoPtr<CClassTypeStrings> code(new CClassTypeStrings(
837         GlobalName(), ClassName(), GetNamespaceName(), this, Comments()));
838     return AddMembers(code);
839 }
840 
AddMembers(AutoPtr<CClassTypeStrings> & code) const841 AutoPtr<CTypeStrings> CDataContainerType::AddMembers(
842     AutoPtr<CClassTypeStrings>& code) const
843 {
844     bool isRootClass = GetParentType() == 0;
845     bool haveUserClass = isRootClass;
846 /*
847     bool isObject;
848     if ( haveUserClass ) {
849         isObject = true;
850     }
851     else {
852         isObject = GetBoolVar("_object");
853     }
854 */
855     code->SetHaveUserClass(haveUserClass);
856     code->SetObject(true /*isObject*/ );
857     ITERATE ( TMembers, i, GetMembers() ) {
858         string defaultCode;
859         bool optional = (*i)->Optional();
860         const CDataValue* defaultValue = (*i)->GetDefault();
861         if ( defaultValue ) {
862             defaultCode = (*i)->GetType()->GetDefaultString(*defaultValue);
863             _ASSERT(!defaultCode.empty());
864         }
865 
866         bool delayed = GetBoolVar((*i)->GetName()+"._delay");
867         AutoPtr<CTypeStrings> memberType = (*i)->GetType()->GetFullCType();
868         string external_name = (*i)->GetName();
869         string member_name = (*i)->GetType()->DefClassMemberName();
870         if (member_name.empty()) {
871             member_name = external_name;
872         }
873         code->AddMember(external_name, member_name, memberType,
874                         (*i)->GetType()->GetVar("_pointer"),
875                         optional, defaultCode, delayed,
876                         (*i)->GetType()->GetTag(),
877                         !IsASNDataSpec(), (*i)->Attlist(), (*i)->Notag(),
878                         (*i)->SimpleType(),(*i)->GetType(),false,
879                         (*i)->Comments());
880         (*i)->GetType()->SetTypeStr(&(*code));
881     }
882     SetTypeStr(&(*code));
883     SetParentClassTo(*code);
884     return AutoPtr<CTypeStrings>(code.release());
885 }
886 
GetRefCType(void) const887 AutoPtr<CTypeStrings> CDataContainerType::GetRefCType(void) const
888 {
889     return AutoPtr<CTypeStrings>(new CClassRefTypeStrings(ClassName(),
890                                                           Namespace(),
891                                                           FileName(),
892                                                           Comments()));
893 }
894 
GetSpecKeyword(void) const895 string CDataContainerType::GetSpecKeyword(void) const
896 {
897     bool hasAttlist = !m_Members.empty() && m_Members.front()->Attlist();
898     if (( hasAttlist && m_Members.size() == 2) ||
899         (!hasAttlist && m_Members.size() == 1)) {
900         const CDataMember* member = m_Members.back().get();
901         if (!GetParentType() && (member->SimpleType() || member->Notag())) {
902             return member->GetType()->GetSpecKeyword();
903         }
904     }
905     return GetASNKeyword();
906 }
907 
908 
GetASNKeyword(void) const909 const char* CDataSetType::GetASNKeyword(void) const
910 {
911     return "SET";
912 }
913 
GetDEFKeyword(void) const914 const char* CDataSetType::GetDEFKeyword(void) const
915 {
916     return "_SET_";
917 }
918 
CheckValue(const CDataValue & value) const919 bool CDataSetType::CheckValue(const CDataValue& value) const
920 {
921     const CBlockDataValue* block =
922         dynamic_cast<const CBlockDataValue*>(&value);
923     if ( !block ) {
924         value.Warning("block of values expected", 2);
925         return false;
926     }
927 
928     typedef map<string, const CDataMember*> TReadValues;
929     TReadValues mms;
930     for ( TMembers::const_iterator m = GetMembers().begin();
931           m != GetMembers().end(); ++m ) {
932         mms[m->get()->GetName()] = m->get();
933     }
934 
935     ITERATE ( CBlockDataValue::TValues, v, block->GetValues() ) {
936         const CNamedDataValue* currvalue =
937             dynamic_cast<const CNamedDataValue*>(v->get());
938         if ( !currvalue ) {
939             v->get()->Warning("named value expected", 3);
940             return false;
941         }
942         TReadValues::iterator member = mms.find(currvalue->GetName());
943         if ( member == mms.end() ) {
944             currvalue->Warning("unexpected member", 4);
945             return false;
946         }
947         if ( !member->second->GetType()->CheckValue(currvalue->GetValue()) ) {
948             return false;
949         }
950         mms.erase(member);
951     }
952 
953     for ( TReadValues::const_iterator member = mms.begin();
954           member != mms.end(); ++member ) {
955         if ( !member->second->Optional() ) {
956             value.Warning(member->first + " member expected", 5);
957             return false;
958         }
959     }
960     return true;
961 }
962 
CreateClassInfo(void)963 CClassTypeInfo* CDataSetType::CreateClassInfo(void)
964 {
965     return CParent::CreateClassInfo()->SetRandomOrder();
966 }
967 
GetASNKeyword(void) const968 const char* CDataSequenceType::GetASNKeyword(void) const
969 {
970     return "SEQUENCE";
971 }
972 
GetDEFKeyword(void) const973 const char* CDataSequenceType::GetDEFKeyword(void) const
974 {
975     return "_SEQUENCE_";
976 }
977 
CheckValue(const CDataValue & value) const978 bool CDataSequenceType::CheckValue(const CDataValue& value) const
979 {
980     const CBlockDataValue* block =
981         dynamic_cast<const CBlockDataValue*>(&value);
982     if ( !block ) {
983         value.Warning("block of values expected", 6);
984         return false;
985     }
986     TMembers::const_iterator member = GetMembers().begin();
987     CBlockDataValue::TValues::const_iterator cvalue =
988         block->GetValues().begin();
989     while ( cvalue != block->GetValues().end() ) {
990         const CNamedDataValue* currvalue =
991             dynamic_cast<const CNamedDataValue*>(cvalue->get());
992         if ( !currvalue ) {
993             cvalue->get()->Warning("named value expected", 7);
994             return false;
995         }
996         for (;;) {
997             if ( member == GetMembers().end() ) {
998                 currvalue->Warning("unexpected value", 8);
999                 return false;
1000             }
1001             if ( (*member)->GetName() == currvalue->GetName() )
1002                 break;
1003             if ( !(*member)->Optional() ) {
1004                 currvalue->GetValue().Warning((*member)->GetName() +
1005                                               " member expected", 9);
1006                 return false;
1007             }
1008             ++member;
1009         }
1010         if ( !(*member)->GetType()->CheckValue(currvalue->GetValue()) ) {
1011             return false;
1012         }
1013         ++member;
1014         ++cvalue;
1015     }
1016     while ( member != GetMembers().end() ) {
1017         if ( !(*member)->Optional() ) {
1018             value.Warning((*member)->GetName() + " member expected", 10);
1019             return false;
1020         }
1021     }
1022     return true;
1023 }
1024 
GetFullCType(void) const1025 AutoPtr<CTypeStrings> CWsdlDataType::GetFullCType(void) const
1026 {
1027     AutoPtr<CClassTypeStrings> code(new CWsdlTypeStrings(
1028         GlobalName(), ClassName(), GetNamespaceName(), this, Comments()));
1029     code->SetHaveTypeInfo(false);
1030     return AddMembers(code);
1031 }
1032 
1033 
CDataMember(const string & name,const AutoPtr<CDataType> & type)1034 CDataMember::CDataMember(const string& name, const AutoPtr<CDataType>& type)
1035     : m_Name(name), m_Type(type), m_Optional(false),
1036     m_NoPrefix(false), m_Attlist(false), m_Notag(false), m_SimpleType(false),
1037     m_Nillable(false)
1038 {
1039     if ( m_Name.empty() ) {
1040         m_Notag = true;
1041 /*
1042         string loc("Invalid identifier name in ASN.1 specification");
1043         if (type) {
1044             loc += " (line " + NStr::IntToString(type->GetSourceLine()) + ")";
1045         }
1046         NCBI_THROW(CDatatoolException,eInvalidData, loc);
1047 */
1048     }
1049     m_Type->SetDataMember(this);
1050 }
1051 
~CDataMember(void)1052 CDataMember::~CDataMember(void)
1053 {
1054 }
1055 
PrintASN(CNcbiOstream & out,int indent,bool last) const1056 void CDataMember::PrintASN(CNcbiOstream& out, int indent, bool last) const
1057 {
1058     GetType()->PrintASNTypeComments(out, indent);
1059     bool oneLineComment = m_Comments.OneLine();
1060     if ( !oneLineComment )
1061         m_Comments.PrintASN(out, indent);
1062     out << CDataTypeModule::ToAsnId(GetName()) << ' ';
1063     GetType()->PrintASN(out, indent);
1064     if ( GetDefault() ) {
1065         GetDefault()->PrintASN(out << " DEFAULT ", indent + 1);
1066     }
1067     else if ( Optional() ) {
1068         out << " OPTIONAL";
1069     }
1070     if ( !last )
1071         out << ',';
1072     if ( oneLineComment ) {
1073         out << ' ';
1074         m_Comments.PrintASN(out, indent, CComments::eOneLine);
1075     }
1076 }
1077 
PrintSpecDump(CNcbiOstream & out,int indent,const char * tag) const1078 void CDataMember::PrintSpecDump(CNcbiOstream& out, int indent, const char* tag) const
1079 {
1080     if (!SimpleType()) {
1081         const CDataType* type = GetType();
1082         if (!Attlist()) {
1083             bool needTitle= !Notag() || (type->IsStdType() || type->IsPrimitive());
1084             if (!needTitle) {
1085                 const CDataMemberContainerType* cont =
1086                     dynamic_cast<const CDataMemberContainerType*>(type);
1087                 if (cont) {
1088                     needTitle=true;
1089                     const CDataMemberContainerType::TMembers& members = cont->GetMembers();
1090                     bool hasAttlist = !members.empty() && members.front()->Attlist();
1091                     if (( hasAttlist && members.size() == 2) ||
1092                         (!hasAttlist && members.size() == 1)) {
1093                         const CDataMember* member = members.back().get();
1094                         needTitle = !member->GetType()->IsUniSeq();
1095                         if (!needTitle) {
1096                             --indent;
1097                         }
1098                     }
1099                 } else if (type->IsUniSeq()) {
1100                     const CDataType* parent = type->GetParentType();
1101                     needTitle = parent->GetDataMember() != 0;
1102                     if (!needTitle && parent->IsContainer()) {
1103                         cont = dynamic_cast<const CDataMemberContainerType*>(parent);
1104                         needTitle = cont->GetMembers().size() == 1;
1105                     }
1106                     if (!needTitle) {
1107                         const CUniSequenceDataType* uni =
1108                             dynamic_cast<const CUniSequenceDataType*>(type);
1109                         if (uni->GetElementType()->IsContainer()) {
1110                             --indent;
1111                         }
1112                     }
1113                 }
1114             }
1115             if (needTitle) {
1116                 PrintASNNewLine(out, indent);
1117                 out << tag << ',' <<
1118                        type->GetSourceLine() <<",";
1119                 out << type->GetFullName() << ',' << type->GetSpecKeyword();
1120                 if ( GetDefault() ) {
1121                     GetDefault()->PrintASN(out << ",DEFAULT,", indent + 1);
1122                 }
1123                 else if ( Optional() ) {
1124                     out << ",OPTIONAL";
1125                 }
1126                 type->Comments().PrintASN(out, indent,CComments::eNoEOL);
1127                 m_Comments.PrintASN(out, indent,CComments::eNoEOL);
1128             }
1129         }
1130         type->PrintSpecDump(out, indent);
1131     }
1132 }
1133 
PrintJSONSchema(CNcbiOstream & out,int indent,list<string> & required,bool contents_only) const1134 void CDataMember::PrintJSONSchema(CNcbiOstream& out, int indent, list<string>& required, bool contents_only) const
1135 {
1136     GetType()->PrintJSONSchema(out, indent, required, contents_only);
1137 }
1138 
PrintXMLSchema(CNcbiOstream & out,int indent,bool contents_only) const1139 void CDataMember::PrintXMLSchema(CNcbiOstream& out, int indent, bool contents_only) const
1140 {
1141 #if !_DATATOOL_USE_SCHEMA_STYLE_COMMENTS
1142     m_Comments.PrintDTD(out, CComments::eNoEOL);
1143 #endif
1144     GetType()->PrintXMLSchema(out, indent, contents_only);
1145 }
1146 
PrintDTD(CNcbiOstream & out) const1147 void CDataMember::PrintDTD(CNcbiOstream& out) const
1148 {
1149     GetType()->PrintDTD(out, m_Comments);
1150 }
1151 
Check(void) const1152 bool CDataMember::Check(void) const
1153 {
1154     if ( !m_Type->Check() )
1155         return false;
1156     if ( !m_Default )
1157         return true;
1158     return GetType()->CheckValue(*m_Default);
1159 }
1160 
SetDefault(const AutoPtr<CDataValue> & value)1161 void CDataMember::SetDefault(const AutoPtr<CDataValue>& value)
1162 {
1163     m_Default = value;
1164 }
1165 
SetOptional(void)1166 void CDataMember::SetOptional(void)
1167 {
1168     m_Optional = true;
1169 }
1170 
SetNoPrefix(void)1171 void CDataMember::SetNoPrefix(void)
1172 {
1173 //    m_NoPrefix = true;
1174 }
1175 
SetAttlist(void)1176 void CDataMember::SetAttlist(void)
1177 {
1178     m_Attlist = true;
1179 }
SetNotag(void)1180 void CDataMember::SetNotag(void)
1181 {
1182     m_Notag = true;
1183 }
SetSimpleType(void)1184 void CDataMember::SetSimpleType(void)
1185 {
1186     m_SimpleType = true;
1187 }
SetNillable(void)1188 void CDataMember::SetNillable(void)
1189 {
1190     m_Nillable = true;
1191 }
1192 
1193 END_NCBI_SCOPE
1194