diff options
author | methodus <methodus@web.de> | 2012-10-12 11:45:32 +0200 |
---|---|---|
committer | methodus <methodus@web.de> | 2012-10-12 11:45:32 +0200 |
commit | b0e2b9d4f39d5adf322280269e096c8464a8fda2 (patch) | |
tree | 5b51495008d95d18e90973b898f6a01b55273fb2 /media | |
parent | 350f2dc11ffb88ff3310987ed4a103229a3c82d3 (diff) | |
download | vdr-plugin-upnp-b0e2b9d4f39d5adf322280269e096c8464a8fda2.tar.gz vdr-plugin-upnp-b0e2b9d4f39d5adf322280269e096c8464a8fda2.tar.bz2 |
Third step to scan directories provided by plugable providers.
Diffstat (limited to 'media')
-rw-r--r-- | media/mediaManager.cpp | 241 | ||||
-rw-r--r-- | media/pluginManager.cpp | 18 |
2 files changed, 163 insertions, 96 deletions
diff --git a/media/mediaManager.cpp b/media/mediaManager.cpp index 16e7497..3df218a 100644 --- a/media/mediaManager.cpp +++ b/media/mediaManager.cpp @@ -6,6 +6,7 @@ */ #include "../include/media/mediaManager.h" +#include "../include/pluginManager.h" #include "../include/server.h" #include "../include/parser.h" #include <upnp/upnp.h> @@ -14,6 +15,7 @@ #include <upnp/ixml.h> #include <memory> #include <tntdb/statement.h> +#include <tntdb/transaction.h> namespace upnp { @@ -108,6 +110,13 @@ cMediaManager::cMediaManager() } cMediaManager::~cMediaManager(){ + try { + tntdb::Statement stmt = connection.prepare("VACUUM"); + stmt.execute(); + } catch (const std::exception& e) { + esyslog("UPnP\tFailed to vacuum database '%s': '%s'", databaseFile.c_str(), e.what()); + } + delete pluginManager; } @@ -218,16 +227,21 @@ StringList cMediaManager::GetSupportedProtocolInfos() const { ss << "SELECT DISTINCT `" << property::resource::KEY_PROTOCOL_INFO << "` FROM " << db::Resources; - tntdb::Statement stmt = conn.prepare(ss.str()); - StringList list; - for(tntdb::Statement::const_iterator it = stmt.begin(); it != stmt.end(); ++it){ - tntdb::Row row = (*it); + try { + tntdb::Statement stmt = conn.prepare(ss.str()); + + for(tntdb::Statement::const_iterator it = stmt.begin(); it != stmt.end(); ++it){ + tntdb::Row row = (*it); - cout << row.getString(property::resource::KEY_PROTOCOL_INFO) << endl; + cout << row.getString(property::resource::KEY_PROTOCOL_INFO) << endl; - list.push_back(row.getString(property::resource::KEY_PROTOCOL_INFO)); + list.push_back(row.getString(property::resource::KEY_PROTOCOL_INFO)); + } + + } catch (const std::exception& e) { + esyslog("UPnP\tException occurred while getting protocol infos: %s", e.what()); } return list; @@ -244,114 +258,120 @@ int cMediaManager::CreateResponse(MediaRequest& request, const string& select, c << "`" << property::object::KEY_OBJECTID << "` = " << ":objectID"; - tntdb::Statement select1 = connection.prepare(select); - tntdb::Result result = connection.select(count); - tntdb::Statement select2 = connection.prepare(resources.str()); - tntdb::Statement select3 = connection.prepare(details.str()); + IXML_Document* DIDLDoc = NULL; - StringList filterList = cFilterCriteria::parse(request.filter); + try { + tntdb::Statement select1 = connection.prepare(select); + tntdb::Result result = connection.select(count); + tntdb::Statement select2 = connection.prepare(resources.str()); + tntdb::Statement select3 = connection.prepare(details.str()); - request.numberReturned = 0; - request.updateID = 0; - request.totalMatches = result.getRow(0).getInt32("totalMatches"); + StringList filterList = cFilterCriteria::parse(request.filter); - IXML_Document* DIDLDoc = NULL; - if(ixmlParseBufferEx(DIDLFragment, &DIDLDoc)==IXML_SUCCESS){ + request.numberReturned = 0; + request.updateID = 0; + request.totalMatches = result.getRow(0).getInt32("totalMatches"); - IXML_Node* root = ixmlNode_getFirstChild((IXML_Node*) DIDLDoc); + if(ixmlParseBufferEx(DIDLFragment, &DIDLDoc)==IXML_SUCCESS){ - tntdb::Row row, row2, row3; + IXML_Node* root = ixmlNode_getFirstChild((IXML_Node*) DIDLDoc); - for(tntdb::Statement::const_iterator it = select1.begin(); it != select1.end(); ++it){ + tntdb::Row row, row2, row3; - row = (*it); + for(tntdb::Statement::const_iterator it = select1.begin(); it != select1.end(); ++it){ - IXML_Element* object; - string upnpClass = row.getString(property::object::KEY_CLASS); + row = (*it); - bool isContainer; + IXML_Element* object; + string upnpClass = row.getString(property::object::KEY_CLASS); + bool isContainer; - if(upnpClass.find("object.item",0) == 0){ - object = ixmlDocument_createElement(DIDLDoc, "item"); - isContainer = false; - } else if(upnpClass.find("object.container",0) == 0) { - object = ixmlDocument_createElement(DIDLDoc, "container"); - isContainer = true; - } else { - goto error; - } - ixmlNode_appendChild(root, (IXML_Node*)object); - string objectID = row.getString(property::object::KEY_OBJECTID); + if(upnpClass.find("object.item",0) == 0){ + object = ixmlDocument_createElement(DIDLDoc, "item"); + isContainer = false; + } else if(upnpClass.find("object.container",0) == 0) { + object = ixmlDocument_createElement(DIDLDoc, "container"); + isContainer = true; + } else { + goto error; + } + ixmlNode_appendChild(root, (IXML_Node*)object); - ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_OBJECTID, objectID); - ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_PARENTID, row.getString(property::object::KEY_PARENTID)); - ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_RESTRICTED, row.getString(property::object::KEY_RESTRICTED)); - ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_TITLE, row.getString(property::object::KEY_TITLE).substr(0, MAX_METADATA_LENGTH_S)); - ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_CLASS, row.getString(property::object::KEY_CLASS).substr(0, MAX_METADATA_LENGTH_S)); + string objectID = row.getString(property::object::KEY_OBJECTID); - if(isContainer){ - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CHILD_COUNT, row.getString(property::object::KEY_CHILD_COUNT)); - } - else { - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CHANNEL_NR, row.getString(property::object::KEY_CHANNEL_NR)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CHANNEL_NAME, row.getString(property::object::KEY_CHANNEL_NAME)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_SCHEDULED_START, row.getString(property::object::KEY_SCHEDULED_START)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_SCHEDULED_END, row.getString(property::object::KEY_SCHEDULED_END)); - } + ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_OBJECTID, objectID); + ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_PARENTID, row.getString(property::object::KEY_PARENTID)); + ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_RESTRICTED, row.getString(property::object::KEY_RESTRICTED)); + ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_TITLE, row.getString(property::object::KEY_TITLE).substr(0, MAX_METADATA_LENGTH_S)); + ixml::IxmlAddProperty(DIDLDoc, object, property::object::KEY_CLASS, row.getString(property::object::KEY_CLASS).substr(0, MAX_METADATA_LENGTH_S)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CREATOR, row.getString(property::object::KEY_CREATOR)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_DESCRIPTION, row.getString(property::object::KEY_DESCRIPTION)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_LONG_DESCRIPTION, row.getString(property::object::KEY_LONG_DESCRIPTION)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_DATE, row.getString(property::object::KEY_DATE)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_LANGUAGE, row.getString(property::object::KEY_LANGUAGE)); + if(isContainer){ + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CHILD_COUNT, row.getString(property::object::KEY_CHILD_COUNT)); + } + else { + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CHANNEL_NR, row.getString(property::object::KEY_CHANNEL_NR)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CHANNEL_NAME, row.getString(property::object::KEY_CHANNEL_NAME)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_SCHEDULED_START, row.getString(property::object::KEY_SCHEDULED_START)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_SCHEDULED_END, row.getString(property::object::KEY_SCHEDULED_END)); + } + + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_CREATOR, row.getString(property::object::KEY_CREATOR)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_DESCRIPTION, row.getString(property::object::KEY_DESCRIPTION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_LONG_DESCRIPTION, row.getString(property::object::KEY_LONG_DESCRIPTION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_DATE, row.getString(property::object::KEY_DATE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::object::KEY_LANGUAGE, row.getString(property::object::KEY_LANGUAGE)); - select2.setString("objectID", objectID); + select2.setString("objectID", objectID); - for(tntdb::Statement::const_iterator it2 = select2.begin(); it2 != select2.end(); ++it2){ - row2 = (*it2); + for(tntdb::Statement::const_iterator it2 = select2.begin(); it2 != select2.end(); ++it2){ + row2 = (*it2); - boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(row2.getString(property::resource::KEY_RESOURCE))); + boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(row2.getString(property::resource::KEY_RESOURCE))); - if(provider.get()){ - string resourceURI = provider->GetHTTPUri(row2.getString(property::resource::KEY_RESOURCE), cMediaServer::GetInstance()->GetServerIPAddress()); + if(provider.get()){ + string resourceURI = provider->GetHTTPUri(row2.getString(property::resource::KEY_RESOURCE), cMediaServer::GetInstance()->GetServerIPAddress()); - IXML_Element* resource = ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOURCE, resourceURI); + IXML_Element* resource = ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOURCE, resourceURI); - if(resource){ - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_PROTOCOL_INFO, row2.getString(property::resource::KEY_PROTOCOL_INFO)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITRATE, row2.getString(property::resource::KEY_BITRATE)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITS_PER_SAMPLE, row2.getString(property::resource::KEY_BITS_PER_SAMPLE)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_COLOR_DEPTH, row2.getString(property::resource::KEY_COLOR_DEPTH)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_DURATION, row2.getString(property::resource::KEY_DURATION)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_NR_AUDIO_CHANNELS, row2.getString(property::resource::KEY_NR_AUDIO_CHANNELS)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOLUTION, row2.getString(property::resource::KEY_RESOLUTION)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SAMPLE_FREQUENCY, row2.getString(property::resource::KEY_SAMPLE_FREQUENCY)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SIZE, tools::ToString(row2.getInt64(property::resource::KEY_SIZE))); + if(resource){ + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_PROTOCOL_INFO, row2.getString(property::resource::KEY_PROTOCOL_INFO)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITRATE, row2.getString(property::resource::KEY_BITRATE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITS_PER_SAMPLE, row2.getString(property::resource::KEY_BITS_PER_SAMPLE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_COLOR_DEPTH, row2.getString(property::resource::KEY_COLOR_DEPTH)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_DURATION, row2.getString(property::resource::KEY_DURATION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_NR_AUDIO_CHANNELS, row2.getString(property::resource::KEY_NR_AUDIO_CHANNELS)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOLUTION, row2.getString(property::resource::KEY_RESOLUTION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SAMPLE_FREQUENCY, row2.getString(property::resource::KEY_SAMPLE_FREQUENCY)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SIZE, tools::ToString(row2.getInt64(property::resource::KEY_SIZE))); + } } - } - } + } - select3.setString("objectID", objectID); + select3.setString("objectID", objectID); - for(tntdb::Statement::const_iterator it3 = select3.begin(); it3 != select2.end(); ++it3){ - row3 = (*it3); + for(tntdb::Statement::const_iterator it3 = select3.begin(); it3 != select2.end(); ++it3){ + row3 = (*it3); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, row3.getString("property"), row3.getString("value")); - } + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, row3.getString("property"), row3.getString("value")); + } - ++request.numberReturned; - } + ++request.numberReturned; + } - request.result = ixmlDocumenttoString(DIDLDoc); + request.result = ixmlDocumenttoString(DIDLDoc); - cout << request.result << endl; + cout << request.result << endl; - ixmlDocument_free(DIDLDoc); - return UPNP_E_SUCCESS; + ixmlDocument_free(DIDLDoc); + return UPNP_E_SUCCESS; + } + } catch (const std::exception& e) { + esyslog("UPnP\tException occurred while creating response for object '%s': %s", + request.objectID.c_str(), e.what()); } error: @@ -572,6 +592,10 @@ bool cMediaManager::Initialise(){ bool cMediaManager::CheckIntegrity(){ + tntdb::Statement enableForeignKeys = connection.prepare("PRAGMA foreign_keys = ON"); + + enableForeignKeys.execute(); + tntdb::Statement checkTable = connection.prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name=:table;" ); @@ -680,7 +704,7 @@ void cMediaManager::Action(){ uri = scanTargets.front(); boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(uri)); if(!ScanURI(uri, provider.get())){ - isyslog("UPnP\tAn error occured while scanning '%s'!", uri.c_str()); + //isyslog("UPnP\tAn error occured while scanning '%s'!", uri.c_str()); } scanTargets.pop_front(); } @@ -706,7 +730,6 @@ bool cMediaManager::ScanURI(const string& uri, cUPnPResourceProvider* provider){ } } - isyslog("UPnP\tCannot find a profiler for schema '%s'", schema.c_str()); return false; } else { @@ -735,7 +758,16 @@ bool cMediaManager::ScanURI(const string& uri, cUPnPResourceProvider* provider){ << " != '" << tools::GenerateUUIDFromURL(uristrm.str()) << "'"; } - cout << ss.str() << endl; + try { + tntdb::Statement objects = connection.prepare(ss.str()); + + objects.execute(); + } catch (const std::exception& e) { + esyslog("UPnP\tException occurred while removing old object in '%s' from database '%s': %s", + tools::GenerateUUIDFromURL(uri).c_str(), databaseFile.c_str(), e.what()); + + return false; + } for(StringList::iterator it = entries.begin(); it != entries.end(); ++it){ uristrm.str(string()); @@ -750,6 +782,8 @@ bool cMediaManager::ScanURI(const string& uri, cUPnPResourceProvider* provider){ bool cMediaManager::RefreshObject(cMetadata& metadata){ stringstream ss; + string objectID = metadata.GetPropertyByKey(property::object::KEY_OBJECTID).GetString(); + try { connection.beginTransaction(); @@ -779,8 +813,6 @@ bool cMediaManager::RefreshObject(cMetadata& metadata){ const cMediaServer::Description desc = cMediaServer::GetInstance()->GetServerDescription(); - string objectID = metadata.GetPropertyByKey(property::object::KEY_OBJECTID).GetString(); - object.setString("objectID", objectID) .setString("parentID", metadata.GetPropertyByKey(property::object::KEY_PARENTID).GetString()) .setString("title", metadata.GetPropertyByKey(property::object::KEY_TITLE).GetString()) @@ -808,7 +840,7 @@ bool cMediaManager::RefreshObject(cMetadata& metadata){ object.setNull("language"); (!metadata.GetPropertyByKey(property::object::KEY_CHANNEL_NR).IsEmpty()) ? - object.setInteger("channelNr", metadata.GetPropertyByKey(property::object::KEY_CHANNEL_NR).GetString()) : + object.setInt("channelNr", metadata.GetPropertyByKey(property::object::KEY_CHANNEL_NR).GetInteger()) : object.setNull("channelNr"); (!metadata.GetPropertyByKey(property::object::KEY_CHANNEL_NAME).IsEmpty()) ? @@ -852,7 +884,7 @@ bool cMediaManager::RefreshObject(cMetadata& metadata){ << "`" << property::resource::KEY_COLOR_DEPTH << "`" << ") VALUES ( " << ":objectID, :resource, :protocolInfo, :size," - << ":duration, :resolution, :bitrate, :sampleFreq, :bpSample" + << ":duration, :resolution, :bitrate, :sampleFreq, :bpSample," << ":nrChannels, :colorDepth" << ")"; @@ -862,7 +894,7 @@ bool cMediaManager::RefreshObject(cMetadata& metadata){ for(cMetadata::ResourceList::iterator it = resources.begin(); it != resources.end(); ++it){ resourcestmt2.setString("objectID", objectID) .setString("resource", (*it).GetResourceUri()) - .setString("protocolInfo", (*it).GetProtocolInfo()) + .setString("protocolInfo", (*it).GetProtocolInfo()); ((*it).GetSize()) ? resourcestmt2.setInt("size",(*it).GetSize()) : @@ -905,7 +937,7 @@ bool cMediaManager::RefreshObject(cMetadata& metadata){ << "`" << property::object::KEY_OBJECTID << "`" << " = :objectID"; - tntdb::Statement detailstmt = connection.prepare(resourcestr.str()); + tntdb::Statement detailstmt = connection.prepare(detailstr.str()); detailstmt.setString("objectID", objectID) .execute(); @@ -920,17 +952,36 @@ bool cMediaManager::RefreshObject(cMetadata& metadata){ << ":objectID, :property, :value" << ")"; - tntdb::Statement detailstmt2 = connection.prepare(resourcestr.str()); + tntdb::Statement detailstmt2 = connection.prepare(detailstr.str()); cMetadata::PropertyRange properties = metadata.GetAllProperties(); for(cMetadata::PropertyMap::iterator it = properties.first; it != properties.second; ++it){ - // TODO + if((*it).first.compare(property::object::KEY_OBJECTID) == 0 || + (*it).first.compare(property::object::KEY_PARENTID) == 0 || + (*it).first.compare(property::object::KEY_TITLE) == 0 || + (*it).first.compare(property::object::KEY_CLASS) == 0 || + (*it).first.compare(property::object::KEY_RESTRICTED) == 0 || + (*it).first.compare(property::object::KEY_CREATOR) == 0 || + (*it).first.compare(property::object::KEY_DESCRIPTION) == 0 || + (*it).first.compare(property::object::KEY_LONG_DESCRIPTION) == 0 || + (*it).first.compare(property::object::KEY_DATE) == 0 || + (*it).first.compare(property::object::KEY_LANGUAGE) == 0 || + (*it).first.compare(property::object::KEY_CHANNEL_NR) == 0 || + (*it).first.compare(property::object::KEY_CHANNEL_NAME) == 0 || + (*it).first.compare(property::object::KEY_SCHEDULED_START) == 0 || + (*it).first.compare(property::object::KEY_SCHEDULED_END) == 0) continue; + + detailstmt2.setString("objectID", objectID) + .setString("property", (*it).second.GetKey()) + .setString("value", (*it).second.GetString()) + .execute(); } connection.commitTransaction(); } catch (const std::exception& e) { - esyslog("UPnP\tException occurred while initializing database '%s': %s", databaseFile.c_str(), e.what()); + esyslog("UPnP\tException occurred while storing object '%s' to database '%s': %s", + objectID.c_str(), databaseFile.c_str(), e.what()); connection.rollbackTransaction(); diff --git a/media/pluginManager.cpp b/media/pluginManager.cpp index 59dccfa..baacbc2 100644 --- a/media/pluginManager.cpp +++ b/media/pluginManager.cpp @@ -6,8 +6,8 @@ */ #include "../include/server.h" -#include "../include/media/mediaManager.h" #include "../include/pluginManager.h" +#include "../include/media/mediaManager.h" #include "../include/tools/string.h" #include "../include/tools/uuid.h" #include <string> @@ -120,6 +120,18 @@ cMetadata::Property& cMetadata::GetPropertyByKey(const string& property) { return (it != properties.end()) ? (*it).second : cMetadata::Property::Empty; } +cMetadata::ResourceList& cMetadata::GetResources(){ + return resources; +} + +void cMetadata::AddResource(const Resource& resource){ + resources.push_back(resource); +} + +void cMetadata::RemoveResource(const Resource& resource){ + resources.remove(resource); +} + string cMetadata::ToString() { stringstream ss; @@ -218,6 +230,10 @@ bool cMetadata::Resource::SetColorDepth(uint32_t colorDepth){ return true; } +bool cMetadata::Resource::operator ==(const Resource& rhs){ + return (GetResourceUri().compare(rhs.GetResourceUri()) == 0); +} + class ClassValidator : public PropertyValidator { public: ClassValidator() : PropertyValidator(property::object::KEY_CLASS) {} |