2
* Copyright (C) 2011 Tuomo Penttinen, all rights reserved.
4
* Author: Tuomo Penttinen <tp@herqq.org>
6
* This file is part of Herqq UPnP Av (HUPnPAv) library.
8
* Herqq UPnP Av is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
11
* (at your option) any later version.
13
* Herqq UPnP Av is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with Herqq UPnP Av. If not, see <http://www.gnu.org/licenses/>.
22
#include "hcds_dlite_serializer.h"
23
#include "hcds_dlite_serializer_p.h"
24
#include "hcdsproperty_db.h"
25
#include "hcdsproperty.h"
27
#include "../cds_objects/hitem.h"
28
#include "../cds_objects/halbum.h"
29
#include "../cds_objects/hmovie.h"
30
#include "../cds_objects/hphoto.h"
31
#include "../cds_objects/hperson.h"
32
#include "../cds_objects/htextitem.h"
33
#include "../cds_objects/haudioitem.h"
34
#include "../cds_objects/haudiobook.h"
35
#include "../cds_objects/hcontainer.h"
36
#include "../cds_objects/haudiobook.h"
37
#include "../cds_objects/hvideoitem.h"
38
#include "../cds_objects/hmusicgenre.h"
39
#include "../cds_objects/hmoviegenre.h"
40
#include "../cds_objects/hmusictrack.h"
41
#include "../cds_objects/hmusicalbum.h"
42
#include "../cds_objects/hphotoalbum.h"
43
#include "../cds_objects/hmusicartist.h"
44
#include "../cds_objects/hphotoalbum.h"
45
#include "../cds_objects/hepgcontainer.h"
46
#include "../cds_objects/hchannelgroup.h"
47
#include "../cds_objects/hbookmarkitem.h"
48
#include "../cds_objects/hplaylistitem.h"
49
#include "../cds_objects/haudioprogram.h"
50
#include "../cds_objects/hvideoprogram.h"
51
#include "../cds_objects/hstoragesystem.h"
52
#include "../cds_objects/hstoragevolume.h"
53
#include "../cds_objects/hstoragefolder.h"
54
#include "../cds_objects/hgenrecontainer.h"
55
#include "../cds_objects/hvideobroadcast.h"
56
#include "../cds_objects/haudiobroadcast.h"
57
#include "../cds_objects/hmusicvideoclip.h"
58
#include "../cds_objects/hbookmarkfolder.h"
59
#include "../cds_objects/haudiochannelgroup.h"
60
#include "../cds_objects/hvideochannelgroup.h"
61
#include "../cds_objects/hplaylistcontainer.h"
63
#include <HUpnpCore/private/hlogger_p.h>
65
#include <QtCore/QSet>
66
#include <QtCore/QVariant>
67
#include <QtCore/QStringList>
68
#include <QtCore/QXmlStreamReader>
69
#include <QtCore/QXmlStreamWriter>
72
* \defgroup hupnp_av_cds_om_mgmt Object Model Management
73
* \ingroup hupnp_av_cds
88
void addNamespaces(QXmlStreamReader& reader)
90
QXmlStreamNamespaceDeclaration didl(
91
"DIDL-Lite", "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/");
92
QXmlStreamNamespaceDeclaration dc(
93
"dc", "http://purl.org/dc/elements/1.1/");
94
QXmlStreamNamespaceDeclaration upnp(
95
"upnp", "urn:schemas-upnp-org:metadata-1-0/upnp/");
96
QXmlStreamNamespaceDeclaration xsi(
97
"xsi", "http://www.w3.org/2001/XMLSchema-instance");
99
reader.addExtraNamespaceDeclaration(didl);
100
reader.addExtraNamespaceDeclaration(dc);
101
reader.addExtraNamespaceDeclaration(upnp);
102
reader.addExtraNamespaceDeclaration(xsi);
105
QString saveItemToXml(QXmlStreamReader& reader)
107
Q_ASSERT(reader.name() == "item" || reader.name() == "container");
110
QXmlStreamWriter writer(&retVal);
112
writer.writeStartElement(reader.name().toString());
114
while(!reader.atEnd() && reader.readNext())
116
switch(reader.tokenType())
118
case QXmlStreamReader::StartElement:
119
if (reader.name() == "item" || reader.name() == "container")
124
writer.writeStartElement(reader.qualifiedName().toString());
125
writer.writeAttributes(reader.attributes());
128
case QXmlStreamReader::EndElement:
129
if (reader.name() == "item" || reader.name() == "container")
134
writer.writeEndElement();
137
case QXmlStreamReader::Characters:
138
writer.writeCharacters(reader.text().toString());
147
writer.writeEndElement();
153
/*******************************************************************************
154
* HCdsDidlLiteSerializerPrivate
155
******************************************************************************/
158
inline HObject* createItem () { return HItem::create(); }
159
inline HObject* createImageItem () { return HImageItem::create(); }
160
inline HObject* createPhoto () { return HPhoto::create(); }
161
inline HObject* createAudioItem () { return HAudioItem::create(); }
162
inline HObject* createMusicTrack () { return HMusicTrack::create(); }
163
inline HObject* createAudioBroadcast() { return HAudioBroadcast::create(); }
164
inline HObject* createAudioBook () { return HAudioBook::create(); }
165
inline HObject* createVideoItem () { return HVideoItem::create(); }
166
inline HObject* createMovie () { return HMovie::create(); }
167
inline HObject* createVideoBroadcast() { return HVideoBroadcast::create(); }
168
inline HObject* createMusicVideoClip() { return HMusicVideoClip::create(); }
169
inline HObject* createPlaylistItem () { return HPlaylistItem::create(); }
170
inline HObject* createTextItem () { return HTextItem::create(); }
171
inline HObject* createBookmarkItem () { return HBookmarkItem::create(); }
172
inline HObject* createEpgItem () { return HEpgItem::create(); }
173
inline HObject* createAudioProgram () { return HAudioProgram::create(); }
174
inline HObject* createVideoProgram () { return HVideoProgram::create(); }
175
inline HObject* createContainer () { return HContainer::create(); }
176
inline HObject* createPerson () { return HPerson::create(); }
177
inline HObject* createMusicArtist () { return HMusicArtist::create(); }
178
inline HObject* createPlaylistContainer() { return HPlaylistContainer::create(); }
179
inline HObject* createAlbum () { return HAlbum::create(); }
180
inline HObject* createMusicAlbum () { return HMusicAlbum::create(); }
181
inline HObject* createPhotoAlbum () { return HPhotoAlbum::create(); }
182
inline HObject* createGenreContainer() { return HGenreContainer::create(); }
183
inline HObject* createMusicGenre () { return HMusicGenre::create(); }
184
inline HObject* createMovieGenre () { return HMovieGenre::create(); }
185
inline HObject* createChannelGroup () { return HChannelGroup::create(); }
186
inline HObject* createAudioChannelGroup() { return HAudioChannelGroup::create(); }
187
inline HObject* createVideoChannelGroup() { return HVideoChannelGroup::create(); }
188
inline HObject* createEpgContainer () { return HEpgContainer::create(); }
189
inline HObject* createStorageSystem () { return HStorageSystem::create(); }
190
inline HObject* createStorageVolume () { return HStorageVolume::create(); }
191
inline HObject* createStorageFolder () { return HStorageFolder::create(); }
192
inline HObject* createBookmarkFolder() { return HBookmarkFolder::create(); }
195
HCdsDidlLiteSerializerPrivate::HCdsDidlLiteSerializerPrivate() :
196
m_creatorFunctions(), m_lastErrorDescription()
198
m_creatorFunctions.insert(HItem::sClass(), createItem);
199
m_creatorFunctions.insert(HImageItem::sClass(), createImageItem);
200
m_creatorFunctions.insert(HPhoto::sClass(), createPhoto);
201
m_creatorFunctions.insert(HAudioItem::sClass(), createAudioItem);
202
m_creatorFunctions.insert(HMusicTrack::sClass(), createMusicTrack);
203
m_creatorFunctions.insert(HAudioBroadcast::sClass(), createAudioBroadcast);
204
m_creatorFunctions.insert(HAudioBook::sClass(), createAudioBook);
205
m_creatorFunctions.insert(HVideoItem::sClass(), createVideoItem);
206
m_creatorFunctions.insert(HMovie::sClass(), createMovie);
207
m_creatorFunctions.insert(HVideoBroadcast::sClass(), createVideoBroadcast);
208
m_creatorFunctions.insert(HMusicVideoClip::sClass(), createMusicVideoClip);
209
m_creatorFunctions.insert(HPlaylistItem::sClass(), createPlaylistItem);
210
m_creatorFunctions.insert(HTextItem::sClass(), createTextItem);
211
m_creatorFunctions.insert(HBookmarkItem::sClass(), createBookmarkItem);
212
m_creatorFunctions.insert(HEpgItem::sClass(), createEpgItem);
213
m_creatorFunctions.insert(HAudioProgram::sClass(), createAudioProgram);
214
m_creatorFunctions.insert(HVideoProgram::sClass(), createVideoProgram);
216
m_creatorFunctions.insert(HContainer::sClass(), createContainer);
217
m_creatorFunctions.insert(HPerson::sClass(), createPerson);
218
m_creatorFunctions.insert(HMusicArtist::sClass(), createMusicArtist);
219
m_creatorFunctions.insert(HPlaylistContainer::sClass(), createPlaylistContainer);
220
m_creatorFunctions.insert(HAlbum::sClass(), createAlbum);
221
m_creatorFunctions.insert(HMusicAlbum::sClass(), createMusicAlbum);
222
m_creatorFunctions.insert(HPhotoAlbum::sClass(), createPhotoAlbum);
223
m_creatorFunctions.insert(HGenreContainer::sClass(), createGenreContainer);
224
m_creatorFunctions.insert(HMusicGenre::sClass(), createMusicGenre);
225
m_creatorFunctions.insert(HMovieGenre::sClass(), createMovieGenre);
226
m_creatorFunctions.insert(HChannelGroup::sClass(), createChannelGroup);
227
m_creatorFunctions.insert(HAudioChannelGroup::sClass(), createAudioChannelGroup);
228
m_creatorFunctions.insert(HVideoChannelGroup::sClass(), createVideoChannelGroup);
229
m_creatorFunctions.insert(HEpgContainer::sClass(), createEpgContainer);
230
m_creatorFunctions.insert(HStorageSystem::sClass(), createStorageSystem);
231
m_creatorFunctions.insert(HStorageVolume::sClass(), createStorageVolume);
232
m_creatorFunctions.insert(HStorageFolder::sClass(), createStorageFolder);
233
m_creatorFunctions.insert(HBookmarkFolder::sClass(), createBookmarkFolder);
236
HCdsDidlLiteSerializerPrivate::~HCdsDidlLiteSerializerPrivate()
240
bool HCdsDidlLiteSerializerPrivate::serializePropertyFromAttribute(
241
HObject* object, const QString& xmlTokenName, const QString& attributeValue)
245
QString cdsName = QString("@%1").arg(xmlTokenName);
246
if (!object->hasCdsProperty(cdsName))
251
HCdsProperty prop = HCdsPropertyDb::instance().property(cdsName);
257
HCdsPropertyHandler hnd = prop.handler();
259
QVariant value(attributeValue);
260
value.convert(prop.info().defaultValue().type());
261
/*if (!hnd.inSerializer()(xmlTokenName, &value, 0))
266
HValidator validator = hnd.validator();
267
if (validator && !validator(value))
272
if (!object->setCdsProperty(cdsName, value))
280
bool HCdsDidlLiteSerializerPrivate::serializePropertyFromElement(
281
HObject* object, const QString& propertyName, QXmlStreamReader& reader)
285
if (!object->hasCdsProperty(propertyName))
290
HCdsProperty prop = HCdsPropertyDb::instance().property(propertyName);
294
return object->serialize(propertyName, 0, &reader);
298
HCdsPropertyHandler hnd = prop.handler();
299
if (hnd.inSerializer()(propertyName, &value, &reader) == false)
304
HValidator validator = hnd.validator();
305
if (validator && !validator(value))
310
if (prop.info().propertyFlags() & HCdsPropertyInfo::MultiValued)
313
object->getCdsProperty(propertyName, &tmp);
314
QVariantList values = tmp.toList();
315
values.append(value);
316
return object->setCdsProperty(propertyName, values);
319
return object->setCdsProperty(propertyName, value);
322
bool HCdsDidlLiteSerializerPrivate::serializeProperty(
323
const HObject& object, const QString& propName, const QVariant& value,
324
const QSet<QString>& filter, QXmlStreamWriter& writer)
328
if (!object.isCdsPropertySet(propName) ||
329
!object.isCdsPropertyActive(propName))
334
bool filterOk = filter.contains("*") || filter.contains(propName);
336
HCdsProperty prop = HCdsPropertyDb::instance().property(propName);
342
if (!object.serialize(propName, value, writer))
344
HLOG_DBG(QString("Failed to serialize property: [%1]").arg(propName));
349
HLOG_DBG(QString("Failed to serialize property: [%1]").arg(propName));
354
HCdsPropertyInfo::PropertyFlags pflags = prop.info().propertyFlags();
355
if (!filterOk && (!(pflags & HCdsPropertyInfo::Mandatory)))
359
else if (pflags & HCdsPropertyInfo::MultiValued)
361
foreach(const QVariant& var, value.toList())
363
if (prop.handler().outSerializer()(propName, var, writer) == false)
365
HLOG_DBG(QString("Failed to serialize property: [%1]").arg(propName));
371
if (prop.handler().outSerializer()(propName, value, writer) == false)
373
HLOG_DBG(QString("Failed to serialize property: [%1]").arg(propName));
380
bool HCdsDidlLiteSerializerPrivate::serializeObject(
381
const HObject& object, const QSet<QString>& filter,
382
QXmlStreamWriter& writer)
386
writer.writeStartElement(HObject::isItem(object.type()) ? "item" : "container");
388
QHash<QString, QVariant> cdsProps = object.cdsProperties();
390
QSet<QString> didlLiteDepProps =
391
HCdsPropertyDb::instance().didlLiteDependentProperties();
393
foreach(const QString& arg, didlLiteDepProps)
395
if (cdsProps.contains(arg))
397
serializeProperty(object, arg, cdsProps.value(arg), filter, writer);
398
cdsProps.remove(arg);
402
QHash<QString, QVariant>::const_iterator ci = cdsProps.constBegin();
403
for(; ci != cdsProps.constEnd(); ++ci)
405
serializeProperty(object, ci.key(), ci.value(), filter, writer);
408
writer.writeEndElement();
413
HObject* HCdsDidlLiteSerializerPrivate::parseObject(
414
QXmlStreamReader& reader, HCdsDidlLiteSerializer::XmlType xtype)
418
QXmlStreamAttributes attrs = reader.attributes();
420
QString itemData = saveItemToXml(reader);
422
QXmlStreamReader itemReader(itemData);
424
if (xtype == HCdsDidlLiteSerializer::Document)
426
addNamespaces(itemReader);
430
itemReader.setNamespaceProcessing(false);
433
while(!itemReader.atEnd() && itemReader.readNext())
435
if (itemReader.tokenType() == QXmlStreamReader::StartElement)
437
if (itemReader.qualifiedName() != "upnp:class")
447
QString clazz = itemReader.readElementText();
448
HObjectCreator creator = m_creatorFunctions.value(clazz);
451
m_lastErrorDescription = QString("Unknown class: [%1]").arg(clazz);
455
QScopedPointer<HObject> object(creator());
457
foreach(const QXmlStreamAttribute& attr, attrs)
459
serializePropertyFromAttribute(
460
object.data(), attr.name().toString(), attr.value().toString());
463
bool tcoEnabled = false;
466
if (xtype == HCdsDidlLiteSerializer::Document)
468
addNamespaces(itemReader);
472
itemReader.setNamespaceProcessing(false);
474
itemReader.addData(itemData);
476
while(!itemReader.atEnd() && itemReader.readNext())
478
if (itemReader.tokenType() == QXmlStreamReader::StartElement)
480
QString name = itemReader.qualifiedName().toString();
482
if (name == "upnp:class")
486
else if (!serializePropertyFromElement(object.data(), name, itemReader))
488
if (name == "item" || name == "container")
494
HLOG_DBG(QString("Couldn't serialize property: %1").arg(name));
497
else if (!tcoEnabled)
499
const HCdsPropertyInfo& info = HCdsProperties::instance().get(name);
500
if (info.isValid() && (
501
info.type() == HCdsProperties::upnp_objectUpdateID ||
502
info.type() == HCdsProperties::upnp_containerUpdateID ||
503
info.type() == HCdsProperties::upnp_totalDeletedChildCount)
514
object->setTrackChangesOption(true);
517
return object->validate() ? object.take() : 0;
523
void HCdsDidlLiteSerializerPrivate::writeDidlLiteDocumentInfo(
524
QXmlStreamWriter& writer)
526
writer.setCodec("UTF-8");
527
writer.writeStartDocument();
528
writer.writeStartElement("DIDL-Lite");
529
writer.writeDefaultNamespace("urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/");
530
writer.writeAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
531
writer.writeAttribute("xmlns:upnp", "urn:schemas-upnp-org:metadata-1-0/upnp/");
532
writer.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
533
writer.writeAttribute("xsi:schemaLocation",
534
"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/ " \
535
"http://www.upnp.org/schemas/av/didl-lite.xsd " \
536
"urn:schemas-upnp-org:metadata-1-0/upnp/ " \
537
"http://www.upnp.org/schemas/av/upnp.xsd");
540
/*******************************************************************************
541
* HCdsDidlLiteSerializer
542
******************************************************************************/
543
HCdsDidlLiteSerializer::HCdsDidlLiteSerializer() :
544
h_ptr(new HCdsDidlLiteSerializerPrivate())
548
HCdsDidlLiteSerializer::~HCdsDidlLiteSerializer()
553
QString HCdsDidlLiteSerializer::lastErrorDescription() const
555
return h_ptr->m_lastErrorDescription;
558
bool HCdsDidlLiteSerializer::serializeFromXml(
559
const QString& didlLiteDoc, HObjects* retVal, XmlType inputType)
564
QXmlStreamReader reader(didlLiteDoc);
566
if (inputType == Document)
568
addNamespaces(reader);
569
if (reader.readNextStartElement())
571
if (reader.name().compare("DIDL-Lite", Qt::CaseInsensitive) != 0)
573
h_ptr->m_lastErrorDescription = "Missing mandatory DIDL-Lite element";
580
reader.setNamespaceProcessing(false);
584
while(!reader.atEnd() && reader.readNext())
586
switch(reader.tokenType())
588
case QXmlStreamReader::StartElement:
590
QStringRef name = reader.name();
591
if (name == "item" || name == "container")
593
HObject* obj = h_ptr->parseObject(reader, inputType);
607
if (reader.error() != QXmlStreamReader::NoError)
609
h_ptr->m_lastErrorDescription =
610
QString("Parse failed: [%1]").arg(reader.errorString());
619
QString HCdsDidlLiteSerializer::serializeToXml(
620
const HObject& object, XmlType xmlType)
622
QSet<QString> filter; filter.insert("*");
623
return serializeToXml(object, filter, xmlType);
626
QString HCdsDidlLiteSerializer::serializeToXml(
627
const HObject& object, const QSet<QString>& filter, XmlType xmlType)
630
QXmlStreamWriter writer(&retVal);
632
if (xmlType == Document)
634
h_ptr->writeDidlLiteDocumentInfo(writer);
637
if (!h_ptr->serializeObject(object, filter, writer))
642
if (xmlType == Document)
644
writer.writeEndDocument();
650
QString HCdsDidlLiteSerializer::serializeToXml(const HObjects& objects)
652
QSet<QString> filter; filter.insert("*");
653
return serializeToXml(objects, filter);
656
QString HCdsDidlLiteSerializer::serializeToXml(
657
const HObjects& objects, const QSet<QString>& filter)
660
QXmlStreamWriter writer(&retVal);
662
h_ptr->writeDidlLiteDocumentInfo(writer);
664
foreach(const HObject* obj, objects)
666
if (!h_ptr->serializeObject(*obj, filter, writer))
672
writer.writeEndDocument();