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