Index: nepomuk/core/resourcedata.cpp =================================================================== --- nepomuk/core/resourcedata.cpp (revision 842405) +++ nepomuk/core/resourcedata.cpp (revision 842406) @@ -87,6 +87,9 @@ m_types << m_mainType; + // there is no need to store the trivial type + m_initialTypeSaved = ( m_mainType == Soprano::Vocabulary::RDFS::Resource() ); + // TODO: handle the caching in a decent Cache class and not this ugly. if ( s_dataCnt >= 1000 ) { for( ResourceDataHash::iterator rdIt = initializedData()->begin(); @@ -143,6 +146,31 @@ } +void Nepomuk::ResourceData::setTypes( const QList<QUrl>& types ) +{ + if( m_proxyData ) { + m_proxyData->setTypes( types ); + } + else if ( store() ) { + // reset types + m_types.clear(); + m_mainType = Soprano::Vocabulary::RDFS::Resource(); + + // load types (and set maintype) + QList<Node> nodes; + foreach( const QUrl& url, types ) { + loadType( url ); + nodes << Node( url ); + } + + // update the data store + ResourceFilterModel fm( ResourceManager::instance()->mainModel() ); + fm.updateProperty( m_uri, Soprano::Vocabulary::RDF::type(), nodes ); + } +} + + + void Nepomuk::ResourceData::deleteData() { if( m_proxyData ) { @@ -257,13 +285,17 @@ m_modificationMutex.unlock(); } - if ( !exists() ) { - QList<Statement> statements; + QList<Statement> statements; - // save type (There should be no need to save all the types since there is only one way - // that m_types contains more than one element: if we loaded them) + // save type (There should be no need to save all the types since there is only one way + // that m_types contains more than one element: if we loaded them) + // The first type, however, can be set at creation time to any value + if ( !m_initialTypeSaved && + !ResourceManager::instance()->mainModel()->containsAnyStatement( m_uri, Soprano::Vocabulary::RDF::type(), m_types.first() ) ) { statements.append( Statement( m_uri, Soprano::Vocabulary::RDF::type(), m_types.first() ) ); + } + if ( !exists() ) { // save the creation date statements.append( Statement( m_uri, Soprano::Vocabulary::NAO::created(), Soprano::LiteralValue( QDateTime::currentDateTime() ) ) ); @@ -274,13 +306,17 @@ // HACK: make sure that files have proper fileUrl properties so long as we do not have a File class for // Dolphin and co. - if ( constHasType( Soprano::Vocabulary::Xesam::File() ) && + if ( ( m_uri.scheme() == "file" || + constHasType( Soprano::Vocabulary::Xesam::File() ) ) && QFile::exists( m_uri.toLocalFile()) ) { statements.append( Statement( m_uri, Soprano::Vocabulary::Xesam::url(), LiteralValue( m_uri.toLocalFile() ) ) ); } + } + if ( !statements.isEmpty() ) { + m_initialTypeSaved = true; ResourceFilterModel fm( ResourceManager::instance()->mainModel() ); return fm.addStatements( statements ) == Soprano::Error::ErrorNone; } @@ -290,6 +326,48 @@ } +void Nepomuk::ResourceData::loadType( const QUrl& storedType ) +{ + if ( !m_types.contains( storedType ) ) { + m_types << storedType; + } + if ( m_mainType == Soprano::Vocabulary::RDFS::Resource() ) { + Q_ASSERT( !storedType.isEmpty() ); + m_mainType = storedType; + } + else { + Types::Class currentTypeClass = m_mainType; + Types::Class storedTypeClass = storedType; + + // Keep the type that is further down the hierarchy + if ( storedTypeClass.isSubClassOf( currentTypeClass ) ) { + m_mainType = storedTypeClass.uri(); + } + else { + // This is a little convenience hack since the user is most likely + // more interested in the file content than the actual file + Types::Class xesamContentClass( Soprano::Vocabulary::Xesam::Content() ); + if ( m_mainType == Soprano::Vocabulary::Xesam::File() && + ( storedTypeClass == xesamContentClass || + storedTypeClass.isSubClassOf( xesamContentClass ) ) ) { + m_mainType = storedTypeClass.uri(); + } + else { + // the same is true for nie:DataObject vs. nie:InformationElement + Types::Class nieInformationElementClass( Vocabulary::NIE::InformationElement() ); + Types::Class nieDataObjectClass( Vocabulary::NIE::DataObject() ); + if( ( currentTypeClass == nieDataObjectClass || + currentTypeClass.isSubClassOf( nieDataObjectClass ) ) && + ( storedTypeClass == nieInformationElementClass || + storedTypeClass.isSubClassOf( nieInformationElementClass ) ) ) { + m_mainType = storedTypeClass.uri(); + } + } + } + } +} + + bool Nepomuk::ResourceData::load() { if ( m_cacheDirty ) { @@ -304,43 +382,7 @@ if ( statement.predicate().uri() == Soprano::Vocabulary::RDF::type() ) { if ( statement.object().isResource() ) { QUrl storedType = statement.object().uri(); - if ( !m_types.contains( storedType ) ) { - m_types << storedType; - } - if ( m_mainType == Soprano::Vocabulary::RDFS::Resource() ) { - Q_ASSERT( !storedType.isEmpty() ); - m_mainType = storedType; - } - else { - Types::Class currentTypeClass = m_mainType; - Types::Class storedTypeClass = storedType; - - // Keep the type that is further down the hierarchy - if ( storedTypeClass.isSubClassOf( currentTypeClass ) ) { - m_mainType = storedTypeClass.uri(); - } - else { - // This is a little convenience hack since the user is most likely - // more interested in the file content than the actual file - Types::Class xesamContentClass( Soprano::Vocabulary::Xesam::Content() ); - if ( m_mainType == Soprano::Vocabulary::Xesam::File() && - ( storedTypeClass == xesamContentClass || - storedTypeClass.isSubClassOf( xesamContentClass ) ) ) { - m_mainType = storedTypeClass.uri(); - } - else { - // the same is true for nie:DataObject vs. nie:InformationElement - Types::Class nieInformationElementClass( Vocabulary::NIE::InformationElement() ); - Types::Class nieDataObjectClass( Vocabulary::NIE::DataObject() ); - if( ( currentTypeClass == nieDataObjectClass || - currentTypeClass.isSubClassOf( nieDataObjectClass ) ) && - ( storedTypeClass == nieInformationElementClass || - storedTypeClass.isSubClassOf( nieInformationElementClass ) ) ) { - m_mainType = storedTypeClass.uri(); - } - } - } - } + loadType( storedType ); } } else { @@ -374,8 +416,7 @@ if ( value.simpleType() == qMetaTypeId<Resource>() ) { QList<Resource> l = value.toResourceList(); for( QList<Resource>::iterator resIt = l.begin(); resIt != l.end(); ++resIt ) { - if ( resIt->isValid() ) - resIt->m_data->store(); + resIt->m_data->store(); } } @@ -396,7 +437,7 @@ // one-to-many literals else if( value.isList() ) { - valueNodes += Nepomuk::valuesToRDFNodes( value ); + valueNodes = Nepomuk::valuesToRDFNodes( value ); } // one-to-one literal @@ -480,7 +521,9 @@ Soprano::Model* model = ResourceManager::instance()->mainModel(); - if( model->containsAnyStatement( Statement( QUrl( kickoffUriOrId() ), Node(), Node() ) ) ) { + // kickoffUriOrId() cannot be a URI without a slash (ugly hack for a tiny speed gain) + if( kickoffUriOrId().contains('/') && + model->containsAnyStatement( Statement( QUrl( kickoffUriOrId() ), Node(), Node() ) ) ) { // // The kickoffUriOrId is actually a URI // @@ -549,13 +592,15 @@ // // The resource does not exist, create a new one: // If the kickoffUriOrId is a valid URI we use it as such, otherwise we create a new URI - // Special case: files: paths are always converted to URIs + // Special case: files: paths are always converted to URIs (but we only allow absolute paths, + // otherwise there can be false positives when for example a tag has the same name as a folder) // QUrl uri( kickoffUriOrId() ); if ( uri.isValid() && !uri.scheme().isEmpty() ) { m_uri = uri; } - else if ( QFile::exists( kickoffUriOrId() ) ) { + else if ( kickoffUriOrId()[0] == '/' && + QFile::exists( kickoffUriOrId() ) ) { // KURL defaults to schema "file:" m_uri = KUrl::fromPath( kickoffUriOrId() ); } Index: nepomuk/core/resourcedata.h =================================================================== --- nepomuk/core/resourcedata.h (revision 842405) +++ nepomuk/core/resourcedata.h (revision 842406) @@ -84,6 +84,8 @@ QList<QUrl> allTypes(); + void setTypes( const QList<QUrl>& types ); + QHash<QUrl, Variant> allProperties(); bool hasProperty( const QUrl& uri ); @@ -183,6 +185,7 @@ private: bool constHasType( const QUrl& type ) const; + void loadType( const QUrl& type ); /** * The kickoff URI or ID is used as long as the resource has not been synced yet @@ -216,6 +219,9 @@ QHash<QUrl, Variant> m_cache; bool m_cacheDirty; + // used to prevent countless model operations in store() + bool m_initialTypeSaved; + friend class ResourceManager; }; } Index: nepomuk/core/resource.cpp =================================================================== --- nepomuk/core/resource.cpp (revision 842405) +++ nepomuk/core/resource.cpp (revision 842406) @@ -83,6 +83,7 @@ Nepomuk::Resource::~Resource() { + // FIXME: ResourceData instances having a proxy also need to be deleted, maybe extend deref if( m_data && m_data->deref() == 0 && !m_data->isValid() ) { m_data->deleteData(); } @@ -150,6 +151,20 @@ } +void Nepomuk::Resource::setTypes( const QList<QUrl>& types ) +{ + if ( m_data ) + m_data->setTypes( types ); +} + + +void Nepomuk::Resource::addType( const QUrl& type ) +{ + if ( m_data ) + setTypes( types() << type ); +} + + bool Nepomuk::Resource::hasType( const QUrl& typeUri ) const { return m_data ? m_data->hasType( typeUri ) : false; @@ -321,6 +336,7 @@ QString Nepomuk::Resource::genericIcon() const { + // FIXME: support resource symbols Variant symbol = property( Soprano::Vocabulary::NAO::hasSymbol() ); if ( symbol.isString() ) { return symbol.toString(); Index: nepomuk/core/resource.h =================================================================== --- nepomuk/core/resource.h (revision 842405) +++ nepomuk/core/resource.h (revision 842406) @@ -222,6 +222,20 @@ QList<QUrl> types() const; /** + * Set the types of the resource. Previous types will be overwritten. + * + * \since 4.2 + */ + void setTypes( const QList<QUrl>& types ); + + /** + * Add a type to the list of types. + * + * \since 4.2 + */ + void addType( const QUrl& type ); + + /** * Check if the resource is of a certain type. The type hierarchy * is checked including subclass relations. */ Index: nepomuk/Mainpage.dox =================================================================== --- nepomuk/Mainpage.dox (revision 842405) +++ nepomuk/Mainpage.dox (revision 842406) @@ -25,14 +25,7 @@ This is the KDE Meta Data library (not to confuse with KFileMetaData). -@warning The Nepomuk API may be subject to change before the release of -KDE 4.1. Using these classes in applications that are part of the KDE core -packages is no problem at all; API changes will be merged in quickly. -However, if you intend to use this API in external applications please -be aware that your application might stop working with KDE 4.1. Changes to -the API will be announced on the kde-core mailing list. - \section overview The General Idea Three types of meta data can be identified: @@ -82,8 +75,8 @@ - \ref hacking - \ref examples - \ref nepomuk-rcgen +- <a href="http://techbase.kde.org/Development/Tutorials#Nepomuk">The Nepomuk techbase tutorials</a> - \authors Sebastian Trueg \<trueg@kde.org\> @@ -241,7 +234,8 @@ << it.next().genericLabel(); \endcode -Reading the information using the convenience classes: +Reading the information using the convenience classes (be aware that these classes need to be generated +from an ontology using the \ref nepomuk-rcgen "Resource Generator"): \code Nepomuk::File f( "/home/foo/bar.txt" );