MythTV master
mythxsd.cpp
Go to the documentation of this file.
1
2// Program Name: xsd.cpp
3// Created : Feb. 20, 2012
4//
5// Purpose : XSD Generation Class
6//
7// Copyright (c) 2012 David Blain <dblain@mythtv.org>
8//
9// Licensed under the GPL v2 or later, see COPYING for details
10//
12
13// C++ headers
14#include <algorithm>
15
16// Qt
17#include <QCoreApplication>
18
19// MythTV
20#include "mythlogging.h"
21#include "http/mythxsd.h"
26
27
29//
31
32// The code for creating the enum in xsd does not work
33// Since there is only one enum n the entire system, treat it as a string
34
35// HTTPResponse MythXSD::GetEnumXSD( const HTTPRequest2& pRequest, const QString& sEnumName )
36// {
37// if (sEnumName.isEmpty())
38// return Error(pRequest, QString( "XSD request on empty invalid enum name"));
39
40// // ----------------------------------------------------------------------
41// // sEnumName needs to be in class.enum format
42// // ----------------------------------------------------------------------
43
44// if (sEnumName.count('.') != 1 )
45// return Error(pRequest, QString( "XSD request invalid enum name %1").arg(sEnumName));
46
47// QStringList lstTypeParts = sEnumName.split( '.' );
48
49// // ----------------------------------------------------------------------
50// // Create Parent object so we can get to its metaObject
51// // ----------------------------------------------------------------------
52
53// QString sParentFQN = lstTypeParts[0];
54// #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
55// int nParentId = QMetaType::type( sParentFQN.toUtf8() );
56
57// // ----------------------------------------------------------------------
58// // Check for things that were formerly registered as both 'Foo' and 'Foo*'
59// // ----------------------------------------------------------------------
60// if (nParentId == QMetaType::UnknownType)
61// {
62// QString sFQN = sParentFQN + "*";
63// nParentId = QMetaType::type( sFQN.toUtf8() );
64// }
65
66// // ----------------------------------------------------------------------
67// // if a DataContract type, we need to prefix name with DTC::
68// // These types are all pointers to objects, so we also need to add "*"
69// // ----------------------------------------------------------------------
70
71// if (nParentId == QMetaType::UnknownType)
72// {
73// QString sFQN = "V2" + sParentFQN + "*";
74// nParentId = QMetaType::type( sFQN.toUtf8() );
75// }
76
77// if (nParentId == QMetaType::UnknownType)
78// return Error(pRequest, QString( "XSD request unknown enum name %1").arg(sEnumName));
79// const QMetaObject *pMetaObject = QMetaType::metaObjectForType(nParentId);
80// #else
81// QMetaType metaType = QMetaType::fromName( sParentFQN.toUtf8() );
82// if (metaType.id() == QMetaType::UnknownType)
83// metaType = QMetaType::fromName( sParentFQN.toUtf8() + "*" );
84// if (metaType.id() == QMetaType::UnknownType)
85// metaType = QMetaType::fromName( "V2" + sParentFQN.toUtf8() + "*" );
86// if (metaType.id() == QMetaType::UnknownType)
87// return Error(pRequest, QString( "XSD request unknown enum name %1").arg(sEnumName));
88// const QMetaObject *pMetaObject = metaType.metaObject();
89// #endif
90
91// if (pMetaObject == nullptr)
92// return Error(pRequest, QString( "XSD cannot find enum name %1").arg(sEnumName));
93
94// // ----------------------------------------------------------------------
95// // Now look up enum
96// // ----------------------------------------------------------------------
97
98// int nEnumIdx = pMetaObject->indexOfEnumerator( lstTypeParts[1].toUtf8() );
99
100// if (nEnumIdx < 0 )
101// return Error(pRequest, QString( "XSD cannot find values for enum name %1").arg(sEnumName));
102
103// QMetaEnum metaEnum = pMetaObject->enumerator( nEnumIdx );
104
105// // ----------------------------------------------------------------------
106// // render xsd for this enum
107// //
108// // <xs:simpleType name="RecordingInfo.RecordingDupMethodEnum">
109// // <xs:restriction base="xs:string">
110// // <xs:enumeration value="kDupCheckNone">
111// // <xs:annotation>
112// // <xs:appinfo>
113// // <EnumerationValue xmlns="http://schemas.microsoft.com/2003/10/Serialization/">1</EnumerationValue>
114// // </xs:appinfo>
115// // </xs:annotation>
116// // </xs:enumeration>
117// // <xs:enumeration value="kDupCheckSub">
118// // <xs:annotation>
119// // <xs:appinfo>
120// // <EnumerationValue xmlns="http://schemas.microsoft.com/2003/10/Serialization/">2</EnumerationValue>
121// // </xs:appinfo>
122// // </xs:annotation>
123// // </xs:enumeration>
124// // </xs:restriction>
125// // </xs:simpleType>
126// //
127// // <xs:element name="RecordingInfo.RecordingDupMethodEnum" type="tns:RecordingInfo.RecordingDupMethodEnum" nillable="true"/>
128// // ----------------------------------------------------------------------
129
130// if (!pRequest->m_queries.contains( "raw" ))
131// {
132// appendChild( createProcessingInstruction( "xml-stylesheet",
133// R"(type="text/xsl" href="/xslt/enum.xslt")" ));
134// }
135
136// // ----------------------------------------------------------------------
137// // Create xs:simpleType structure
138// // ----------------------------------------------------------------------
139
140// QDomElement oTypeNode = createElement( "xs:simpleType" );
141// QDomElement oRestrictNode = createElement( "xs:restriction" );
142
143// oTypeNode .setAttribute( "name", sEnumName );
144// oRestrictNode.setAttribute( "base", "xs:string" );
145
146// oTypeNode.appendChild( oRestrictNode );
147
148// for( int nIdx = 0; nIdx < metaEnum.keyCount(); nIdx++)
149// {
150// QDomElement oEnum = createElement( "xs:enumeration" );
151
152// oEnum.setAttribute( "value", metaEnum.key( nIdx ));
153
154// // ------------------------------------------------------------------
155// // Add appInfo to store numerical value & translated text
156// // ------------------------------------------------------------------
157
158// QDomElement oAnn = createElement( "xs:annotation" );
159// QDomElement oApp = createElement( "xs:appinfo" );
160// QDomElement oEnumVal = createElement( "EnumerationValue" );
161// QDomElement oEnumDesc = createElement( "EnumerationDesc" );
162
163// // The following namespace is needed for visual studio to generate negative enums correctly.
164// oEnumVal.setAttribute("xmlns", "http://schemas.microsoft.com/2003/10/Serialization/");
165
166// oEnum.appendChild( oAnn );
167// oAnn .appendChild( oApp );
168// oApp .appendChild( oEnumVal );
169// oApp .appendChild( oEnumDesc );
170
171// QString sFQNKey = sEnumName + "." + metaEnum.key( nIdx );
172
173// oEnumVal .appendChild( createTextNode( QString::number( metaEnum.value( nIdx ))));
174// oEnumDesc.appendChild( createTextNode( QCoreApplication::translate("Enums",
175// sFQNKey.toUtf8() )));
176
177// oRestrictNode.appendChild( oEnum );
178// }
179
180// // ----------------------------------------------------------------------
181
182// QDomElement oElementNode = createElement( "xs:element" );
183
184// oElementNode.setAttribute( "name" , sEnumName );
185// oElementNode.setAttribute( "type" , "tns:" + sEnumName );
186// oElementNode.setAttribute( "nillable", "true" );
187
188// // ----------------------------------------------------------------------
189// //
190// // ----------------------------------------------------------------------
191
192// QDomElement oRoot = CreateSchemaRoot();
193
194// oRoot.appendChild( oTypeNode );
195// oRoot.appendChild( oElementNode );
196
197// appendChild( oRoot );
198
199// // ----------------------------------------------------------------------
200// // Return xsd doc to caller
201// // ----------------------------------------------------------------------
202
203// // Create the XML result
204// auto data = MythHTTPData::Create(toByteArray());
205// data->m_mimeType = MythMimeDatabase().MimeTypeForName("application/xml");
206// data->m_cacheType = HTTPETag | HTTPShortLife;
207// return MythHTTPResponse::DataResponse(pRequest, data);
208// }
209
211//
213
214HTTPResponse MythXSD::GetXSD( const HTTPRequest2& pRequest, QString sTypeName )
215{
216 bool bIsArray = false;
217 bool bIsMap = false;
218
219 if (sTypeName.isEmpty())
220 return Error(pRequest, QString( "XSD request on empty name" ));
221
222
223 // ----------------------------------------------------------------------
224 // Is this a special type name?
225 // ----------------------------------------------------------------------
226
227 if (sTypeName.startsWith( "ArrayOf" ))
228 {
229 bIsArray = true;
230 sTypeName = sTypeName.mid( 7 );
231 }
232
233 if (sTypeName.startsWith( "MapOfString" ))
234 {
235 bIsMap = true;
236 sTypeName = sTypeName.mid( 11 );
237 }
238
239 // ----------------------------------------------------------------------
240 // Check to see if one of the Qt Types we need to handle special
241 // ----------------------------------------------------------------------
242
243#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
244 int id = QMetaType::type( sTypeName.toUtf8() );
245
246 // ----------------------------------------------------------------------
247 // Check for things that were formerly registered as both 'Foo' and 'Foo*'
248 // ----------------------------------------------------------------------
249 if (id == QMetaType::UnknownType)
250 {
251 QString sFQN = sTypeName + "*";
252 id = QMetaType::type( sFQN.toUtf8() );
253 }
254
255 // ----------------------------------------------------------------------
256 // if a DataContract type, we need to prefix name with DTC::
257 // These types are all pointers to objects, so we also need to add "*"
258 // ----------------------------------------------------------------------
259
260 if (id == QMetaType::UnknownType)
261 {
262 QString sFQN = "V2" + sTypeName;
263 id = QMetaType::type( sFQN.toUtf8() );
264 }
265 if (id == QMetaType::UnknownType)
266 {
267 QString sFQN = "V2" + sTypeName + "*";
268 id = QMetaType::type( sFQN.toUtf8() );
269 }
270 // if (id == QMetaType::UnknownType)
271 // return Error(pRequest, QString( "XSD request on unknown type %1").arg(sTypeName));
272#else
273 QMetaType metaType = QMetaType::fromName( sTypeName.toUtf8() );
274 if (metaType.id() == QMetaType::UnknownType)
275 metaType = QMetaType::fromName( sTypeName.toUtf8() + "*" );
276 if (metaType.id() == QMetaType::UnknownType)
277 metaType = QMetaType::fromName( "V2" + sTypeName.toUtf8() + "*" );
278 // if (metaType.id() == QMetaType::UnknownType)
279 // return Error(pRequest,QString( "XSD request on unknown type %1").arg(sTypeName));
280
281 int id = metaType.id();
282#endif
283
284 // ----------------------------------------------------------------------
285 //
286 // ----------------------------------------------------------------------
287
288 if (!(bIsArray || bIsMap) && (id < QMetaType::User))
289 return Error(pRequest,QString( "XSD request on unsupported type %1").arg(sTypeName));
290
291 // ------------------------------------------------------------------
292 // Need to create an instance of the class to access it's metadata.
293 // ------------------------------------------------------------------
294
295 // --------------------------------------------------------------
296 // Render XSD
297 // --------------------------------------------------------------
298
299 if (!pRequest->m_queries.contains( "raw" ))
300 {
301 appendChild( createProcessingInstruction( "xml-stylesheet",
302 R"(type="text/xsl" href="/xslt/class.xslt")" ));
303 }
304
305 if (bIsArray)
306 {
307 RenderArrayXSD( pRequest, sTypeName, id >= QMetaType::User );
308 }
309 else if (bIsMap)
310 {
311 RenderMapXSD( pRequest,
312 sTypeName,
313 id >= QMetaType::User );
314 }
315 else
316 {
317#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
318 const QMetaObject *pMetaObject = QMetaType::metaObjectForType(id);
319#else
320 const QMetaObject *pMetaObject = metaType.metaObject();
321#endif
322 if (pMetaObject)
323 {
324 QObject* pClass = pMetaObject->newInstance();
325 if (pClass != nullptr)
326 RenderXSD( pRequest, pClass );
327 delete pClass;
328 }
329 }
330
331 // Create the XML result
332 auto data = MythHTTPData::Create(toByteArray());
333 data->m_mimeType = MythMimeDatabase::MimeTypeForName("application/xml");
334 data->m_cacheType = HTTPETag | HTTPShortLife;
335 return MythHTTPResponse::DataResponse(pRequest, data);
336}
337
339//
340//<?xml version="1.0" encoding="UTF-8"?>
341//<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
342// xmlns:tns="http://mythtv.org"
343// targetNamespace="http://mythtv.org"
344// elementFormDefault="qualified"
345// attributeFormDefault="unqualified">
346// <xs:include schemaLocation="<path to dependant schema"/>
347//
348// <xs:complexType name="<className>">
349// <xs:annotation>
350// <xs:documentation>Comment describing your root element</xs:documentation>
351// </xs:annotation>
352// <xs:sequence>
353// <xs:element minOccurs="0" name="<propName>" type="<propType>"/>
354// <xs:element minOccurs="0" name="<childName>" nillable="true" type="tns:<ChildType>"/>
355// </xs:sequence>
356// </xs:complexType>
357// <xs:element name="<className>" nillable="true" type="tns:<className>"/>
358//
359//</xs:schema>
361
362bool MythXSD::RenderXSD( const HTTPRequest2& pRequest, QObject *pClass )
363{
364 const QMetaObject *pMetaObject = pClass->metaObject();
365
366 QString sClassName = ConvertTypeToXSD( pMetaObject->className(), true);
367 QDomElement oRoot = CreateSchemaRoot();
368
369 QMap<QString, TypeInfo> typesToInclude;
370
371 // ------------------------------------------------------------------
372 // Create xs:complexType structure
373 // ------------------------------------------------------------------
374
375 QDomElement oTypeNode = createElement( "xs:complexType" );
376 QDomElement oSeqNode = createElement( "xs:sequence" );
377
378 oTypeNode.setAttribute( "name", sClassName );
379
380 oTypeNode.appendChild( oSeqNode );
381
382 // -=>TODO: Add an xs:annotation node with class descriptions
383
384 // ------------------------------------------------------------------
385 // Add all properties for this type
386 //
387 // <xs:element minOccurs="0" name="<propName>" type="<propType>"/>
388 // <xs:element minOccurs="0" name="<childName>" nillable="true"
389 // type="tns:<ChildType>"/>
390 // ------------------------------------------------------------------
391
392 int nCount = pMetaObject->propertyCount();
393
394 for (int nIdx=0; nIdx < nCount; ++nIdx )
395 {
396 QMetaProperty metaProperty = pMetaObject->property( nIdx );
397
398 if (metaProperty.isDesignable())
399 {
400 const char *pszPropName = metaProperty.name();
401 QString sPropName( pszPropName );
402
403 if ( sPropName.compare( "objectName" ) == 0)
404 continue;
405
406 // ----------------------------------------------------------
407 // Create xs:element for this property
408 // ----------------------------------------------------------
409
410 QDomElement oNode = createElement( "xs:element" );
411 QString sType = metaProperty.typeName();
412 bool bCustomType = false;
413 QString sCustomAttr = "type";
414 QString sContentName = QString();
415 QString sContentType = QString();
416
417 LOG(VB_UPNP, LOG_DEBUG, QString( "Type: %1").arg( sType ));
418
419 // if this is a child object, sType will be QObject*
420 // which we can't use, so we need to read the
421 // properties value, and read it's metaObject data
422
423 if (sType == "QObject*")
424 {
425 QVariant val = metaProperty.read( pClass );
426 const QObject *pObject = val.value< QObject* >();
427
428 sType = pObject->metaObject()->className();
429 bCustomType = true;
430 }
431 else if ((sType == "QVariantList" ) || (sType == "QVariantMap"))
432 {
433 sContentType = ReadPropertyMetadata( pClass,
434 sPropName,
435 "type" );
436
437 if (sContentType.startsWith('Q'))
438 sContentType = sContentType.mid( 1 );
439
440 if (sContentType.startsWith("V2"))
441 sContentType.remove(0,2);
442 sContentType.remove( QChar('*'));
443
444 if (sType == "QVariantMap")
445 {
446 sContentName = ReadPropertyMetadata( pClass,
447 sPropName,
448 "name" );
449
450 if (sContentName.isEmpty())
451 sContentName = sContentType;
452
453 sType = "MapOfString" + sContentName;
454 }
455 else
456 {
457 sType = "ArrayOf" + sContentType;
458 }
459
460 bCustomType = true;
461
462 }
463 else if (sType == "QStringList")
464 {
465 sType = "ArrayOfString";
466 bCustomType = true;
467 }
468 else if (IsEnum( metaProperty, sType ))
469 {
470 // The code for creating the enum in xsd does not work
471 // Since there is only one enum, treat it as a string
472 // sCustomAttr = "enum";
473 // if (sType.startsWith("V2"))
474 // sType.remove(0,2);
475 // // if sType still contains "::", then no need to prefix with sClassName
476 // if (sType.contains( "::" ))
477 // sType = sType.replace( "::", "." );
478 // else
479 // sType = sClassName + "." + sType;
480 // bCustomType = true;
481 sType="string";
482 }
483
484 QString sNewPropName( metaProperty.name() );
485
486 if (IsNillable( sType ))
487 oNode.setAttribute( "nillable" , static_cast<int>(true) );
488
489 if (bCustomType)
490 {
491 TypeInfo info = { .sAttrName=sCustomAttr,
492 .sContentType=sContentType };
493 typesToInclude.insert( sType, info );
494 }
495
496 oNode.setAttribute( "type" , (bCustomType ? "tns:" : "xs:") +
497 ConvertTypeToXSD( sType, bCustomType ));
498
499 oNode.setAttribute( "name" , sNewPropName );
500 oNode.setAttribute( "minOccurs", 0 );
501
502 oSeqNode.appendChild( oNode );
503 }
504 }
505
506 // ------------------------------------------------------------------
507 // Create element for class
508 //
509 // <xs:element name="<className>" nillable="true" type="tns:<className>"/>
510 // ------------------------------------------------------------------
511
512 QDomElement oElementNode = createElement( "xs:element" );
513
514 oElementNode.setAttribute( "type" , "tns:" + sClassName );
515 oElementNode.setAttribute( "nillable", "true" );
516 oElementNode.setAttribute( "name" , sClassName );
517
518 // ----------------------------------------------------------------------
519 // Build xml tree...
520 // ----------------------------------------------------------------------
521
522 appendChild( oRoot );
523
524 if (typesToInclude.count() > 0)
525 {
526 // ------------------------------------------------------------------
527 // Create all needed includes
528 //
529 // <xs:include schemaLocation="<path to dependant schema"/>
530 // ------------------------------------------------------------------
531
532 // QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) +
533 // pRequest->m_sResourceUrl;
534 QString sBaseUri("http://" +
535 pRequest->m_headers->value("host") +
536 pRequest->m_path + "xsd");
537 QMap<QString, TypeInfo >::const_iterator it = typesToInclude.constBegin();
538 while( it != typesToInclude.constEnd())
539 {
540 QDomElement oIncNode = createElement( "xs:include" );
541 QString sType = it.key();
542
543 if (sType.startsWith("V2"))
544 sType.remove(0,2);
545
546 TypeInfo info = it.value();
547
548 QString sValue = QString( "%1?%2=%3" ).arg( sBaseUri,
549 info.sAttrName,
550 sType );
551
552 if (!info.sContentType.isEmpty())
553 sValue += "&name=" + info.sContentType;
554
555 oIncNode.setAttribute( "schemaLocation", sValue );
556
557 oRoot.appendChild( oIncNode );
558 ++it;
559 }
560 }
561
562 oRoot.appendChild( oTypeNode );
563 oRoot.appendChild( oElementNode );
564
565 // ----------------------------------------------------------------------
566 // Return xsd doc to caller
567 // ----------------------------------------------------------------------
568
569 // QTextStream os( &(pRequest->m_response) );
570
571 // pRequest->m_eResponseType = ResponseTypeXML;
572
573 // save( os, 0 );
574
575 return true;
576}
577
578bool MythXSD::IsEnum( const QMetaProperty &metaProperty, const QString &/*sType*/ )
579{
580 return (metaProperty.isEnumType() || metaProperty.isFlagType() );
581}
582
584//
585// ArrayOf<Type>
586//
587//<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://mythtv.org" targetNamespace="http://mythtv.org" elementFormDefault="qualified" attributeFormDefault="unqualified">
588// <xs:include schemaLocation="<type>.xsd"/>
589//
590// <xs:complexType name="ArrayOf<Type>">
591// <xs:annotation>
592// <xs:documentation>Comment describing your root element</xs:documentation>
593// </xs:annotation>
594// <xs:sequence>
595// <xs:element minOccurs="0" maxOccurs="unbounded" name="<type>" nillable="true" type="tns:<Type>"/>
596// </xs:sequence>
597// </xs:complexType>
598// <xs:element name="ArrayOf<Type>" nillable="true" type="tns:ArrayOf<Type>"/>
599//
600//</xs:schema>
601//
603
605 const QString &sClassName,
606 bool bCustomType )
607{
608 QString sArrayName = "ArrayOf" + sClassName;
609 QString sType;
610
611 if (bCustomType)
612 sType = "tns:" + sClassName;
613 else
614 sType = "xs:" + ConvertTypeToXSD( sClassName, false );
615
616 QDomElement oRoot = CreateSchemaRoot();
617
618 // ------------------------------------------------------------------
619 // Create xs:complexType structure
620 // ------------------------------------------------------------------
621
622 QDomElement oTypeNode = createElement( "xs:complexType" );
623 QDomElement oSeqNode = createElement( "xs:sequence" );
624
625 // -=>TODO: Add an xs:annotation node with class descriptions
626
627 oTypeNode.setAttribute( "name", sArrayName );
628 oTypeNode.appendChild( oSeqNode );
629
630 QDomElement oNode = createElement( "xs:element" );
631
632 oNode.setAttribute( "type" , sType );
633 oNode.setAttribute( "nillable" , "true" );
634 oNode.setAttribute( "name" , sClassName );
635 oNode.setAttribute( "maxOccurs" , "unbounded" );
636 oNode.setAttribute( "minOccurs" , "0" );
637
638 oSeqNode.appendChild( oNode );
639
640 // ----------------------------------------------------------------------
641 //
642 // ----------------------------------------------------------------------
643
644 QDomElement oElementNode = createElement( "xs:element" );
645
646 oElementNode.setAttribute( "type" , "tns:" + sArrayName );
647 oElementNode.setAttribute( "nillable", "true" );
648 oElementNode.setAttribute( "name" , sArrayName );
649
650 // ----------------------------------------------------------------------
651 // Build xml tree...
652 // ----------------------------------------------------------------------
653
654 appendChild( oRoot );
655
656 if (bCustomType)
657 {
658 QDomElement oIncNode = createElement( "xs:include" );
659
660 // QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) +
661 // pRequest->m_sResourceUrl + "?type=";
662 QString sBaseUri("http://" +
663 pRequest->m_headers->value("host") +
664 pRequest->m_path + "xsd" + "?type=");
665
666 oIncNode.setAttribute( "schemaLocation", sBaseUri + sClassName );
667
668 oRoot.appendChild( oIncNode );
669 }
670
671 oRoot.appendChild( oTypeNode );
672 oRoot.appendChild( oElementNode );
673
674 // ----------------------------------------------------------------------
675 // Return xsd doc to caller
676 // ----------------------------------------------------------------------
677
678 // QTextStream os( &(pRequest->m_response) );
679
680 // pRequest->m_eResponseType = ResponseTypeXML;
681
682 // save( os, 0 );
683
684 return true;
685
686}
687
689//
690// MapOfString<Type>
691//
692//<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://mythtv.org" targetNamespace="http://mythtv.org" elementFormDefault="qualified" attributeFormDefault="unqualified">
693// <xs:include schemaLocation="<type>.xsd"/>
694//
695//<xs:complexType name="MapOfString<Type>">
696// <xs:annotation>
697// <xs:appinfo>
698// <IsDictionary xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</IsDictionary>
699// </xs:appinfo>
700// </xs:annotation>
701// <xs:sequence>
702// <xs:element minOccurs="0" maxOccurs="unbounded" name="KeyValueOfString<Type>">
703// <xs:complexType>
704// <xs:sequence>
705// <xs:element name="Key" nillable="true" type="xs:string" />
706// <xs:element name="Value" nillable="true" type="<Type>" />
707// </xs:sequence>
708// </xs:complexType>
709// </xs:element>
710// </xs:sequence>
711//</xs:complexType>
712//<xs:element name="MapOfString<Type>" nillable="true" type="tns:MapOfString<Type>" />
713//
714//</xs:schema>
715//
717
719 const QString &sClassName,
720 bool bCustomType )
721{
722 QString sArrayName = "MapOfString" + sClassName;
723 // QString sMapItemName = pRequest->m_mapParams[ "name" ];
724 QString sMapItemName = pRequest->m_queries.value("name");
725
726 QString sType;
727
728 if (sMapItemName.isEmpty())
729 sMapItemName = sClassName;
730
731 if (bCustomType)
732 sType = "tns:" + sMapItemName;
733 else
734 sType = "xs:" + ConvertTypeToXSD( sMapItemName, false );
735
736 QDomElement oRoot = CreateSchemaRoot();
737
738 // ------------------------------------------------------------------
739 // Create xs:complexType structure
740 // ------------------------------------------------------------------
741
742 QDomElement oTypeNode = createElement( "xs:complexType" );
743 QDomElement oSeqNode = createElement( "xs:sequence" );
744 QDomElement oAnno = createElement( "xs:annotation" );
745 QDomElement oAppInfo = createElement( "xs:appinfo" );
746
747 QDomElement oNode = createElement( "IsDictionary" );
748 oNode.setAttribute( "xmlns", "http://schemas.microsoft.com/2003/10/Serialization/" );
749 oNode.appendChild( createTextNode( "true" ));
750
751 oTypeNode.appendChild( oAnno );
752 oAnno .appendChild( oAppInfo );
753 oAppInfo .appendChild( oNode );
754
755 // -=>TODO: Add an xs:annotation node with class descriptions
756
757 // ----------------------------------------------------------------------
758 // <xs:sequence>
759 // ----------------------------------------------------------------------
760
761 oTypeNode.setAttribute( "name", sArrayName );
762 oTypeNode.appendChild( oSeqNode );
763
764 // ----------------------------------------------------------------------
765 // <xs:element minOccurs="0" maxOccurs="unbounded" name="KeyValueOfString<Type>">
766 // ----------------------------------------------------------------------
767
768 QDomElement oInnerNode = createElement( "xs:element" );
769
770 oInnerNode.setAttribute( "name" , sClassName );
771 oInnerNode.setAttribute( "maxOccurs" , "unbounded" );
772 oInnerNode.setAttribute( "minOccurs" , "0" );
773
774 oSeqNode.appendChild( oInnerNode );
775
776 // ----------------------------------------------------------------------
777 // <xs:complexType>
778 // ----------------------------------------------------------------------
779 oNode = createElement( "xs:complexType" );
780
781 oInnerNode.appendChild( oNode );
782
783 // ----------------------------------------------------------------------
784 // <xs:sequence>
785 // ----------------------------------------------------------------------
786
787 QDomElement oInnerSeq = createElement( "xs:sequence" );
788
789 oNode.appendChild( oInnerSeq );
790
791 // ----------------------------------------------------------------------
792 // <xs:element name="Key" nillable="true" type="xs:string" />
793 // ----------------------------------------------------------------------
794
795 oNode = createElement( "xs:element" );
796
797 oNode.setAttribute( "type" , "xs:string" );
798 oNode.setAttribute( "nillable" , "true" );
799 oNode.setAttribute( "name" , "Key" );
800
801 oInnerSeq.appendChild( oNode );
802
803 // ----------------------------------------------------------------------
804 // <xs:element name="Value" nillable="true" type="<Type>" />
805 // ----------------------------------------------------------------------
806
807 oNode = createElement( "xs:element" );
808
809 oNode.setAttribute( "type" , sType );
810 oNode.setAttribute( "nillable" , "true" );
811 oNode.setAttribute( "name" , "Value" );
812
813 oInnerSeq.appendChild( oNode );
814
815 // ----------------------------------------------------------------------
816 //<xs:element name="MapOfString<Type>" nillable="true" type="tns:MapOfString<Type>" />
817 // ----------------------------------------------------------------------
818
819 QDomElement oElementNode = createElement( "xs:element" );
820
821 oElementNode.setAttribute( "type" , "tns:" + sArrayName );
822 oElementNode.setAttribute( "nillable", "true" );
823 oElementNode.setAttribute( "name" , sArrayName );
824
825 // ----------------------------------------------------------------------
826 // Build xml tree...
827 // ----------------------------------------------------------------------
828
829 appendChild( oRoot );
830
831 if (bCustomType)
832 {
833 QDomElement oIncNode = createElement( "xs:include" );
834
835 // QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) + pRequest->m_sResourceUrl + "?type=";
836 QString sBaseUri("http://" +
837 pRequest->m_headers->value("host") +
838 pRequest->m_path + "xsd" + "?type=");
839
840 oIncNode.setAttribute( "schemaLocation", sBaseUri + sClassName );
841
842 oRoot.appendChild( oIncNode );
843 }
844
845 oRoot.appendChild( oTypeNode );
846 oRoot.appendChild( oElementNode );
847
848 // ----------------------------------------------------------------------
849 // Return xsd doc to caller
850 // ----------------------------------------------------------------------
851
852 // QTextStream os( &(pRequest->m_response) );
853
854 // pRequest->m_eResponseType = ResponseTypeXML;
855
856 // save( os, 0 );
857
858 return true;
859
860}
861
863//
865
867{
868 QString sTargetNamespace = "http://mythtv.org";
869
870 QDomElement oRoot = createElement( "xs:schema" );
871
872 oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema");
873 oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
874 oRoot.setAttribute( "targetNamespace" , sTargetNamespace );
875 oRoot.setAttribute( "elementFormDefault" , "qualified" );
876 oRoot.setAttribute( "attributeFormDefault", "unqualified" );
877 return oRoot;
878}
879
881//
883
884bool MythXSD::IsNillable( const QString &sType )
885{
886 if (sType.startsWith( "V2"))
887 return true;
888
889 if (sType == "QDateTime")
890 return true;
891
892 if (sType == "QDate")
893 return true;
894
895 if (sType == "QTime")
896 return true;
897
898 return false;
899}
900
902//
904
905QString MythXSD::ConvertTypeToXSD( const QString &sType, bool bCustomType )
906{
907 if (bCustomType || sType.startsWith( "V2"))
908 {
909 QString sTypeName( sType );
910
911 if (sTypeName.startsWith("V2"))
912 sTypeName.remove(0,2);
913 sTypeName.remove( QChar('*') );
914
915 if (sType == "QStringList" )
916 return "ArrayOfString";
917
918 return sTypeName;
919 }
920
921 if (sType == "QDateTime")
922 return "dateTime";
923
924 if (sType == "QDate")
925 return "date";
926
927 if (sType == "QTime")
928 return "time";
929
930 if (sType == "bool")
931 return "boolean";
932
933 if (sType == "uint")
934 return "unsignedInt";
935
936 if (sType == "qlonglong" )
937 return "long";
938
939 if (sType == "std::chrono::seconds" )
940 return "long";
941
942 if (sType == "qulonglong" )
943 return "unsignedLong";
944
945 if (sType == "QJsonObject" )
946 return "string";
947
948 if (sType == "long long" )
949 return "long";
950
951 if (sType == "QFileInfo" )
952 return "string"; // temp solution
953
954 if (sType.startsWith('Q'))
955 return sType.mid( 1 ).toLower();
956
957 return sType.toLower();
958}
959
961//
963
964QString MythXSD::ReadPropertyMetadata( QObject *pObject, const QString& sPropName, const QString& sKey )
965{
966 const QMetaObject *pMeta = pObject->metaObject();
967
968 int nIdx = -1;
969
970 if (pMeta)
971 nIdx = pMeta->indexOfClassInfo( sPropName.toUtf8().constData() );
972
973 if (nIdx >=0)
974 {
975 QString sMetadata = pMeta->classInfo( nIdx ).value();
976 QStringList sOptions = sMetadata.split( ';' );
977
978 QString sFullKey = sKey + "=";
979
980 auto hasKey = [&sFullKey](const QString& o) { return o.startsWith( sFullKey ); };
981 auto it = std::ranges::find_if(std::as_const(sOptions), hasKey);
982 if (it != sOptions.cend())
983 return (*it).mid( sFullKey.length() );
984 }
985
986 return {};
987}
988
989HTTPResponse MythXSD::Error(const HTTPRequest2& pRequest, const QString &msg)
990{
991 LOG(VB_GENERAL, LOG_ERR, "MythCSD Exception: " + msg);
992 pRequest->m_status = HTTPBadRequest;
993 return MythHTTPResponse::ErrorResponse(pRequest, msg);
994}
static HTTPData Create()
Definition: mythhttpdata.cpp:4
static HTTPResponse ErrorResponse(MythHTTPStatus Status, const QString &ServerName)
static HTTPResponse DataResponse(const HTTPRequest2 &Request, const HTTPData &Data)
static MythMimeType MimeTypeForName(const QString &Name)
Return a mime type that matches the given name.
static HTTPResponse Error(const HTTPRequest2 &pRequest, const QString &msg)
Definition: mythxsd.cpp:989
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: mythxsd.cpp:905
HTTPResponse GetXSD(const HTTPRequest2 &pRequest, QString sTypeName)
Definition: mythxsd.cpp:214
bool RenderXSD(const HTTPRequest2 &pRequest, QObject *pClass)
Definition: mythxsd.cpp:362
QDomElement CreateSchemaRoot()
Definition: mythxsd.cpp:866
static QString ReadPropertyMetadata(QObject *pObject, const QString &sPropName, const QString &sKey)
Definition: mythxsd.cpp:964
static bool IsEnum(const QMetaProperty &metaProperty, const QString &sType)
Definition: mythxsd.cpp:578
bool RenderMapXSD(const HTTPRequest2 &pRequest, const QString &sClassName, bool bCustomType)
Definition: mythxsd.cpp:718
bool RenderArrayXSD(const HTTPRequest2 &pRequest, const QString &sClassName, bool bCustomType)
Definition: mythxsd.cpp:604
static bool IsNillable(const QString &sType)
Definition: mythxsd.cpp:884
@ HTTPBadRequest
std::shared_ptr< MythHTTPRequest > HTTPRequest2
Definition: mythhttptypes.h:39
std::shared_ptr< MythHTTPResponse > HTTPResponse
Definition: mythhttptypes.h:40
@ HTTPETag
@ HTTPShortLife
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
dictionary info
Definition: azlyrics.py:7