Multiple Redland RDF models seem to contain the same RDF nodes from the model that was parsed last

StackOverflow https://stackoverflow.com/questions/16810319

  •  30-05-2022
  •  | 
  •  

Question

I've now isolated a problem with the Redland RDF libraries that I had also previously asked about. So I'm still wondering: what causes the following test case to fail?

Essentially what should happen is the following: I'm constructing two RDF models, each of which contains an event ID. I use these event IDs as keys into a dictionary (hashtable). Since RDF models are C structs I wrap them inside an Objective C class to obtain something that can be stored in a dictionary. Now when I try to retrieve the two RDF models from the dictionary via these distinct keys, I always get in effect the same RDF model back. The pointer values for the RDF models are distinct, but all contained RDF nodes are apparently the same. What am I missing? (BTW, I'm pretty sure the problem is not an artifact of all the obfuscation :)

//
//  RedlandRdfStandaloneTests.h
//

#import <SenTestingKit/SenTestingKit.h>

@interface RedlandRdfStandaloneTests : SenTestCase

@end

//
//  RedlandRdfStandaloneTests.m
//

#import "librdf.h"

#import "RdfModelWrapper.h"
#import "RedlandRdfStandaloneTests.h"

// namespace URIs
#define OBFUSCATED6_NAMESPACE @"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED7#"

// datatype properties
#define OBFUSCATED6_EVENT_ID_LOCAL_NAME @"eventId"

// individuals
#define OBFUSCATED6_CALENDAR_EVENT_LOCAL_NAME @"CalendarEvent"

#define IDENTIFIER_1 @"dc4bc97ugu7kl5hoh2spmanob4"
#define IDENTIFIER_2 @"cbd3mghbgas8juuh4l88rt04ec"

@interface RedlandRdfStandaloneTests ()

@property (assign, readonly) librdf_world   *rdfWorld;
@property (assign, readonly) librdf_storage *rdfStorage;
@property (assign, readonly) librdf_node    *rdfTypeProperty;
@property (assign, readonly) librdf_uri     *obfuscated8NamespaceUri;
@property (assign, readonly) librdf_node    *obfuscated8EventIdProperty;
@property (assign, readonly) librdf_node    *obfuscated8CalendarEventIndividual;
@property (assign, readonly) librdf_parser  *rdfParser;
@property (assign, readonly) librdf_uri     *baseUri;

@property (strong, readonly) NSString *rdfModel1AsString;
@property (strong, readonly) NSString *rdfModel2AsString;

@end

@implementation RedlandRdfStandaloneTests

