17#include <QCoreApplication>
216 bool bIsArray =
false;
219 if (sTypeName.isEmpty())
220 return Error(pRequest, QString(
"XSD request on empty name" ));
227 if (sTypeName.startsWith(
"ArrayOf" ))
230 sTypeName = sTypeName.mid( 7 );
233 if (sTypeName.startsWith(
"MapOfString" ))
236 sTypeName = sTypeName.mid( 11 );
243#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
249 if (
id == QMetaType::UnknownType)
251 QString sFQN = sTypeName +
"*";
260 if (
id == QMetaType::UnknownType)
262 QString sFQN =
"V2" + sTypeName;
265 if (
id == QMetaType::UnknownType)
267 QString sFQN =
"V2" + sTypeName +
"*";
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() +
"*" );
281 int id = metaType.id();
288 if (!(bIsArray || bIsMap) && (
id < QMetaType::User))
289 return Error(pRequest,QString(
"XSD request on unsupported type %1").arg(sTypeName));
299 if (!pRequest->m_queries.contains(
"raw" ))
301 appendChild( createProcessingInstruction(
"xml-stylesheet",
302 R
"(type="text/xsl" href="/xslt/class.xslt")" ));
313 id >= QMetaType::User );
317#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
318 const QMetaObject *pMetaObject = QMetaType::metaObjectForType(
id);
320 const QMetaObject *pMetaObject = metaType.metaObject();
324 QObject* pClass = pMetaObject->newInstance();
325 if (pClass !=
nullptr)
364 const QMetaObject *pMetaObject = pClass->metaObject();
369 QMap<QString, TypeInfo> typesToInclude;
375 QDomElement oTypeNode = createElement(
"xs:complexType" );
376 QDomElement oSeqNode = createElement(
"xs:sequence" );
378 oTypeNode.setAttribute(
"name", sClassName );
380 oTypeNode.appendChild( oSeqNode );
392 int nCount = pMetaObject->propertyCount();
394 for (
int nIdx=0; nIdx < nCount; ++nIdx )
396 QMetaProperty metaProperty = pMetaObject->property( nIdx );
398 if (metaProperty.isDesignable())
400 const char *pszPropName = metaProperty.name();
401 QString sPropName( pszPropName );
403 if ( sPropName.compare(
"objectName" ) == 0)
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();
417 LOG(VB_UPNP, LOG_DEBUG, QString(
"Type: %1").arg( sType ));
423 if (sType ==
"QObject*")
425 QVariant val = metaProperty.read( pClass );
426 const QObject *pObject = val.value< QObject* >();
428 sType = pObject->metaObject()->className();
431 else if ((sType ==
"QVariantList" ) || (sType ==
"QVariantMap"))
437 if (sContentType.startsWith(
'Q'))
438 sContentType = sContentType.mid( 1 );
440 if (sContentType.startsWith(
"V2"))
441 sContentType.remove(0,2);
442 sContentType.remove( QChar(
'*'));
444 if (sType ==
"QVariantMap")
450 if (sContentName.isEmpty())
451 sContentName = sContentType;
453 sType =
"MapOfString" + sContentName;
457 sType =
"ArrayOf" + sContentType;
463 else if (sType ==
"QStringList")
465 sType =
"ArrayOfString";
468 else if (
IsEnum( metaProperty, sType ))
484 QString sNewPropName( metaProperty.name() );
487 oNode.setAttribute(
"nillable" ,
static_cast<int>(
true) );
492 .sContentType=sContentType };
493 typesToInclude.insert( sType,
info );
496 oNode.setAttribute(
"type" , (bCustomType ?
"tns:" :
"xs:") +
499 oNode.setAttribute(
"name" , sNewPropName );
500 oNode.setAttribute(
"minOccurs", 0 );
502 oSeqNode.appendChild( oNode );
512 QDomElement oElementNode = createElement(
"xs:element" );
514 oElementNode.setAttribute(
"type" ,
"tns:" + sClassName );
515 oElementNode.setAttribute(
"nillable",
"true" );
516 oElementNode.setAttribute(
"name" , sClassName );
522 appendChild( oRoot );
524 if (typesToInclude.count() > 0)
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())
540 QDomElement oIncNode = createElement(
"xs:include" );
541 QString sType = it.key();
543 if (sType.startsWith(
"V2"))
548 QString sValue = QString(
"%1?%2=%3" ).arg( sBaseUri,
552 if (!
info.sContentType.isEmpty())
553 sValue +=
"&name=" +
info.sContentType;
555 oIncNode.setAttribute(
"schemaLocation", sValue );
557 oRoot.appendChild( oIncNode );
562 oRoot.appendChild( oTypeNode );
563 oRoot.appendChild( oElementNode );
580 return (metaProperty.isEnumType() || metaProperty.isFlagType() );
605 const QString &sClassName,
608 QString sArrayName =
"ArrayOf" + sClassName;
612 sType =
"tns:" + sClassName;
622 QDomElement oTypeNode = createElement(
"xs:complexType" );
623 QDomElement oSeqNode = createElement(
"xs:sequence" );
627 oTypeNode.setAttribute(
"name", sArrayName );
628 oTypeNode.appendChild( oSeqNode );
630 QDomElement oNode = createElement(
"xs:element" );
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" );
638 oSeqNode.appendChild( oNode );
644 QDomElement oElementNode = createElement(
"xs:element" );
646 oElementNode.setAttribute(
"type" ,
"tns:" + sArrayName );
647 oElementNode.setAttribute(
"nillable",
"true" );
648 oElementNode.setAttribute(
"name" , sArrayName );
654 appendChild( oRoot );
658 QDomElement oIncNode = createElement(
"xs:include" );
662 QString sBaseUri(
"http://" +
663 pRequest->m_headers->value(
"host") +
664 pRequest->m_path +
"xsd" +
"?type=");
666 oIncNode.setAttribute(
"schemaLocation", sBaseUri + sClassName );
668 oRoot.appendChild( oIncNode );
671 oRoot.appendChild( oTypeNode );
672 oRoot.appendChild( oElementNode );
719 const QString &sClassName,
722 QString sArrayName =
"MapOfString" + sClassName;
724 QString sMapItemName = pRequest->m_queries.value(
"name");
728 if (sMapItemName.isEmpty())
729 sMapItemName = sClassName;
732 sType =
"tns:" + sMapItemName;
742 QDomElement oTypeNode = createElement(
"xs:complexType" );
743 QDomElement oSeqNode = createElement(
"xs:sequence" );
744 QDomElement oAnno = createElement(
"xs:annotation" );
745 QDomElement oAppInfo = createElement(
"xs:appinfo" );
747 QDomElement oNode = createElement(
"IsDictionary" );
748 oNode.setAttribute(
"xmlns",
"http://schemas.microsoft.com/2003/10/Serialization/" );
749 oNode.appendChild( createTextNode(
"true" ));
751 oTypeNode.appendChild( oAnno );
752 oAnno .appendChild( oAppInfo );
753 oAppInfo .appendChild( oNode );
761 oTypeNode.setAttribute(
"name", sArrayName );
762 oTypeNode.appendChild( oSeqNode );
768 QDomElement oInnerNode = createElement(
"xs:element" );
770 oInnerNode.setAttribute(
"name" , sClassName );
771 oInnerNode.setAttribute(
"maxOccurs" ,
"unbounded" );
772 oInnerNode.setAttribute(
"minOccurs" ,
"0" );
774 oSeqNode.appendChild( oInnerNode );
779 oNode = createElement(
"xs:complexType" );
781 oInnerNode.appendChild( oNode );
787 QDomElement oInnerSeq = createElement(
"xs:sequence" );
789 oNode.appendChild( oInnerSeq );
795 oNode = createElement(
"xs:element" );
797 oNode.setAttribute(
"type" ,
"xs:string" );
798 oNode.setAttribute(
"nillable" ,
"true" );
799 oNode.setAttribute(
"name" ,
"Key" );
801 oInnerSeq.appendChild( oNode );
807 oNode = createElement(
"xs:element" );
809 oNode.setAttribute(
"type" , sType );
810 oNode.setAttribute(
"nillable" ,
"true" );
811 oNode.setAttribute(
"name" ,
"Value" );
813 oInnerSeq.appendChild( oNode );
819 QDomElement oElementNode = createElement(
"xs:element" );
821 oElementNode.setAttribute(
"type" ,
"tns:" + sArrayName );
822 oElementNode.setAttribute(
"nillable",
"true" );
823 oElementNode.setAttribute(
"name" , sArrayName );
829 appendChild( oRoot );
833 QDomElement oIncNode = createElement(
"xs:include" );
836 QString sBaseUri(
"http://" +
837 pRequest->m_headers->value(
"host") +
838 pRequest->m_path +
"xsd" +
"?type=");
840 oIncNode.setAttribute(
"schemaLocation", sBaseUri + sClassName );
842 oRoot.appendChild( oIncNode );
845 oRoot.appendChild( oTypeNode );
846 oRoot.appendChild( oElementNode );
868 QString sTargetNamespace =
"http://mythtv.org";
870 QDomElement oRoot = createElement(
"xs:schema" );
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" );
886 if (sType.startsWith(
"V2"))
889 if (sType ==
"QDateTime")
892 if (sType ==
"QDate")
895 if (sType ==
"QTime")
907 if (bCustomType || sType.startsWith(
"V2"))
909 QString sTypeName( sType );
911 if (sTypeName.startsWith(
"V2"))
912 sTypeName.remove(0,2);
913 sTypeName.remove( QChar(
'*') );
915 if (sType ==
"QStringList" )
916 return "ArrayOfString";
921 if (sType ==
"QDateTime")
924 if (sType ==
"QDate")
927 if (sType ==
"QTime")
934 return "unsignedInt";
936 if (sType ==
"qlonglong" )
939 if (sType ==
"std::chrono::seconds" )
942 if (sType ==
"qulonglong" )
943 return "unsignedLong";
945 if (sType ==
"QJsonObject" )
948 if (sType ==
"long long" )
951 if (sType ==
"QFileInfo" )
954 if (sType.startsWith(
'Q'))
955 return sType.mid( 1 ).toLower();
957 return sType.toLower();
966 const QMetaObject *pMeta = pObject->metaObject();
971 nIdx = pMeta->indexOfClassInfo( sPropName.toUtf8().constData() );
975 QString sMetadata = pMeta->classInfo( nIdx ).value();
976 QStringList sOptions = sMetadata.split(
';' );
978 QString sFullKey = sKey +
"=";
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() );
991 LOG(VB_GENERAL, LOG_ERR,
"MythCSD Exception: " + msg);
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)
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
HTTPResponse GetXSD(const HTTPRequest2 &pRequest, QString sTypeName)
bool RenderXSD(const HTTPRequest2 &pRequest, QObject *pClass)
QDomElement CreateSchemaRoot()
static QString ReadPropertyMetadata(QObject *pObject, const QString &sPropName, const QString &sKey)
static bool IsEnum(const QMetaProperty &metaProperty, const QString &sType)
bool RenderMapXSD(const HTTPRequest2 &pRequest, const QString &sClassName, bool bCustomType)
bool RenderArrayXSD(const HTTPRequest2 &pRequest, const QString &sClassName, bool bCustomType)
static bool IsNillable(const QString &sType)
std::shared_ptr< MythHTTPRequest > HTTPRequest2
std::shared_ptr< MythHTTPResponse > HTTPResponse
#define LOG(_MASK_, _LEVEL_, _QSTRING_)