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