MythTV  master
mythxmlplistserialiser.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QMetaProperty>
3 #include <QSequentialIterable>
4 
5 // MythTV
6 #include "mythdate.h"
7 #include "http/mythhttpdata.h"
9 
10 MythXMLPListSerialiser::MythXMLPListSerialiser(const QString& Name, const QVariant& Value)
11 {
12  m_writer.setDevice(&m_buffer);
13  m_writer.setAutoFormatting(true);
14  m_writer.setAutoFormattingIndent(4);
15  m_writer.writeStartDocument("1.0");
16  m_writer.writeDTD(R"(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">)");
17  m_writer.writeStartElement("plist");
18  m_writer.writeAttribute("version", "1.0");
19  m_writer.writeStartElement("dict");
20  m_writer.writeTextElement("key", "serializerversion");
21  m_writer.writeTextElement("string", XML_PLIST_SERIALIZER_VERSION);
22  QString name = Name;
23  if (name.startsWith("V2"))
24  name.remove(0,2);
25  AddObject(name, Value);
26  m_writer.writeEndElement();
27  m_writer.writeEndElement();
28  m_writer.writeEndDocument();
29 }
30 
31 void MythXMLPListSerialiser::AddObject(const QString& Name, const QVariant& Value)
32 {
33  auto * object = Value.value<QObject*>();
34  AddValue(object ? GetContentName(Name, object->metaObject()) : Name, Value);
35 }
36 
37 void MythXMLPListSerialiser::AddValue(const QString& Name, const QVariant& Value, bool NeedKey)
38 {
39  auto * object = Value.value<QObject*>();
40  if (object)
41  {
42  QVariant isNull = object->property("isNull");
43  if (isNull.value<bool>())
44  return;
45  AddQObject(Name, object);
46  return;
47  }
48 
49  switch (
50 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
51  static_cast<QMetaType::Type>(Value.type())
52 #else
53  static_cast<QMetaType::Type>(Value.typeId())
54 #endif
55  )
56  {
57  case QMetaType::QStringList: AddStringList(Name, Value); break;
58  case QMetaType::QVariantList: AddList(Name, Value.toList()); break;
59  case QMetaType::QVariantMap: AddMap(Name, Value.toMap()); break;
60  case QMetaType::UInt:
61  case QMetaType::ULongLong:
62  {
63  if (NeedKey)
64  m_writer.writeTextElement("key", Name);
65  m_writer.writeTextElement("integer", QString::number(Value.toULongLong()));
66  break;
67  }
68 
69  case QMetaType::Int:
70  case QMetaType::LongLong:
71  case QMetaType::Double:
72  {
73  if (NeedKey)
74  m_writer.writeTextElement("key", Name);
75  m_writer.writeTextElement("real", QString("%1").arg(Value.toDouble(), 0, 'f', 6));
76  break;
77  }
78  case QMetaType::Float:
79  {
80  if (NeedKey)
81  m_writer.writeTextElement("key", Name);
82  m_writer.writeTextElement("real", QString("%1").arg(Value.toFloat(), 0, 'f', 6));
83  break;
84  }
85  case QMetaType::QByteArray:
86  {
87  if (!Value.toByteArray().isNull())
88  {
89  if (NeedKey)
90  m_writer.writeTextElement("key", Name);
91  m_writer.writeTextElement("data", Value.toByteArray().toBase64().data());
92  }
93  break;
94  }
95  case QMetaType::Bool:
96  {
97  if (NeedKey)
98  m_writer.writeTextElement("key", Name);
99  m_writer.writeEmptyElement(Value.toBool() ? "true" : "false");
100  break;
101  }
102  case QMetaType::QDateTime:
103  if (Value.toDateTime().isValid())
104  {
105  if (NeedKey)
106  m_writer.writeTextElement("key", Name);
107  m_writer.writeTextElement("date", Value.toDateTime().toUTC().toString("yyyy-MM-ddThh:mm:ssZ"));
108  }
109  break;
110  case QMetaType::QString:
111  default:
112  if (NeedKey)
113  m_writer.writeTextElement("key", Name);
114  m_writer.writeTextElement("string", Value.toString());
115  }
116 }
117 
118 void MythXMLPListSerialiser::AddQObject(const QString &Name, const QObject* Object)
119 {
120  if (!Object)
121  return;
122 
123  m_writer.writeTextElement("key", Name);
124  m_writer.writeStartElement("dict");
125 
126  const auto * meta = Object->metaObject();
127  if (int index = meta->indexOfClassInfo("Version"); index >= 0)
128  {
129  m_writer.writeTextElement("key", "version");
130  m_writer.writeTextElement("string", meta->classInfo(index).value());
131  }
132 
133  int count = meta->propertyCount();
134  for (int index = 0; index < count; ++index )
135  {
136  QMetaProperty metaproperty = meta->property(index);
137  if (
138 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
139  metaproperty.isUser(Object)
140 #else
141  metaproperty.isUser()
142 #endif
143  )
144  {
145  const char *rawname = metaproperty.name();
146  QString name(rawname);
147  if (name.compare("objectName") == 0)
148  continue;
149  AddProperty(name, Object->property(rawname), meta, &metaproperty);
150  }
151  }
152 
153  m_writer.writeEndElement();
154 }
155 
156 void MythXMLPListSerialiser::AddProperty(const QString& Name, const QVariant& Value,
157  const QMetaObject* MetaObject, const QMetaProperty *MetaProperty)
158 {
159  QString value;
160  // Enum ?
161  if (MetaProperty && (MetaProperty->isEnumType() || MetaProperty->isFlagType()))
162  {
163  QMetaEnum metaEnum = MetaProperty->enumerator();
164  value = MetaProperty->isFlagType() ? metaEnum.valueToKeys(Value.toInt()).constData() :
165  metaEnum.valueToKey(Value.toInt());
166  }
167  AddValue(GetContentName(Name, MetaObject), value.isEmpty() ? Value : value);
168 }
169 
170 void MythXMLPListSerialiser::AddStringList(const QString& Name, const QVariant& Values)
171 {
172  m_writer.writeTextElement("key", Name);
173  m_writer.writeStartElement("array");
174  auto values = Values.value<QSequentialIterable>();
175  for (const auto & value : values)
176  m_writer.writeTextElement("string", value.toString());
177  m_writer.writeEndElement();
178 }
179 
180 void MythXMLPListSerialiser::AddList(const QString& Name, const QVariantList &Values)
181 {
182  bool array = true;
183  if (!Values.isEmpty())
184  {
185 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
186  auto type = static_cast<QMetaType::Type>(Values.front().type());
187  auto typesEqual = [type](const QVariant& value)
188  { return (static_cast<QMetaType::Type>(value.type()) == type); };
189 #else
190  auto type = static_cast<QMetaType::Type>(Values.front().typeId());
191  auto typesEqual = [type](const QVariant& value)
192  { return (static_cast<QMetaType::Type>(value.typeId()) == type); };
193 #endif
194  array = std::all_of(Values.cbegin(), Values.cend(), typesEqual);
195  }
196 
197  QString name = GetItemName(Name);
198  m_writer.writeTextElement("key", name);
199  m_writer.writeStartElement(array ? "array" : "dict");
200 
201  QListIterator<QVariant> it(Values);
202  while (it.hasNext())
203  AddValue(name, it.next(), !array);
204  m_writer.writeEndElement();
205 }
206 
207 void MythXMLPListSerialiser::AddMap(const QString& Name, const QVariantMap& Map)
208 {
209  QString itemname = GetItemName(Name);
210  m_writer.writeTextElement("key", itemname);
211  m_writer.writeStartElement("dict");
212  for (auto it = Map.cbegin(); it != Map.cend(); ++it)
213  AddValue(it.key(), it.value());
214  m_writer.writeEndElement();
215 }
216 
218 {
219  QString name = Name.startsWith("Q") ? Name.mid(1) : Name;
220  name.remove("DTC::");
221  if (name.startsWith("V2"))
222  name.remove(0,2);
223  name.remove(QChar('*'));
224  return name;
225 }
226 
228 QString MythXMLPListSerialiser::GetContentName(const QString& Name, const QMetaObject* MetaObject)
229 {
230  // Try to read Name or TypeName from classinfo metadata.
231  if (int index = MetaObject ? MetaObject->indexOfClassInfo(Name.toLatin1()) : -1; index >= 0)
232  {
233 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
234  QStringList infos = QString(MetaObject->classInfo(index).value()).split(';', QString::SkipEmptyParts);
235 #else
236  QStringList infos = QString(MetaObject->classInfo(index).value()).split(';', Qt::SkipEmptyParts);
237 #endif
238  QString type; // fallback
239  foreach (const QString &info, infos)
240  {
241  if (info.startsWith(QStringLiteral("name=")))
242  if (auto name = info.mid(5).trimmed(); !name.isEmpty())
243  return GetItemName(name);
244  if (info.startsWith(QStringLiteral("type=")))
245  type = info.mid(5).trimmed();
246  }
247  if (!type.isEmpty())
248  return GetItemName(type);
249  }
250 
251  // Neither found, so lets use the type name (slightly modified).
252  return GetItemName(Name);
253 }
254 
MythXMLPListSerialiser::AddQObject
void AddQObject(const QString &Name, const QObject *Object)
Definition: mythxmlplistserialiser.cpp:118
MythXMLPListSerialiser::AddMap
void AddMap(const QString &Name, const QVariantMap &Map)
Definition: mythxmlplistserialiser.cpp:207
MythXMLPListSerialiser::GetItemName
static QString GetItemName(const QString &Name)
Definition: mythxmlplistserialiser.cpp:217
MythSerialiser::m_buffer
QBuffer m_buffer
Definition: mythserialiser.h:22
mythhttpdata.h
mythdate.h
MythXMLPListSerialiser::m_writer
QXmlStreamWriter m_writer
Definition: mythxmlplistserialiser.h:31
mythxmlplistserialiser.h
MythXMLPListSerialiser::GetContentName
static QString GetContentName(const QString &Name, const QMetaObject *MetaObject)
FIXME We shouldn't be doing this on the fly.
Definition: mythxmlplistserialiser.cpp:228
MythXMLPListSerialiser::AddObject
void AddObject(const QString &Name, const QVariant &Value)
Definition: mythxmlplistserialiser.cpp:31
MythXMLPListSerialiser::MythXMLPListSerialiser
MythXMLPListSerialiser(const QString &Name, const QVariant &Value)
Definition: mythxmlplistserialiser.cpp:10
MythXMLPListSerialiser::AddList
void AddList(const QString &Name, const QVariantList &Values)
Definition: mythxmlplistserialiser.cpp:180
Name
Definition: channelsettings.cpp:71
MythXMLPListSerialiser::AddStringList
void AddStringList(const QString &Name, const QVariant &Values)
Definition: mythxmlplistserialiser.cpp:170
MythXMLPListSerialiser::AddValue
void AddValue(const QString &Name, const QVariant &Value, bool NeedKey=true)
Definition: mythxmlplistserialiser.cpp:37
MythXMLPListSerialiser::AddProperty
void AddProperty(const QString &Name, const QVariant &Value, const QMetaObject *MetaObject, const QMetaProperty *MetaProperty)
Definition: mythxmlplistserialiser.cpp:156
XML_PLIST_SERIALIZER_VERSION
#define XML_PLIST_SERIALIZER_VERSION
Definition: mythxmlplistserialiser.h:10