- (void)test
{
    NSMutableDictionary *identifiersToRdfModelWrappers = [NSMutableDictionary dictionary];

    librdf_model *rdfModel;

    {
        rdfModel = librdf_new_model(_rdfWorld, _rdfStorage, NULL);
        librdf_parser_parse_string_into_model(_rdfParser, (const unsigned char *)_rdfModel1AsString.UTF8String, _baseUri, rdfModel);
        RdfModelWrapper *rdfModelWrapper = [[RdfModelWrapper alloc] initWithRdfModel:rdfModel];
        [identifiersToRdfModelWrappers setObject:rdfModelWrapper forKey:IDENTIFIER_1];

        [self writeModel:rdfModel withRdfWorld:_rdfWorld]; // #1: ok -- same as _rdfModel1AsString with eventId same as IDENTIFIER_1
    }
    {
        rdfModel = librdf_new_model(_rdfWorld, _rdfStorage, NULL);
        librdf_parser_parse_string_into_model(_rdfParser, (const unsigned char *)_rdfModel2AsString.UTF8String, _baseUri, rdfModel);
        RdfModelWrapper *rdfModelWrapper = [[RdfModelWrapper alloc] initWithRdfModel:rdfModel];
        [identifiersToRdfModelWrappers setObject:rdfModelWrapper forKey:IDENTIFIER_2];

        [self writeModel:rdfModel withRdfWorld:_rdfWorld]; // #2: ok -- same as _rdfModel2AsString with eventId same as IDENTIFIER_2
    }

    {
        RdfModelWrapper *rdfModelWrapper = [identifiersToRdfModelWrappers objectForKey:IDENTIFIER_1];
        rdfModel = rdfModelWrapper.rdfModel;
        librdf_node *eventResource  = librdf_model_get_source(rdfModel, _rdfTypeProperty, _obfuscated8CalendarEventIndividual);
        librdf_node *eventIdLiteral = librdf_model_get_target(rdfModel, eventResource, _obfuscated8EventIdProperty);
        NSString    *eventId        = [NSString stringWithCString:librdf_node_get_literal_value_as_latin1(eventIdLiteral) encoding:NSISOLatin1StringEncoding];

        [self writeModel:rdfModel withRdfWorld:_rdfWorld]; // #3: error -- same as _rdfModel2AsString with eventId same as IDENTIFIER_2
        STAssertEqualObjects(IDENTIFIER_1, eventId, nil); // #4: fails
    }
    {
        RdfModelWrapper *rdfModelWrapper = [identifiersToRdfModelWrappers objectForKey:IDENTIFIER_2];
        rdfModel = rdfModelWrapper.rdfModel;
        librdf_node *eventResource  = librdf_model_get_source(rdfModel, _rdfTypeProperty, _obfuscated8CalendarEventIndividual);
        librdf_node *eventIdLiteral = librdf_model_get_target(rdfModel, eventResource, _obfuscated8EventIdProperty);
        NSString    *eventId        = [NSString stringWithCString:librdf_node_get_literal_value_as_latin1(eventIdLiteral) encoding:NSISOLatin1StringEncoding];

        [self writeModel:rdfModel withRdfWorld:_rdfWorld]; // #5: ok -- same as _rdfModel2AsString with eventId same as IDENTIFIER_2
        STAssertEqualObjects(IDENTIFIER_2, eventId, nil); // #6: ok
    }
}

- (void)writeModel:(librdf_model *)rdfModel withRdfWorld:(librdf_world *)rdfWorld
{
    raptor_world *raptorWorld = librdf_world_get_raptor(rdfWorld);

    raptor_iostream *stream = raptor_new_iostream_to_file_handle(raptorWorld, stdout);
    NSAssert(stream != nil, @"cannot create stream");

    int flag = librdf_model_write(rdfModel, stream);
    NSAssert(flag == 0, @"librdf_model_write() failed: %d", flag);

    raptor_free_iostream(stream);
}

#pragma mark -
#pragma mark override SenTestCase

