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