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  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  sType = "ArrayOf" + sContentType;
436 
437  bCustomType = true;
438 
439  }
440  else if (sType == "QStringList")
441  {
442  sType = "ArrayOfString";
443  bCustomType = true;
444  }
445  else if (IsEnum( metaProperty, sType ))
446  {
447  sCustomAttr = "enum";
448 
449  sType.remove( "DTC::" );
450 
451  // if sType still contains "::", then no need to prefix with sClassName
452 
453  if (sType.contains( "::" ))
454  sType = sType.replace( "::", "." );
455  else
456  sType = sClassName + "." + sType;
457 
458  bCustomType = true;
459  }
460 
461  QString sNewPropName( metaProperty.name() );
462 
463  if (IsNillable( sType ))
464  oNode.setAttribute( "nillable" , static_cast<int>(true) );
465 
466  if (bCustomType)
467  {
468  TypeInfo info = { sCustomAttr, sContentType };
469  typesToInclude.insert( sType, info );
470  }
471 
472  oNode.setAttribute( "type" , (bCustomType ? "tns:" : "xs:") +
473  ConvertTypeToXSD( sType, bCustomType ));
474 
475  oNode.setAttribute( "name" , sNewPropName );
476  oNode.setAttribute( "minOccurs", 0 );
477 
478  oSeqNode.appendChild( oNode );
479  }
480  }
481 
482  // ------------------------------------------------------------------
483  // Create element for class
484  //
485  // <xs:element name="<className>" nillable="true" type="tns:<className>"/>
486  // ------------------------------------------------------------------
487 
488  QDomElement oElementNode = createElement( "xs:element" );
489 
490  oElementNode.setAttribute( "type" , "tns:" + sClassName );
491  oElementNode.setAttribute( "nillable", "true" );
492  oElementNode.setAttribute( "name" , sClassName );
493 
494  // ----------------------------------------------------------------------
495  // Build xml tree...
496  // ----------------------------------------------------------------------
497 
498  appendChild( oRoot );
499 
500  if (typesToInclude.count() > 0)
501  {
502  // ------------------------------------------------------------------
503  // Create all needed includes
504  //
505  // <xs:include schemaLocation="<path to dependant schema"/>
506  // ------------------------------------------------------------------
507 
508  QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) +
509  pRequest->m_sResourceUrl;
510 
511  QMap<QString, TypeInfo >::const_iterator it = typesToInclude.constBegin();
512  while( it != typesToInclude.constEnd())
513  {
514  QDomElement oIncNode = createElement( "xs:include" );
515  QString sType = it.key();
516 
517  sType.remove( "DTC::" );
518 
519  TypeInfo info = it.value();
520 
521  QString sValue = QString( "%1?%2=%3" ).arg( sBaseUri,
522  info.sAttrName,
523  sType );
524 
525  if (!info.sContentType.isEmpty())
526  sValue += "&name=" + info.sContentType;
527 
528  oIncNode.setAttribute( "schemaLocation", sValue );
529 
530  oRoot.appendChild( oIncNode );
531  ++it;
532  }
533  }
534 
535  oRoot.appendChild( oTypeNode );
536  oRoot.appendChild( oElementNode );
537 
538  // ----------------------------------------------------------------------
539  // Return xsd doc to caller
540  // ----------------------------------------------------------------------
541 
542  QTextStream os( &(pRequest->m_response) );
543 
544  pRequest->m_eResponseType = ResponseTypeXML;
545 
546  save( os, 0 );
547 
548  return true;
549 }
550 
551 bool Xsd::IsEnum( const QMetaProperty &metaProperty, const QString &/*sType*/ )
552 {
553  return (metaProperty.isEnumType() || metaProperty.isFlagType() );
554 }
555 
557 //
558 // ArrayOf<Type>
559 //
560 //<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://mythtv.org" targetNamespace="http://mythtv.org" elementFormDefault="qualified" attributeFormDefault="unqualified">
561 // <xs:include schemaLocation="<type>.xsd"/>
562 //
563 // <xs:complexType name="ArrayOf<Type>">
564 // <xs:annotation>
565 // <xs:documentation>Comment describing your root element</xs:documentation>
566 // </xs:annotation>
567 // <xs:sequence>
568 // <xs:element minOccurs="0" maxOccurs="unbounded" name="<type>" nillable="true" type="tns:<Type>"/>
569 // </xs:sequence>
570 // </xs:complexType>
571 // <xs:element name="ArrayOf<Type>" nillable="true" type="tns:ArrayOf<Type>"/>
572 //
573 //</xs:schema>
574 //
576 
578  const QString &sClassName,
579  bool bCustomType )
580 {
581  QString sArrayName = "ArrayOf" + sClassName;
582  QString sType;
583 
584  if (bCustomType)
585  sType = "tns:" + sClassName;
586  else
587  sType = "xs:" + ConvertTypeToXSD( sClassName, false );
588 
589  QDomElement oRoot = CreateSchemaRoot();
590 
591  // ------------------------------------------------------------------
592  // Create xs:complexType structure
593  // ------------------------------------------------------------------
594 
595  QDomElement oTypeNode = createElement( "xs:complexType" );
596  QDomElement oSeqNode = createElement( "xs:sequence" );
597 
598  // -=>TODO: Add an xs:annotation node with class descriptions
599 
600  oTypeNode.setAttribute( "name", sArrayName );
601  oTypeNode.appendChild( oSeqNode );
602 
603  QDomElement oNode = createElement( "xs:element" );
604 
605  oNode.setAttribute( "type" , sType );
606  oNode.setAttribute( "nillable" , "true" );
607  oNode.setAttribute( "name" , sClassName );
608  oNode.setAttribute( "maxOccurs" , "unbounded" );
609  oNode.setAttribute( "minOccurs" , "0" );
610 
611  oSeqNode.appendChild( oNode );
612 
613  // ----------------------------------------------------------------------
614  //
615  // ----------------------------------------------------------------------
616 
617  QDomElement oElementNode = createElement( "xs:element" );
618 
619  oElementNode.setAttribute( "type" , "tns:" + sArrayName );
620  oElementNode.setAttribute( "nillable", "true" );
621  oElementNode.setAttribute( "name" , sArrayName );
622 
623  // ----------------------------------------------------------------------
624  // Build xml tree...
625  // ----------------------------------------------------------------------
626 
627  appendChild( oRoot );
628 
629  if (bCustomType)
630  {
631  QDomElement oIncNode = createElement( "xs:include" );
632 
633  QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) +
634  pRequest->m_sResourceUrl + "?type=";
635 
636  oIncNode.setAttribute( "schemaLocation", sBaseUri + sClassName );
637 
638  oRoot.appendChild( oIncNode );
639  }
640 
641  oRoot.appendChild( oTypeNode );
642  oRoot.appendChild( oElementNode );
643 
644  // ----------------------------------------------------------------------
645  // Return xsd doc to caller
646  // ----------------------------------------------------------------------
647 
648  QTextStream os( &(pRequest->m_response) );
649 
650  pRequest->m_eResponseType = ResponseTypeXML;
651 
652  save( os, 0 );
653 
654  return true;
655 
656 }
657 
659 //
660 // MapOfString<Type>
661 //
662 //<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://mythtv.org" targetNamespace="http://mythtv.org" elementFormDefault="qualified" attributeFormDefault="unqualified">
663 // <xs:include schemaLocation="<type>.xsd"/>
664 //
665 //<xs:complexType name="MapOfString<Type>">
666 // <xs:annotation>
667 // <xs:appinfo>
668 // <IsDictionary xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</IsDictionary>
669 // </xs:appinfo>
670 // </xs:annotation>
671 // <xs:sequence>
672 // <xs:element minOccurs="0" maxOccurs="unbounded" name="KeyValueOfString<Type>">
673 // <xs:complexType>
674 // <xs:sequence>
675 // <xs:element name="Key" nillable="true" type="xs:string" />
676 // <xs:element name="Value" nillable="true" type="<Type>" />
677 // </xs:sequence>
678 // </xs:complexType>
679 // </xs:element>
680 // </xs:sequence>
681 //</xs:complexType>
682 //<xs:element name="MapOfString<Type>" nillable="true" type="tns:MapOfString<Type>" />
683 //
684 //</xs:schema>
685 //
687 
689  const QString &sClassName,
690  bool bCustomType )
691 {
692  QString sArrayName = "MapOfString" + sClassName;
693  QString sMapItemName = pRequest->m_mapParams[ "name" ];
694  QString sType;
695 
696  if (sMapItemName.isEmpty())
697  sMapItemName = sClassName;
698 
699  if (bCustomType)
700  sType = "tns:" + sMapItemName;
701  else
702  sType = "xs:" + ConvertTypeToXSD( sMapItemName, false );
703 
704  QDomElement oRoot = CreateSchemaRoot();
705 
706  // ------------------------------------------------------------------
707  // Create xs:complexType structure
708  // ------------------------------------------------------------------
709 
710  QDomElement oTypeNode = createElement( "xs:complexType" );
711  QDomElement oSeqNode = createElement( "xs:sequence" );
712  QDomElement oAnno = createElement( "xs:annotation" );
713  QDomElement oAppInfo = createElement( "xs:appinfo" );
714 
715  QDomElement oNode = createElement( "IsDictionary" );
716  oNode.setAttribute( "xmlns", "http://schemas.microsoft.com/2003/10/Serialization/" );
717  oNode.appendChild( createTextNode( "true" ));
718 
719  oTypeNode.appendChild( oAnno );
720  oAnno .appendChild( oAppInfo );
721  oAppInfo .appendChild( oNode );
722 
723  // -=>TODO: Add an xs:annotation node with class descriptions
724 
725  // ----------------------------------------------------------------------
726  // <xs:sequence>
727  // ----------------------------------------------------------------------
728 
729  oTypeNode.setAttribute( "name", sArrayName );
730  oTypeNode.appendChild( oSeqNode );
731 
732  // ----------------------------------------------------------------------
733  // <xs:element minOccurs="0" maxOccurs="unbounded" name="KeyValueOfString<Type>">
734  // ----------------------------------------------------------------------
735 
736  QDomElement oInnerNode = createElement( "xs:element" );
737 
738  oInnerNode.setAttribute( "name" , sClassName );
739  oInnerNode.setAttribute( "maxOccurs" , "unbounded" );
740  oInnerNode.setAttribute( "minOccurs" , "0" );
741 
742  oSeqNode.appendChild( oInnerNode );
743 
744  // ----------------------------------------------------------------------
745  // <xs:complexType>
746  // ----------------------------------------------------------------------
747  oNode = createElement( "xs:complexType" );
748 
749  oInnerNode.appendChild( oNode );
750 
751  // ----------------------------------------------------------------------
752  // <xs:sequence>
753  // ----------------------------------------------------------------------
754 
755  QDomElement oInnerSeq = createElement( "xs:sequence" );
756 
757  oNode.appendChild( oInnerSeq );
758 
759  // ----------------------------------------------------------------------
760  // <xs:element name="Key" nillable="true" type="xs:string" />
761  // ----------------------------------------------------------------------
762 
763  oNode = createElement( "xs:element" );
764 
765  oNode.setAttribute( "type" , "xs:string" );
766  oNode.setAttribute( "nillable" , "true" );
767  oNode.setAttribute( "name" , "Key" );
768 
769  oInnerSeq.appendChild( oNode );
770 
771  // ----------------------------------------------------------------------
772  // <xs:element name="Value" nillable="true" type="<Type>" />
773  // ----------------------------------------------------------------------
774 
775  oNode = createElement( "xs:element" );
776 
777  oNode.setAttribute( "type" , sType );
778  oNode.setAttribute( "nillable" , "true" );
779  oNode.setAttribute( "name" , "Value" );
780 
781  oInnerSeq.appendChild( oNode );
782 
783  // ----------------------------------------------------------------------
784  //<xs:element name="MapOfString<Type>" nillable="true" type="tns:MapOfString<Type>" />
785  // ----------------------------------------------------------------------
786 
787  QDomElement oElementNode = createElement( "xs:element" );
788 
789  oElementNode.setAttribute( "type" , "tns:" + sArrayName );
790  oElementNode.setAttribute( "nillable", "true" );
791  oElementNode.setAttribute( "name" , sArrayName );
792 
793  // ----------------------------------------------------------------------
794  // Build xml tree...
795  // ----------------------------------------------------------------------
796 
797  appendChild( oRoot );
798 
799  if (bCustomType)
800  {
801  QDomElement oIncNode = createElement( "xs:include" );
802 
803  QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) + pRequest->m_sResourceUrl + "?type=";
804 
805  oIncNode.setAttribute( "schemaLocation", sBaseUri + sClassName );
806 
807  oRoot.appendChild( oIncNode );
808  }
809 
810  oRoot.appendChild( oTypeNode );
811  oRoot.appendChild( oElementNode );
812 
813  // ----------------------------------------------------------------------
814  // Return xsd doc to caller
815  // ----------------------------------------------------------------------
816 
817  QTextStream os( &(pRequest->m_response) );
818 
819  pRequest->m_eResponseType = ResponseTypeXML;
820 
821  save( os, 0 );
822 
823  return true;
824 
825 }
826 
828 //
830 
832 {
833  QString sTargetNamespace = "http://mythtv.org";
834 
835  QDomElement oRoot = createElement( "xs:schema" );
836 
837  oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema");
838  oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
839  oRoot.setAttribute( "targetNamespace" , sTargetNamespace );
840  oRoot.setAttribute( "elementFormDefault" , "qualified" );
841  oRoot.setAttribute( "attributeFormDefault", "unqualified" );
842  return oRoot;
843 }
844 
846 //
848 
849 bool Xsd::IsNillable( const QString &sType )
850 {
851  if (sType.startsWith( "DTC::"))
852  return true;
853 
854  if (sType == "QDateTime")
855  return true;
856 
857  if (sType == "QDate")
858  return true;
859 
860  if (sType == "QTime")
861  return true;
862 
863  return false;
864 }
865 
867 //
869 
870 QString Xsd::ConvertTypeToXSD( const QString &sType, bool bCustomType )
871 {
872  if (bCustomType || sType.startsWith( "DTC::"))
873  {
874  QString sTypeName( sType );
875 
876  sTypeName.remove( "DTC::" );
877  sTypeName.remove( QChar('*') );
878 
879  if (sType == "QStringList" )
880  return "ArrayOfString";
881 
882  return sTypeName;
883  }
884 
885  if (sType == "QDateTime")
886  return "dateTime";
887 
888  if (sType == "QDate")
889  return "date";
890 
891  if (sType == "QTime")
892  return "time";
893 
894  if (sType == "bool")
895  return "boolean";
896 
897  if (sType == "uint")
898  return "unsignedInt";
899 
900  if (sType == "qlonglong" )
901  return "long";
902 
903  if (sType == "long long" )
904  return "long";
905 
906  if (sType == "QFileInfo" )
907  return "string"; // temp solution
908 
909  if (sType.startsWith('Q'))
910  return sType.mid( 1 ).toLower();
911 
912  return sType.toLower();
913 }
914 
916 //
918 
919 QString Xsd::ReadPropertyMetadata( QObject *pObject, const QString& sPropName, const QString& sKey )
920 {
921  const QMetaObject *pMeta = pObject->metaObject();
922 
923  int nIdx = -1;
924 
925  if (pMeta)
926  nIdx = pMeta->indexOfClassInfo( sPropName.toUtf8() );
927 
928  if (nIdx >=0)
929  {
930  QString sMetadata = pMeta->classInfo( nIdx ).value();
931  QStringList sOptions = sMetadata.split( ';' );
932 
933  QString sFullKey = sKey + "=";
934 
935  for (const QString& option : qAsConst(sOptions))
936  {
937  if (option.startsWith( sFullKey ))
938  return option.mid( sFullKey.length() );
939  }
940  }
941 
942  return {};
943 }
HTTPRequest
Definition: httprequest.h:109
xsd.h
Xsd::IsNillable
static bool IsNillable(const QString &sType)
Definition: xsd.cpp:849
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:577
Xsd::CreateSchemaRoot
QDomElement CreateSchemaRoot()
Definition: xsd.cpp:831
TypeInfo::sAttrName
QString sAttrName
Definition: mythxsd.h:72
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:688
Xsd::ReadPropertyMetadata
static QString ReadPropertyMetadata(QObject *pObject, const QString &sPropName, const QString &sKey)
Definition: xsd.cpp:919
ResponseTypeXML
@ ResponseTypeXML
Definition: httprequest.h:78
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:870
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:551
HTTPRequest::m_eResponseType
HttpResponseType m_eResponseType
Definition: httprequest.h:149
Xsd::GetEnumXSD
bool GetEnumXSD(HTTPRequest *pRequest, const QString &sEnumName)
Definition: xsd.cpp:25
TypeInfo::sContentType
QString sContentType
Definition: mythxsd.h:72
servicehost.h
HTTPRequest::m_response
QBuffer m_response
Definition: httprequest.h:157