/* -*- Mode: c; c-basic-offset: 2 -*- * * rdf_statement.c - RDF Triple (Statement) interface * * Copyright (C) 2000-2008, David Beckett http://www.dajobe.org/ * Copyright (C) 2000-2005, University of Bristol, UK http://www.bristol.ac.uk/ * * This package is Free Software and part of Redland http://librdf.org/ * * It is licensed under the following three licenses as alternatives: * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version * 2. GNU General Public License (GPL) V2 or any newer version * 3. Apache License, V2.0 or any newer version * * You may not use this file except in compliance with at least one of * the above three licenses. * * See LICENSE.html or LICENSE.txt at the top of this package for the * complete terms and further detail along with the license texts for * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively. * * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 #include #endif #include #include #ifdef HAVE_STDLIB_H #include /* for abort() as used in errors */ #endif #include #ifndef STANDALONE /* class methods */ /** * librdf_init_statement: * @world: redland world object * * INTERNAL - Initialise the statement module. * **/ void librdf_init_statement(librdf_world *world) { } /** * librdf_finish_statement: * @world: redland world object * * INTERNAL - Terminate the statement module. * **/ void librdf_finish_statement(librdf_world *world) { } /** * librdf_new_statement: * @world: redland world object * * Constructor - create a new empty #librdf_statement. * * Return value: a new #librdf_statement or NULL on failure **/ librdf_statement* librdf_new_statement(librdf_world *world) { librdf_statement* new_statement; librdf_world_open(world); new_statement=(librdf_statement*)LIBRDF_CALLOC(librdf_statement, 1, sizeof(librdf_statement)); if(!new_statement) return NULL; new_statement->world=world; return new_statement; } /** * librdf_new_statement_from_statement: * @statement: #librdf_statement to copy * * Copy constructor - create a new librdf_statement from an existing librdf_statement. * * Return value: a new #librdf_statement with copy or NULL on failure **/ librdf_statement* librdf_new_statement_from_statement(librdf_statement* statement) { librdf_statement* new_statement; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); if(!statement) return NULL; new_statement = librdf_new_statement(statement->world); if(!new_statement) return NULL; if(statement->subject) { new_statement->subject=librdf_new_node_from_node(statement->subject); if(!new_statement->subject) { librdf_free_statement(new_statement); return NULL; } } if(statement->predicate) { new_statement->predicate=librdf_new_node_from_node(statement->predicate); if(!new_statement->predicate) { librdf_free_statement(new_statement); return NULL; } } if(statement->object) { new_statement->object=librdf_new_node_from_node(statement->object); if(!new_statement->object) { librdf_free_statement(new_statement); return NULL; } } return new_statement; } /** * librdf_new_statement_from_nodes: * @world: redland world object * @subject: #librdf_node * @predicate: #librdf_node * @object: #librdf_node * * Constructor - create a new #librdf_statement from existing #librdf_node objects. * * The node objects become owned by the new statement (or freed on error). * * Return value: a new #librdf_statement with copy or NULL on failure **/ librdf_statement* librdf_new_statement_from_nodes(librdf_world *world, librdf_node* subject, librdf_node* predicate, librdf_node* object) { librdf_statement* new_statement; librdf_world_open(world); new_statement = librdf_new_statement(world); if(!new_statement) { if(subject) librdf_free_node(subject); if(predicate) librdf_free_node(predicate); if(object) librdf_free_node(object); return NULL; } new_statement->subject=subject; new_statement->predicate=predicate; new_statement->object=object; return new_statement; } /** * librdf_statement_init: * @world: redland world object * @statement: #librdf_statement object * * Initialise a statically declared librdf_statement. * * This MUST be called on a statically declared librdf_statement * to initialise it properly. It is the responsibility of the * user of the statically allocated librdf_statement to deal * with deallocation of any statement parts (subject, predicate, object). **/ void librdf_statement_init(librdf_world *world, librdf_statement *statement) { librdf_world_open(world); LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); statement->world=world; statement->subject=NULL; statement->predicate=NULL; statement->object=NULL; } /** * librdf_statement_clear: * @statement: #librdf_statement object * * Empty a librdf_statement of nodes. * **/ void librdf_statement_clear(librdf_statement *statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); if(statement->subject) { librdf_free_node(statement->subject); statement->subject=NULL; } if(statement->predicate) { librdf_free_node(statement->predicate); statement->predicate=NULL; } if(statement->object) { librdf_free_node(statement->object); statement->object=NULL; } } /** * librdf_free_statement: * @statement: #librdf_statement object * * Destructor - destroy a #librdf_statement. * **/ void librdf_free_statement(librdf_statement* statement) { #ifdef WITH_THREADS librdf_world *world; #endif LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); #ifdef WITH_THREADS world = statement->world; pthread_mutex_lock(world->statements_mutex); #endif librdf_statement_clear(statement); #ifdef WITH_THREADS pthread_mutex_unlock(world->statements_mutex); #endif LIBRDF_FREE(librdf_statement, statement); } /* methods */ /** * librdf_statement_get_subject: * @statement: #librdf_statement object * * Get the statement subject. * * This method returns a SHARED pointer to the subject which must * be copied by the caller if needed. * * Return value: a pointer to the #librdf_node of the statement subject - **/ librdf_node* librdf_statement_get_subject(librdf_statement *statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); return statement->subject; } /** * librdf_statement_set_subject: * @statement: #librdf_statement object * @node: #librdf_node of subject * * Set the statement subject. * * The subject passed in becomes owned by * the statement object and must not be used by the caller after this call. **/ void librdf_statement_set_subject(librdf_statement *statement, librdf_node *node) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); statement->subject=node; } /** * librdf_statement_get_predicate: * @statement: #librdf_statement object * * Get the statement predicate. * * This method returns a SHARED pointer to the predicate which must * be copied by the caller if needed. * * Return value: a pointer to the #librdf_node of the statement predicate - **/ librdf_node* librdf_statement_get_predicate(librdf_statement *statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); return statement->predicate; } /** * librdf_statement_set_predicate: * @statement: #librdf_statement object * @node: #librdf_node of predicate * * Set the statement predicate. * * The predicate passed in becomes owned by * the statement object and must not be used by the caller after this call. **/ void librdf_statement_set_predicate(librdf_statement *statement, librdf_node *node) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); statement->predicate=node; } /** * librdf_statement_get_object: * @statement: #librdf_statement object * * Get the statement object. * * This method returns a SHARED pointer to the object which must * be copied by the caller if needed. * * Return value: a pointer to the #librdf_node of the statement object - **/ librdf_node* librdf_statement_get_object(librdf_statement *statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); return statement->object; } /** * librdf_statement_set_object: * @statement: #librdf_statement object * @node: #librdf_node of object * * Set the statement object. * * The object passed in becomes owned by * the statement object and must not be used by the caller after this call. **/ void librdf_statement_set_object(librdf_statement *statement, librdf_node *node) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); statement->object=node; } /** * librdf_statement_is_complete: * @statement: #librdf_statement object * * Check if statement is a complete and legal RDF triple. * * Checks that all subject, predicate, object fields are present * and they have the allowed node types. * * Return value: non 0 if the statement is complete and legal **/ int librdf_statement_is_complete(librdf_statement *statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 0); if(!statement->subject || (!librdf_node_is_resource(statement->subject) && !librdf_node_is_blank(statement->subject))) return 0; if(!statement->predicate || !librdf_node_is_resource(statement->predicate)) return 0; if(!statement->object) return 0; return 1; } /** * librdf_statement_to_string: * @statement: the statement * * Format the librdf_statement as a string. * * Formats the statement as a newly allocate string that must be freed by * the caller. * * Return value: the string or NULL on failure. **/ unsigned char * librdf_statement_to_string(librdf_statement *statement) { unsigned char *subject_string, *predicate_string, *object_string; unsigned char *s; int statement_string_len=0; const char *format; #define NULL_STRING_LENGTH 6 static const unsigned char * const null_string=(const unsigned char *)"(null)"; size_t len; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); if(statement->subject) { subject_string=librdf_node_to_counted_string(statement->subject, &len); if(!subject_string) return NULL; statement_string_len += len; } else { subject_string=(unsigned char*)null_string; statement_string_len += NULL_STRING_LENGTH; } if(statement->predicate) { predicate_string=librdf_node_to_counted_string(statement->predicate, &len); if(!predicate_string) { if(subject_string != null_string) LIBRDF_FREE(cstring, subject_string); return NULL; } statement_string_len += len; } else { predicate_string=(unsigned char*)null_string; statement_string_len += NULL_STRING_LENGTH; } if(statement->object) { object_string=librdf_node_to_counted_string(statement->object, &len); if(!object_string) { if(subject_string != null_string) LIBRDF_FREE(cstring, subject_string); if(predicate_string != null_string) LIBRDF_FREE(cstring, predicate_string); return NULL; } statement_string_len += len; } else { object_string=(unsigned char*)null_string; statement_string_len += NULL_STRING_LENGTH; } #define LIBRDF_STATEMENT_FORMAT_STRING_LITERAL "{%s, %s, \"%s\"}" #define LIBRDF_STATEMENT_FORMAT_RESOURCE_LITERAL "{%s, %s, %s}" statement_string_len += + 1 + /* "{" %s */ 2 + /* ", " %s */ 2 + /* ", " %s */ 1; /* "}" */ if(statement->object && librdf_node_get_type(statement->object) == LIBRDF_NODE_TYPE_LITERAL) { format=LIBRDF_STATEMENT_FORMAT_STRING_LITERAL; statement_string_len+=2; /* Extra "" around literal */ } else { format=LIBRDF_STATEMENT_FORMAT_RESOURCE_LITERAL; } s=(unsigned char*)LIBRDF_MALLOC(cstring, statement_string_len+1); if(s) sprintf((char*)s, format, subject_string, predicate_string, object_string); /* always free allocated intermediate strings */ if(subject_string != null_string) LIBRDF_FREE(cstring, subject_string); if(predicate_string != null_string) LIBRDF_FREE(cstring, predicate_string); if(object_string != null_string) LIBRDF_FREE(cstring, object_string); return s; } /** * librdf_statement_print: * @statement: the statement * @fh: file handle * * Pretty print the statement to a file descriptor. * * This method is for debugging and the format of the output should * not be relied on. * **/ void librdf_statement_print(librdf_statement *statement, FILE *fh) { unsigned char *s; LIBRDF_ASSERT_OBJECT_POINTER_RETURN(statement, librdf_statement); if(!statement) return; s=librdf_statement_to_string(statement); if(!s) return; fputs((const char*)s, fh); LIBRDF_FREE(cstring, s); } /** * librdf_statement_equals: * @statement1: first #librdf_statement * @statement2: second #librdf_statement * * Check if two statements are equal. * * Return value: non 0 if statements are equal **/ int librdf_statement_equals(librdf_statement* statement1, librdf_statement* statement2) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement1, librdf_statement, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement2, librdf_statement, 0); if(!statement1 || !statement2) return 0; if(!librdf_node_equals(statement1->subject, statement2->subject)) return 0; if(!librdf_node_equals(statement1->predicate, statement2->predicate)) return 0; if(!librdf_node_equals(statement1->object, statement2->object)) return 0; return 1; } /** * librdf_statement_match: * @statement: statement * @partial_statement: statement with possible empty parts * * Match a statement against a 'partial' statement. * * A partial statement is where some parts of the statement - * subject, predicate or object can be empty (NULL). * Empty parts match against any value, parts with values * must match exactly. Node matching is done via librdf_node_equals() * * Return value: non 0 on match **/ int librdf_statement_match(librdf_statement* statement, librdf_statement* partial_statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(partial_statement, librdf_statement, 0); if(partial_statement->subject && !librdf_node_equals(statement->subject, partial_statement->subject)) return 0; if(partial_statement->predicate && !librdf_node_equals(statement->predicate, partial_statement->predicate)) return 0; if(partial_statement->object && !librdf_node_equals(statement->object, partial_statement->object)) return 0; return 1; } /** * librdf_statement_encode: * @statement: the statement to serialise * @buffer: the buffer to use * @length: buffer size * * Serialise a statement into a buffer. * * Encodes the given statement in the buffer, which must be of sufficient * size. If buffer is NULL, no work is done but the size of buffer * required is returned. * * Return value: the number of bytes written or 0 on failure. **/ size_t librdf_statement_encode(librdf_statement* statement, unsigned char *buffer, size_t length) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 0); return librdf_statement_encode_parts(statement, NULL, buffer, length, LIBRDF_STATEMENT_ALL); } /** * librdf_statement_encode_parts: * @statement: statement to serialise * @context_node: #librdf_node context node (can be NULL) * @buffer: the buffer to use * @length: buffer size * @fields: fields to encode * * Serialise parts of a statement into a buffer. * * Encodes the given statement in the buffer, which must be of sufficient * size. If buffer is NULL, no work is done but the size of buffer * required is returned. * * The fields values are or-ed combinations of: * LIBRDF_STATEMENT_SUBJECT LIBRDF_STATEMENT_PREDICATE * LIBRDF_STATEMENT_OBJECT * or LIBRDF_STATEMENT_ALL for subject,prdicate,object fields * * If context_node is given, it is encoded also * * Return value: the number of bytes written or 0 on failure. **/ size_t librdf_statement_encode_parts(librdf_statement* statement, librdf_node* context_node, unsigned char *buffer, size_t length, librdf_statement_part fields) { size_t total_length=0; size_t node_len; unsigned char *p; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 0); /* min size */ if(buffer && length < 1) return 0; p=buffer; /* magic number 'x' */ if(p) { *p++='x'; length--; } total_length++; if((fields & LIBRDF_STATEMENT_SUBJECT) && statement->subject) { /* 's' + subject */ if(p) { *p++='s'; length--; } total_length++; node_len=librdf_node_encode(statement->subject, p, length); if(!node_len) return 0; if(p) { p += node_len; length -= node_len; } total_length += node_len; } if((fields & LIBRDF_STATEMENT_PREDICATE) && statement->predicate) { /* 'p' + predicate */ if(p) { *p++='p'; length--; } total_length++; node_len=librdf_node_encode(statement->predicate, p, length); if(!node_len) return 0; if(p) { p += node_len; length -= node_len; } total_length += node_len; } if((fields & LIBRDF_STATEMENT_OBJECT) && statement->object) { /* 'o' object */ if(p) { *p++='o'; length--; } total_length++; node_len= librdf_node_encode(statement->object, p, length); if(!node_len) return 0; if(p) { p += node_len; length -= node_len; } total_length += node_len; } if(context_node) { /* 'o' object */ if(p) { *p++='c'; length--; } total_length++; node_len= librdf_node_encode(context_node, p, length); if(!node_len) return 0; if(p) { p += node_len; length -= node_len; } total_length += node_len; } return total_length; } /** * librdf_statement_decode: * @statement: the statement to deserialise into * @buffer: the buffer to use * @length: buffer size * * Decodes a statement from a buffer. * * Decodes the serialised statement (as created by librdf_statement_encode() ) * from the given buffer. * * Return value: number of bytes used or 0 on failure (bad encoding, allocation failure) **/ size_t librdf_statement_decode(librdf_statement* statement, unsigned char *buffer, size_t length) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 0); return librdf_statement_decode_parts(statement, NULL, buffer, length); } /** * librdf_statement_decode_parts: * @statement: the statement to deserialise into * @context_node: pointer to #librdf_node context_node to deserialise into * @buffer: the buffer to use * @length: buffer size * * Decodes a statement + context node from a buffer. * * Decodes the serialised statement (as created by librdf_statement_encode() ) * from the given buffer. If a context node is found and context_node is * not NULL, a pointer to the new #librdf_node is stored in *context_node. * * Return value: number of bytes used or 0 on failure (bad encoding, allocation failure) **/ size_t librdf_statement_decode_parts(librdf_statement* statement, librdf_node** context_node, unsigned char *buffer, size_t length) { unsigned char *p; librdf_node* node; unsigned char type; size_t total_length=0; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 0); #if defined(LIBRDF_DEBUG) && LIBRDF_DEBUG > 1 LIBRDF_DEBUG2("Decoding buffer of %d bytes\n", length); #endif /* absolute minimum - first byte is type */ if(length < 1) return 0; p=buffer; if(*p++ != 'x') return 0; length--; total_length++; while(length>0) { size_t node_len; type=*p++; length--; total_length++; if(!length) return 0; if(!(node=librdf_node_decode(statement->world, &node_len, p, length))) return 0; p += node_len; length -= node_len; total_length += node_len; #if defined(LIBRDF_DEBUG) && LIBRDF_DEBUG > 1 LIBRDF_DEBUG3("Found type %c (%d bytes)\n", type, node_len); #endif switch(type) { case 's': /* subject */ statement->subject=node; break; case 'p': /* predicate */ statement->predicate=node; break; case 'o': /* object */ statement->object=node; break; case 'c': /* context */ if(context_node) *context_node=node; else librdf_free_node(node); break; default: librdf_log(statement->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STATEMENT, NULL, "Illegal statement encoding '%c' seen", p[-1]); return 0; } } return total_length; } #endif /* TEST CODE */ #ifdef STANDALONE /* one more prototype */ int main(int argc, char *argv[]); int main(int argc, char *argv[]) { librdf_statement *statement, *statement2; int size, size2; const char *program=librdf_basename((const char*)argv[0]); char *s, *buffer; librdf_world *world; world=librdf_new_world(); librdf_world_open(world); fprintf(stderr, "%s: Creating statement\n", program); statement=librdf_new_statement(world); s=(char*)librdf_statement_to_string(statement); fprintf(stderr, "%s: Empty statement: %s\n", program, s); LIBRDF_FREE(cstring, s); librdf_statement_set_subject(statement, librdf_new_node_from_uri_string(world, (const unsigned char*)"http://purl.org/net/dajobe/")); librdf_statement_set_predicate(statement, librdf_new_node_from_uri_string(world, (const unsigned char*)"http://purl.org/dc/elements/1.1/#Creator")); librdf_statement_set_object(statement, librdf_new_node_from_literal(world, (const unsigned char*)"Dave Beckett", NULL, 0)); s=(char*)librdf_statement_to_string(statement); fprintf(stderr, "%s: Resulting statement: %s\n", program, s); LIBRDF_FREE(cstring, s); size=librdf_statement_encode(statement, NULL, 0); fprintf(stdout, "%s: Encoding statement requires %d bytes\n", program, size); buffer=(char*)LIBRDF_MALLOC(cstring, size); fprintf(stdout, "%s: Encoding statement in buffer\n", program); size2=librdf_statement_encode(statement, (unsigned char*)buffer, size); if(size2 != size) { fprintf(stderr, "%s: Encoding statement used %d bytes, expected it to use %d\n", program, size2, size); return(1); } fprintf(stdout, "%s: Creating new statement\n", program); statement2=librdf_new_statement(world); fprintf(stdout, "%s: Decoding statement from buffer\n", program); if(!librdf_statement_decode(statement2, (unsigned char*)buffer, size)) { fprintf(stderr, "%s: Decoding statement failed\n", program); return(1); } LIBRDF_FREE(cstring, buffer); fprintf(stdout, "%s: New statement is: ", program); librdf_statement_print(statement2, stdout); fputs("\n", stdout); fprintf(stdout, "%s: Freeing statements\n", program); librdf_free_statement(statement2); librdf_free_statement(statement); librdf_free_world(world); /* keep gcc -Wall happy */ return(0); } #endif