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