- (void)setUp
{
    _rdfWorld = librdf_new_world();
    librdf_world_open(_rdfWorld);

    _rdfStorage = librdf_new_storage(_rdfWorld, "memory", NULL, NULL);

    _rdfTypeProperty = LIBRDF_MS_type(_rdfWorld);

    _obfuscated8NamespaceUri = librdf_new_uri(_rdfWorld, (const unsigned char *)OBFUSCATED6_NAMESPACE.UTF8String);

    _obfuscated8EventIdProperty = librdf_new_node_from_uri_local_name(_rdfWorld, _obfuscated8NamespaceUri, (const unsigned char *)OBFUSCATED6_EVENT_ID_LOCAL_NAME.UTF8String);

    _obfuscated8CalendarEventIndividual = librdf_new_node_from_uri_local_name(_rdfWorld, _obfuscated8NamespaceUri, (const unsigned char *)OBFUSCATED6_CALENDAR_EVENT_LOCAL_NAME.UTF8String);

    _rdfParser = librdf_new_parser(_rdfWorld, NULL, "application/rdf+xml", NULL);
    NSAssert(_rdfParser != nil, @"no RDF parser");

    _baseUri = librdf_new_uri(_rdfWorld, (const unsigned char *)"http://www.w3.org/1999/02/22-rdf-syntax-ns#");

    _rdfModel1AsString = @"<rdf:RDF \
    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" \
    xmlns:j.0=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED7#\" \
    xmlns:owl=\"http://www.w3.org/2002/07/owl#\" \
    xmlns=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED2#\" \
    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema#\" \
    xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\" \
    xmlns:mo=\"http://purl.org/ontology/mo/\">\
    <rdf:Description rdf:about=\"http://www.OBFUSCATED5.com/event/show/2224183\">\
    <rdf:type rdf:resource=\"http://www.w3.org/2000/01/rdf-schema#Resource\"/>\
    <rdf:type rdf:resource=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED7#CalendarEvent\"/>\
    <j.0:eventId>dc4bc97ugu7kl5hoh2spmanob4</j.0:eventId>\
    <j.0:hasOrganization rdf:resource=\"http://www.OBFUSCATED1.com/2013/05/MDW#MDW\"/>\
    </rdf:Description>\
    </rdf:RDF>";

    _rdfModel2AsString = @"<rdf:RDF \
    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" \
    xmlns:j.0=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED7#\" \
    xmlns:owl=\"http://www.w3.org/2002/07/owl#\" \
    xmlns=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED2#\" \
    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema#\" \
    xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\" \
    xmlns:mo=\"http://purl.org/ontology/mo/\">\
    <rdf:Description rdf:about=\"http://www.OBFUSCATED4.com/veranstaltungen/uebersicht/veranstaltung-details/event/201305221830/OBFUSCATED9/\">\
    <rdf:type rdf:resource=\"http://www.w3.org/2000/01/rdf-schema#Resource\"/>\
    <rdf:type rdf:resource=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED7#CalendarEvent\"/>\
    <j.0:eventId>cbd3mghbgas8juuh4l88rt04ec</j.0:eventId>\
    <j.0:hasOrganization rdf:resource=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED3#OBFUSCATED3\"/>\
    <j.0:hasLocation rdf:resource=\"http://www.OBFUSCATED1.com/2013/05/OBFUSCATED3#Konzertsaal\"/>\
    </rdf:Description>\
    </rdf:RDF>";

    [super setUp];
}

- (void)tearDown
{
    librdf_free_node(_obfuscated8EventIdProperty);
    librdf_free_node(_obfuscated8CalendarEventIndividual);

    librdf_free_uri(_obfuscated8NamespaceUri);

    librdf_free_node(_rdfTypeProperty);

    librdf_free_uri(_baseUri);

    librdf_free_parser(_rdfParser);

    librdf_free_storage(_rdfStorage);

    librdf_free_world(_rdfWorld);

    [super tearDown];
}

@end

//
//  RdfModelWrapper.h
//

#import "librdf.h"

@interface RdfModelWrapper : NSObject

@property (assign, readonly) librdf_model *rdfModel;

- (id)initWithRdfModel:(librdf_model *)rdfModel;

@end

//
//  RdfModelWrapper.m
//

#import "RdfModelWrapper.h"

@implementation RdfModelWrapper

- (id)initWithRdfModel:(librdf_model *)rdfModel
{
    self = [super init];

    if (self) {
        _rdfModel = rdfModel; // RDFModelWrapper effectively takes ownership of rdfModel, i.e. is in charge of freeing it
    }

    return self;
}

- (void)dealloc
{
    librdf_free_model(_rdfModel);
}

@end
Was it helpful?

Solution

librdf models are backed by a storage. Both your models are backed by the same memory storage and therefore contain the same data, even if the model instances are different.

You are also using query functions librdf_model_get_source() and librdf_model_get_target() that just return the first match if any, not functions that return all matching statements in a librdf_iterator, such as librdf_model_get_sources(), librdf_model_get_targets() or generic librdf_model_find_statements(). That's why you get the same nodes back for all queries.

So, if you want to work on distinct models, make sure their backing storages are also distinct.

(I remember dajobe had some reasoning for keeping models and storages separate but can't remember what it was.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top