diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:40:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:40:05 +0000 |
commit | 4038ab95a094b363f1748f3dcb51511a1217475d (patch) | |
tree | 7f393d66a783f91ddd263c78d681e485cf4f45ca /src/raptor_serialize_rss.c | |
parent | Initial commit. (diff) | |
download | raptor2-upstream.tar.xz raptor2-upstream.zip |
Adding upstream version 2.0.16.upstream/2.0.16upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/raptor_serialize_rss.c')
-rw-r--r-- | src/raptor_serialize_rss.c | 2425 |
1 files changed, 2425 insertions, 0 deletions
diff --git a/src/raptor_serialize_rss.c b/src/raptor_serialize_rss.c new file mode 100644 index 0000000..90f1f23 --- /dev/null +++ b/src/raptor_serialize_rss.c @@ -0,0 +1,2425 @@ +/* -*- Mode: c; c-basic-offset: 2 -*- + * + * raptor_serialize_rss.c - Raptor RSS 1.0 and Atom 1.0 serializers + * + * Copyright (C) 2003-2015, David Beckett http://www.dajobe.org/ + * Copyright (C) 2003-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 <raptor_config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +/* Raptor includes */ +#include "raptor2.h" +#include "raptor_internal.h" +#include "raptor_rss.h" + + +typedef struct { + raptor_world* world; + raptor_term* term; + /* shared pointer */ + raptor_rss_item* item; +} raptor_rss_group_map; + + + +/* + * Raptor 'RSS 1.0' serializer object + */ +typedef struct { + raptor_world* world; + + /* static rss model */ + raptor_rss_model model; + + /* Triples with no assigned type node */ + raptor_sequence *triples; + + /* Sequence of raptor_rss_item* : rdf:Seq items rdf:_ < n> at offset n */ + raptor_sequence *items; + + /* Sequence of raptor_rss_item* (?x rdf:type rss:Enclosure) */ + raptor_sequence *enclosures; + + /* Term of rdf:Seq node */ + raptor_term *seq_term; + + /* Namespace stack for serializing */ + raptor_namespace_stack *nstack; + + /* the default namespace (rdf: or atom:) - + * this is destroyed when nstack above is deleted + */ + raptor_namespace* default_nspace; + + /* rdf namespace in @nspaces array */ + raptor_namespace* rdf_nspace; + + /* the xml: namespace */ + raptor_namespace *xml_nspace; + + /* the root element (rdf:RDF or atom:feed) */ + raptor_xml_element* root_element; + + /* where the xml is being written */ + raptor_xml_writer *xml_writer; + + /* non-0 if this is an atom 1.0 serializer */ + int is_atom; + + /* 0 = none + * 1 = existing rss:item item containg rdf/xml encoding of any extra + * triples about URI (rss-1.0 serializer only) + * 2 = at:md element containing rdf/xml property elements encoding + * of any extra triples about URI (atom serializer only) + */ + int rss_triples_mode; + + /* namespaces declared here */ + raptor_namespace* nspaces[RAPTOR_RSS_NAMESPACES_SIZE]; + + /* Map of group URI (key, owned) : rss item object (value, shared) */ + raptor_avltree *group_map; + + /* User declared namespaces */ + raptor_sequence *user_namespaces; + + /* URI of XML Literal datatype */ + raptor_uri* xml_literal_dt; + + int free_default_nspace; +} raptor_rss10_serializer_context; + + +static void +raptor_free_group_map(raptor_rss_group_map* gm) +{ + if(gm->term) + raptor_free_term(gm->term); + + RAPTOR_FREE(raptor_rss_group_map, gm); +} + + +static int +raptor_rss_group_map_compare(raptor_rss_group_map* gm1, + raptor_rss_group_map* gm2) +{ + return raptor_term_compare(gm1->term, gm2->term); +} + + +static raptor_rss_item* +raptor_rss10_get_group_item(raptor_rss10_serializer_context *rss_serializer, + raptor_term* term) +{ + raptor_rss_group_map search_gm; + raptor_rss_group_map* gm; + + search_gm.world = rss_serializer->world; + search_gm.term = term; + gm = (raptor_rss_group_map*)raptor_avltree_search(rss_serializer->group_map, + (void*)&search_gm); + + return gm ? gm->item : NULL; +} + + +static int +raptor_rss10_set_item_group(raptor_rss10_serializer_context *rss_serializer, + raptor_term* term, raptor_rss_item *item) +{ + raptor_rss_group_map* gm; + + if(raptor_rss10_get_group_item(rss_serializer, term)) + return 0; + + gm = RAPTOR_CALLOC(raptor_rss_group_map*, 1, sizeof(*gm)); + gm->world = rss_serializer->world; + gm->term = raptor_term_copy(term); + gm->item = item; + + raptor_avltree_add(rss_serializer->group_map, gm); + return 0; +} + + +/** + * raptor_rss10_serialize_init: + * @serializer: serializer object + * @name: serializer name + * + * INTERNAL (raptor_serializer_factory API) - create a new serializer + * + * Return value: non-0 on failure + */ +static int +raptor_rss10_serialize_init(raptor_serializer* serializer, const char *name) +{ + raptor_rss10_serializer_context *rss_serializer; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + rss_serializer->world = serializer->world; + + raptor_rss_common_init(serializer->world); + raptor_rss_model_init(serializer->world, &rss_serializer->model); + + rss_serializer->triples = raptor_new_sequence((raptor_data_free_handler)raptor_free_statement, (raptor_data_print_handler)raptor_statement_print); + + rss_serializer->items = raptor_new_sequence((raptor_data_free_handler)raptor_free_rss_item, (raptor_data_print_handler)NULL); + + rss_serializer->enclosures = raptor_new_sequence((raptor_data_free_handler)raptor_free_rss_item, (raptor_data_print_handler)NULL); + + rss_serializer->group_map = raptor_new_avltree((raptor_data_compare_handler)raptor_rss_group_map_compare, + (raptor_data_free_handler)raptor_free_group_map, 0); + + rss_serializer->user_namespaces = raptor_new_sequence((raptor_data_free_handler)raptor_free_namespace, NULL); + + rss_serializer->is_atom = !(strcmp(name,"atom")); + + rss_serializer->nstack = raptor_new_namespaces(serializer->world, 1); + + rss_serializer->xml_literal_dt = raptor_new_uri(serializer->world, + raptor_xml_literal_datatype_uri_string); + + return 0; +} + + +/** + * raptor_rss10_serialize_terminate: + * @serializer: serializer object + * + * INTERNAL (raptor_serializer_factory API) - destroy a serializer + */ +static void +raptor_rss10_serialize_terminate(raptor_serializer* serializer) +{ + raptor_world* world = serializer->world; + raptor_rss10_serializer_context *rss_serializer; + int i; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + + raptor_rss_model_clear(&rss_serializer->model); + raptor_rss_common_terminate(world); + + if(rss_serializer->triples) + raptor_free_sequence(rss_serializer->triples); + + if(rss_serializer->items) + raptor_free_sequence(rss_serializer->items); + + if(rss_serializer->enclosures) + raptor_free_sequence(rss_serializer->enclosures); + + if(rss_serializer->seq_term) + raptor_free_term(rss_serializer->seq_term); + + if(rss_serializer->xml_writer) + raptor_free_xml_writer(rss_serializer->xml_writer); + + for(i = 0; i < RAPTOR_RSS_NAMESPACES_SIZE; i++) { + if(rss_serializer->nspaces[i]) + raptor_free_namespace(rss_serializer->nspaces[i]); + } + + if(rss_serializer->free_default_nspace && rss_serializer->default_nspace) + raptor_free_namespace(rss_serializer->default_nspace); + + if(rss_serializer->xml_nspace) + raptor_free_namespace(rss_serializer->xml_nspace); + + if(rss_serializer->user_namespaces) + raptor_free_sequence(rss_serializer->user_namespaces); + + /* all raptor_namespace* objects must be freed BEFORE the stack + * they are attached to here: */ + if(rss_serializer->nstack) + raptor_free_namespaces(rss_serializer->nstack); + + if(rss_serializer->group_map) + raptor_free_avltree(rss_serializer->group_map); + + if(world->rss_fields_info_qnames) { + for(i = 0; i < RAPTOR_RSS_FIELDS_SIZE; i++) { + if(world->rss_fields_info_qnames[i]) + raptor_free_qname(world->rss_fields_info_qnames[i]); + } + RAPTOR_FREE(raptor_qname* array, world->rss_fields_info_qnames); + world->rss_fields_info_qnames = NULL; + } + + if(world->rss_types_info_qnames) { + for(i = 0; i < RAPTOR_RSS_COMMON_SIZE; i++) { + if(world->rss_types_info_qnames[i]) + raptor_free_qname(world->rss_types_info_qnames[i]); + } + RAPTOR_FREE(raptor_wname* array, world->rss_types_info_qnames); + world->rss_types_info_qnames = NULL; + } + + if(rss_serializer->xml_literal_dt) + raptor_free_uri(rss_serializer->xml_literal_dt); +} + + +/** + * raptor_rss10_move_statements: + * @rss_serializer: serializer object + * @type: item type + * @item: item object + * + * INTERNAL - Move statements from the stored triples into item @item + * that match @item's URI as subject. + * + * Return value: count of number of triples moved + */ +static int +raptor_rss10_move_statements(raptor_rss10_serializer_context *rss_serializer, + raptor_rss_type type, + raptor_rss_item *item) +{ + int t; + int count = 0; + int is_atom = rss_serializer->is_atom; + int size = raptor_sequence_size(rss_serializer->triples); + + for(t = 0; t < size; t++) { + raptor_statement* s; + int f; + + s = (raptor_statement*)raptor_sequence_get_at(rss_serializer->triples, t); + if(!s) + continue; + + if(s->subject->type != RAPTOR_TERM_TYPE_URI || + !raptor_uri_equals(s->subject->value.uri, item->uri)) + continue; + + /* now we know this triple is associated with the item URI + * and can count the relevant triples */ + count++; + + /* add triples with anonymous object to the general triples sequence + * for this item, and to the group map (blank node closure) + */ + if(s->object->type == RAPTOR_TERM_TYPE_BLANK) { + raptor_rss10_set_item_group(rss_serializer, s->object, item); + + RAPTOR_DEBUG4("Moved anonymous value property URI <%s> for typed node %u - %s\n", + raptor_uri_as_string(s->predicate->value.uri), + type, raptor_rss_items_info[type].name); + s = (raptor_statement*)raptor_sequence_delete_at(rss_serializer->triples, + t); + raptor_sequence_push(item->triples, s); + continue; + } + + + /* otherwise process object value types resource or literal */ + for(f = 0; f < RAPTOR_RSS_FIELDS_SIZE; f++) { + if(!rss_serializer->world->rss_fields_info_uris[f]) + continue; + + if(s->predicate->type == RAPTOR_TERM_TYPE_URI && + s->object->type != RAPTOR_TERM_TYPE_BLANK && + raptor_uri_equals(s->predicate->value.uri, + rss_serializer->world->rss_fields_info_uris[f])) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + + /* found field this triple to go in 'item' so move the + * object value over + */ + if(s->object->type == RAPTOR_TERM_TYPE_URI) { + field->uri = s->object->value.uri; + s->object->value.uri = NULL; + } else { + field->value = s->object->value.literal.string; + if(s->object->value.literal.datatype && + raptor_uri_equals(s->object->value.literal.datatype, + rss_serializer->xml_literal_dt)) + field->is_xml = 1; + + if(f == RAPTOR_RSS_FIELD_CONTENT_ENCODED) + field->is_xml = 1; + + if(f == RAPTOR_RSS_FIELD_ATOM_SUMMARY && *field->value == '<') + field->is_xml = 1; + + s->object->value.literal.string = NULL; + } + + if(is_atom) { + int i; + + /* Rewrite item fields rss->atom */ + for(i = 0; + raptor_atom_to_rss[i].from != RAPTOR_RSS_FIELD_UNKNOWN; + i++) { + int from_f = raptor_atom_to_rss[i].to; + int to_f = raptor_atom_to_rss[i].from; + + /* Do not rewrite to atom0.3 terms */ + if(raptor_rss_fields_info[to_f].nspace == ATOM0_3_NS) + continue; + + if(f == from_f && + !(item->fields[to_f] && item->fields[to_f]->value)) { + f = to_f; + if(to_f == RAPTOR_RSS_FIELD_ATOM_SUMMARY && *field->value == '<') + field->is_xml = 1; + field->is_mapped = 1; + RAPTOR_DEBUG5("Moved field %d - %s to field %d - %s\n", + from_f, raptor_rss_fields_info[from_f].name, + to_f, raptor_rss_fields_info[to_f].name); + break; + } + } + } /* end is atom field to map */ + + RAPTOR_DEBUG1("Adding field\n"); + raptor_rss_item_add_field(item, f, field); + raptor_sequence_set_at(rss_serializer->triples, t, NULL); + break; + } + } /* end for field loop */ + + /* loop ended early so triple was assocated with a field - continue */ + if(f < RAPTOR_RSS_FIELDS_SIZE) + continue; + + + /* otherwise triple was not found as a field so store in triples + * sequence + */ + RAPTOR_DEBUG4("UNKNOWN property URI <%s> for typed node %u - %s\n", + raptor_uri_as_string(s->predicate->value.uri), + type, raptor_rss_items_info[type].name); + s = (raptor_statement*)raptor_sequence_delete_at(rss_serializer->triples, + t); + raptor_sequence_push(item->triples, s); + + } /* end for all triples */ + +#ifdef RAPTOR_DEBUG + if(count > 0) + RAPTOR_DEBUG5("Moved %d triples to typed node %u - %s with uri <%s>\n", + count, type, raptor_rss_items_info[type].name, + raptor_uri_as_string(item->uri)); +#endif + + return count; +} + + +/** + * raptor_rss10_move_anonymous_statements: + * @rss_serializer: serializer object + * + * INTERNAL - Move statements with a blank node subject to the appropriate item + * + */ +static int +raptor_rss10_move_anonymous_statements(raptor_rss10_serializer_context *rss_serializer) +{ + int t; + int handled = 1; + int round = 0; +#ifdef RAPTOR_DEBUG + int moved_count = 0; +#endif + + for(round = 0; handled; round++) { + int size = raptor_sequence_size(rss_serializer->triples); + + handled = 0; + for(t = 0; t < size; t++) { + raptor_statement* s; + raptor_rss_item* item; + + s = (raptor_statement*)raptor_sequence_get_at(rss_serializer->triples, t); + if(!s) + continue; + + if(s->subject->type != RAPTOR_TERM_TYPE_BLANK) + continue; + + item = raptor_rss10_get_group_item(rss_serializer, s->subject); + + if(item) { + /* triple matched an existing item */ + s = (raptor_statement*)raptor_sequence_delete_at(rss_serializer->triples, + t); + raptor_sequence_push(item->triples, s); +#ifdef RAPTOR_DEBUG + moved_count++; +#endif + + if(s->object->type == RAPTOR_TERM_TYPE_BLANK) + raptor_rss10_set_item_group(rss_serializer, s->object, item); + + + handled = 1; + } + } /* end for all triples */ + +#ifdef RAPTOR_DEBUG + if(moved_count > 0) + RAPTOR_DEBUG3("Round %d: Moved %d triples\n", round, moved_count); +#endif + } + + return 0; +} + + +/** + * raptor_rss10_move_leftover_statements: + * @rss_serializer: serializer object + * + * INTERNAL - Move any statements in the serializer pool to items or channel + * + */ +static int +raptor_rss10_move_leftover_statements(raptor_rss10_serializer_context *rss_serializer) +{ + raptor_rss_model* rss_model; + int i; + int type; + raptor_rss_item* item; + int size; + + rss_model = &rss_serializer->model; + + type = RAPTOR_RSS_ITEM; + size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + raptor_rss10_move_statements(rss_serializer, (raptor_rss_type)type, item); + } + + type = RAPTOR_RSS_CHANNEL; + if(rss_model->common[type]) { + item = rss_model->common[type]; + raptor_rss10_move_statements(rss_serializer, (raptor_rss_type)type, item); + } + + return 0; +} + + +/** + * raptor_rss10_remove_mapped_item_fields: + * @rss_serializer: serializer object + * @item: rss item + * @type: item type + * + * INTERNAL - Remove mapped fields for an item + * + */ +static int +raptor_rss10_remove_mapped_item_fields(raptor_rss10_serializer_context *rss_serializer, + raptor_rss_item* item, int type) +{ + int f; + + if(!item->fields_count) + return 0; + + for(f = 0; f < RAPTOR_RSS_FIELDS_SIZE; f++) { + raptor_rss_field* field; + int saw_mapped = 0; + int saw_non_mapped = 0; + + for(field = item->fields[f]; field; field = field->next) { + if(field->is_mapped) + saw_mapped++; + else + saw_non_mapped++; + } + + if(saw_mapped && saw_non_mapped) { + raptor_rss_field* last_field = NULL; + RAPTOR_DEBUG6("Item %p Field %d - %s: %d mapped %d non-mapped\n", + RAPTOR_VOIDP(item), + f, raptor_rss_fields_info[f].name, + saw_mapped, saw_non_mapped); + + field = item->fields[f]; + while(field) { + raptor_rss_field* next = field->next; + field->next = NULL; + if(field->is_mapped) + raptor_rss_field_free(field); + else { + if(!last_field) + item->fields[f] = field; + else + last_field->next = field; + last_field = field; + } + field = next; + } + } + + } + + return 0; +} + + +/** + * raptor_rss10_remove_mapped_fields: + * @rss_serializer: serializer object + * + * INTERNAL - Move statements with a blank node subject to the appropriate item + * + */ +static int +raptor_rss10_remove_mapped_fields(raptor_rss10_serializer_context *rss_serializer) +{ + raptor_rss_model* rss_model; + int is_atom; + int i; + + rss_model = &rss_serializer->model; + is_atom = rss_serializer->is_atom; + + if(!is_atom) + return 0; + + if(rss_model->items_count) { + int size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + raptor_rss_item* item; + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + raptor_rss10_remove_mapped_item_fields(rss_serializer, item, + RAPTOR_RSS_ITEM); + } + } + + for(i = RAPTOR_RSS_CHANNEL; i < RAPTOR_RSS_COMMON_SIZE; i++) { + raptor_rss_item* item; + for(item = rss_model->common[i]; item; item = item->next) { + raptor_rss10_remove_mapped_item_fields(rss_serializer, item, i); + } + } + + return 0; +} + + +/** + * raptor_rss10_store_statement: + * @rss_serializer: serializer object + * @s: statement (shared - do not become owner of this) + * + * INTERNAL - decide where to store a statement in an item or keep pending + * + * Return value: non-0 if handled (stored) + */ +static int +raptor_rss10_store_statement(raptor_rss10_serializer_context *rss_serializer, + raptor_statement *s) +{ + raptor_rss_item *item = NULL; + int handled = 0; + int is_atom = rss_serializer->is_atom; + + item = raptor_rss10_get_group_item(rss_serializer, s->subject); + + if(item && + s->predicate->type == RAPTOR_TERM_TYPE_URI && + (s->object->type == RAPTOR_TERM_TYPE_URI || + s->object->type == RAPTOR_TERM_TYPE_LITERAL)) { + int f; + raptor_uri* predicate_uri = s->predicate->value.uri; + + /* scan triples (? <predicate-uri> <uri or literal>) */ + + for(f = 0; f < RAPTOR_RSS_FIELDS_SIZE; f++) { + raptor_rss_field* field; + if(!rss_serializer->world->rss_fields_info_uris[f]) + continue; + + if(raptor_uri_equals(predicate_uri, + rss_serializer->world->rss_fields_info_uris[f])) { + /* found field this triple to go in 'item' so move the + * object value over + */ + field = raptor_rss_new_field(rss_serializer->world); + + if(s->object->type == RAPTOR_TERM_TYPE_URI) { + field->uri = s->object->value.uri; + s->object->value.uri = NULL; + } else { + /* must be literal - checked above */ + field->value = s->object->value.literal.string; + + if(s->object->value.literal.datatype && + raptor_uri_equals(s->object->value.literal.datatype, + rss_serializer->xml_literal_dt)) + field->is_xml = 1; + + if(f == RAPTOR_RSS_FIELD_CONTENT_ENCODED) + field->is_xml = 1; + + if(f == RAPTOR_RSS_FIELD_ATOM_SUMMARY && *field->value == '<') + field->is_xml = 1; + s->object->value.literal.string = NULL; + } + + if(is_atom) { + int i; + + /* Rewrite item fields rss->atom */ + for(i = 0; + raptor_atom_to_rss[i].from != RAPTOR_RSS_FIELD_UNKNOWN; i++) { + int from_f = raptor_atom_to_rss[i].to; + int to_f = raptor_atom_to_rss[i].from; + + /* Do not rewrite to atom0.3 terms */ + if(raptor_rss_fields_info[to_f].nspace == ATOM0_3_NS) + continue; + + if(f == from_f && + !(item->fields[to_f] && item->fields[to_f]->value)) { + f = to_f; + + if(to_f == RAPTOR_RSS_FIELD_ATOM_SUMMARY && *field->value == '<') + field->is_xml = 1; + + field->is_mapped = 1; + RAPTOR_DEBUG5("Moved field %d - %s to field %d - %s\n", + from_f, raptor_rss_fields_info[from_f].name, + to_f, raptor_rss_fields_info[to_f].name); + break; + } + } + } + + RAPTOR_DEBUG1("Adding field\n"); + raptor_rss_item_add_field(item, f, field); +#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1 + RAPTOR_DEBUG2("Stored statement under typed node %p\n", + RAPTOR_VOIDP(item)); +#endif + + handled = 1; + break; + } + } + } + + if(!handled) { + raptor_statement *t; + + /* Need to handle this later so copy it */ + t = raptor_statement_copy(s); + if(t) { + raptor_sequence_push(rss_serializer->triples, t); + +#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1 + fprintf(stderr,"Stored statement: "); + raptor_statement_print_as_ntriples(s, stderr); + fprintf(stderr,"\n"); +#endif + handled = 1; + } + } + + return handled; +} + + +static int +raptor_rss10_serialize_start(raptor_serializer* serializer) +{ + raptor_rss10_serializer_context *rss_serializer; + const char* rss_triples; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + + rss_triples = (const char*)RAPTOR_OPTIONS_GET_STRING(serializer, + RAPTOR_OPTION_RSS_TRIPLES); + if(rss_triples) { + if(!strcmp(rss_triples, "none")) + rss_serializer->rss_triples_mode = 0; + else if(!strcmp(rss_triples, "rdf-xml")) + rss_serializer->rss_triples_mode = 1; + else if(!strcmp(rss_triples, "atom-triples")) + rss_serializer->rss_triples_mode = 2; + else + rss_serializer->rss_triples_mode = 0; + } + + return 0; +} + + +/** + * raptor_rss10_serialize_statement: + * @serializer: serializer object + * @statement: statement (shared - am not owner of this) + * + * INTERNAL (raptor_serializer_factory API) - Serialize a statement + * + * Return value: non-0 on failure + */ +static int +raptor_rss10_serialize_statement(raptor_serializer* serializer, + raptor_statement *statement) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_rss_model *rss_model; + int handled = 0; + int i; + raptor_rss_type type; + raptor_rss_item *item = NULL; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + rss_model = &rss_serializer->model; + +#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1 + if(1) { + RAPTOR_DEBUG1("Processing statement\n "); + raptor_statement_print_as_ntriples(statement, stderr); + fputc('\n', stderr); + } +#endif + + if(raptor_uri_equals(statement->predicate->value.uri, + RAPTOR_RSS_RSS_items_URI(rss_model))) { + /* ignore any triple (? rss:items ?) - is infered */ + return 0; + } + + if(!raptor_uri_equals(statement->predicate->value.uri, + RAPTOR_RDF_type_URI(serializer->world))) + goto savetriple; + + + type = RAPTOR_RSS_NONE; + + if(statement->object->type == RAPTOR_TERM_TYPE_URI) { + raptor_uri* object_uri = statement->object->value.uri; + + /* look for triple: (? rdf:type ?class-uri) to find containers and blocks */ + + /* Look for triple (? rdf:type rdf:Seq) */ + if(raptor_uri_equals(object_uri, RAPTOR_RDF_Seq_URI(serializer->world))) { + + rss_serializer->seq_term = raptor_term_copy(statement->subject); + + handled = 1; + goto savetriple; + } + + /* look for triple: (? rdf:type ?class-uri) to find containers and blocks */ + for(i = 0; i < RAPTOR_RSS_COMMON_SIZE; i++) { + raptor_uri *item_uri = serializer->world->rss_types_info_uris[i]; + + if(item_uri && raptor_uri_equals(object_uri, item_uri)) { + type = (raptor_rss_type)i; + +#ifdef RAPTOR_DEBUG + if(1) { + unsigned char* ts; + ts = raptor_term_to_string(statement->subject); + RAPTOR_DEBUG4("Found typed node %u - %s with term %s\n", type, + raptor_rss_items_info[type].name, ts); + RAPTOR_FREE(char*, ts); + } +#endif + break; + } + } + } + + if(type == RAPTOR_RSS_NONE) { +#ifdef RAPTOR_DEBUG + if(1) { + unsigned char* ts; + ts = raptor_term_to_string(statement->object); + RAPTOR_DEBUG2("UNKNOWN typed node with type term %s\n", ts); + RAPTOR_FREE(char*, ts); + } +#endif + goto savetriple; + } + + + if(type == RAPTOR_RSS_ITEM) { + int size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + + if(raptor_rss_item_equals_statement_subject(item, statement)) + break; + + } + if(i < size) { + RAPTOR_DEBUG2("Found RSS item at entry %d in sequence of items\n", i); + } else { +#ifdef RAPTOR_DEBUG + if(1) { + unsigned char* ts; + ts = raptor_term_to_string(statement->subject); + + RAPTOR_DEBUG2("RSS item term %s is not in sequence of items\n", ts); + RAPTOR_FREE(char*, ts); + } +#endif + item = NULL; + } + } else if(type == RAPTOR_RSS_ENCLOSURE) { + int size = raptor_sequence_size(rss_serializer->enclosures); + for(i = 0; i < size; i++) { + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->enclosures, i); + + if(raptor_rss_item_equals_statement_subject(item, statement)) + break; + } + if(i < size) { + RAPTOR_DEBUG2("Found enclosure at entry %d in sequence of enclosures\n", i); + } else { +#ifdef RAPTOR_DEBUG + if(1) { + unsigned char* ts; + ts = raptor_term_to_string(statement->subject); + RAPTOR_DEBUG2("Add new enclosure to sequence with term %s\n", ts); + RAPTOR_FREE(char*, ts); + } +#endif + + item = raptor_new_rss_item(rss_serializer->world); + raptor_sequence_push(rss_serializer->enclosures, item); + } + } else { + item = raptor_rss_model_add_common(rss_model, type); + } + + + if(item && statement->subject->type == RAPTOR_TERM_TYPE_URI) { + raptor_rss_item_set_uri(item, statement->subject->value.uri); + + /* Move any existing statements to the newly discovered item */ + raptor_rss10_move_statements(rss_serializer, type, item); + + raptor_rss10_set_item_group(rss_serializer, item->term, item); + + handled = 1; + } + + + savetriple: + if(!handled) { + handled = raptor_rss10_store_statement(rss_serializer, statement); + + /* failed to store */ + if(!handled) + return 1; + } + + return 0; +} + + +static void +raptor_rss10_build_items(raptor_rss10_serializer_context *rss_serializer) +{ + raptor_rss_model* rss_model = &rss_serializer->model; + int i; + int size; + + if(!rss_serializer->seq_term) + return; + + size = raptor_sequence_size(rss_serializer->triples); + for(i = 0; i < size; i++) { + int ordinal = -1; + raptor_statement* s; + + s = (raptor_statement*)raptor_sequence_get_at(rss_serializer->triples, i); + if(!s) + continue; + +#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1 + RAPTOR_DEBUG1("Processing statement\n "); + raptor_statement_print_as_ntriples(s, stderr); + fputc('\n', stderr); +#endif + + /* skip triples that are not ? ? <uri> */ + if(s->object->type != RAPTOR_TERM_TYPE_URI) { + RAPTOR_DEBUG1("Not ? ? <uri> - continuing\n"); + continue; + } + + + if(raptor_term_equals(s->subject, rss_serializer->seq_term)) { + const unsigned char* uri_str; + /* found <seq URI> <some predicate> <some URI> triple */ + + /* predicate is a resource */ + uri_str = raptor_uri_as_string(s->predicate->value.uri); + + if(!strncmp((const char*)uri_str, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#_", 44)) + ordinal= raptor_check_ordinal(uri_str + 44); + +#ifdef RAPTOR_DEBUG + if(1) { + unsigned char* ts; + ts = raptor_term_to_string(s->object); + RAPTOR_DEBUG3("Found RSS 1.0 item %d with term %s\n", ordinal, ts); + RAPTOR_FREE(char*, ts); + } +#endif + + if(ordinal >= 0) { + raptor_rss_item *item; + + item = raptor_new_rss_item(rss_serializer->world); + + raptor_rss_item_set_uri(item, s->object->value.uri); + + raptor_sequence_set_at(rss_serializer->items, ordinal - 1, item); + + raptor_sequence_set_at(rss_serializer->triples, i, NULL); + + /* Move any existing statements to the newly discovered item */ + raptor_rss10_move_statements(rss_serializer, RAPTOR_RSS_ITEM, item); + + raptor_rss10_set_item_group(rss_serializer, item->term, item); + } + } + } + + rss_model->items_count = raptor_sequence_size(rss_serializer->items); +} + + +static void +raptor_rss10_build_xml_names(raptor_serializer *serializer, int is_entry) +{ + raptor_world* world = serializer->world; + raptor_rss10_serializer_context *rss_serializer; + raptor_rss_model* rss_model; + raptor_uri *base_uri = serializer->base_uri; + raptor_xml_element *element; + raptor_qname *qname; + int root_ns_id; + const unsigned char *root_local_name; + int i; + int is_atom; + const raptor_rss_item_info *item_node_type; + int item_node_typei; + int default_ns_id; + int size; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + rss_model = &rss_serializer->model; + is_atom = rss_serializer->is_atom; + + if(is_atom) { + default_ns_id = ATOM1_0_NS; + root_ns_id = ATOM1_0_NS; + root_local_name = (is_entry ? (const unsigned char*)"entry" : + (const unsigned char*)"feed"); + item_node_typei = RAPTOR_ATOM_ENTRY; + } else { + default_ns_id = RSS1_0_NS; + root_ns_id = RDF_NS; + root_local_name = (const unsigned char*)"RDF"; + item_node_typei = RAPTOR_RSS_ITEM; + } + item_node_type = &raptor_rss_items_info[item_node_typei]; + + rss_serializer->xml_nspace = raptor_new_namespace(rss_serializer->nstack, + (const unsigned char*)"xml", + (const unsigned char*)raptor_xml_namespace_uri, + 0); + + + /* Now we have a namespace stack, declare the namespaces */ + for(i = 0; i < RAPTOR_RSS_NAMESPACES_SIZE; i++) { + raptor_uri* uri = serializer->world->rss_namespaces_info_uris[i]; + const unsigned char *prefix; + + prefix = (const unsigned char*)raptor_rss_namespaces_info[i].prefix; + if(!prefix) + continue; + + if(i == default_ns_id) { + if(!RAPTOR_OPTIONS_GET_NUMERIC(serializer, RAPTOR_OPTION_PREFIX_ELEMENTS)) + prefix = NULL; + } + + if(uri) { + raptor_namespace* nspace; + nspace = raptor_new_namespace_from_uri(rss_serializer->nstack, prefix, + uri, 0); + rss_serializer->nspaces[i] = nspace; + + if(i == default_ns_id) { + rss_serializer->default_nspace = nspace; + rss_serializer->free_default_nspace = 0; + } + } + + if(i == RDF_NS) + rss_serializer->rdf_nspace = rss_serializer->nspaces[i]; + } + + + qname = raptor_new_qname_from_namespace_local_name(serializer->world, + rss_serializer->nspaces[root_ns_id], + root_local_name, + NULL); + if(base_uri) + base_uri = raptor_uri_copy(base_uri); + + element = raptor_new_xml_element(qname, NULL, base_uri); + rss_serializer->root_element = element; + + + /* Declare the namespaces on the root element */ + raptor_xml_element_declare_namespace(element, rss_serializer->default_nspace); + + for(i = 0; i < RAPTOR_RSS_NAMESPACES_SIZE; i++) { + const unsigned char *prefix; + + prefix = (const unsigned char*)raptor_rss_namespaces_info[i].prefix; + if(!prefix && i != default_ns_id) + continue; + + if(rss_serializer->nspaces[i]) + raptor_xml_element_declare_namespace(element, rss_serializer->nspaces[i]); + } + + size = raptor_sequence_size(rss_serializer->user_namespaces); + for(i = 0; i < size; i++) { + raptor_namespace* nspace; + nspace = (raptor_namespace*)raptor_sequence_get_at(rss_serializer->user_namespaces, i); + + /* Ignore user setting default namespace prefix */ + if(!nspace->prefix) + continue; + + raptor_xml_element_declare_namespace(element, nspace); + } + + + world->rss_fields_info_qnames = RAPTOR_CALLOC(raptor_qname**, + RAPTOR_RSS_FIELDS_SIZE, + sizeof(raptor_qname*)); + if(!world->rss_fields_info_qnames) + return; + + for(i = 0; i < RAPTOR_RSS_FIELDS_SIZE; i++) { + int n = raptor_rss_fields_info[i].nspace; + raptor_namespace* nspace = rss_serializer->nspaces[n]; + const unsigned char* lname; + lname = (const unsigned char*)raptor_rss_fields_info[i].name; + + world->rss_fields_info_qnames[i] = + raptor_new_qname_from_namespace_local_name(serializer->world, + nspace, lname, NULL); + if(!world->rss_fields_info_qnames[i]) + return; + } + + world->rss_types_info_qnames = RAPTOR_CALLOC(raptor_qname**, + RAPTOR_RSS_COMMON_SIZE, + sizeof(raptor_qname*)); + if(!world->rss_types_info_qnames) + return; + for(i = 0; i < RAPTOR_RSS_COMMON_SIZE; i++) { + int n = raptor_rss_items_info[i].nspace; + raptor_namespace* nspace = rss_serializer->nspaces[n]; + + if(nspace) { + const unsigned char* lname = + (const unsigned char*)raptor_rss_items_info[i].name; + + world->rss_types_info_qnames[i] = + raptor_new_qname_from_namespace_local_name(serializer->world, + nspace, lname, NULL); + if(!world->rss_types_info_qnames[i]) + return; + } + } + + for(i = 0; i < RAPTOR_RSS_COMMON_SIZE; i++) { + raptor_rss_item* item; + for(item = rss_model->common[i]; item; item = item->next) { + int typei = i; + + if(!item->fields_count) + continue; + + if(is_atom) { + if(typei == RAPTOR_RSS_CHANNEL) + typei = RAPTOR_ATOM_FEED; + else if(typei == RAPTOR_RSS_ITEM) + typei = RAPTOR_ATOM_ENTRY; + } + item->node_type = &raptor_rss_items_info[typei]; + item->node_typei = typei; + } + } + + size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + raptor_rss_item* item; + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + item->node_type = item_node_type; + item->node_typei = item_node_typei; + } + + size = raptor_sequence_size(rss_serializer->enclosures); + for(i = 0; i < size; i++) { + raptor_rss_item* item; + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->enclosures, i); + item->node_type = &raptor_rss_items_info[RAPTOR_RSS_ENCLOSURE]; + item->node_typei = RAPTOR_RSS_ENCLOSURE; + } + +} + + +static void +raptor_rss10_emit_atom_triples_map(raptor_serializer *serializer, int is_feed, + const unsigned char* map_element_name) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_xml_writer* xml_writer; + raptor_uri *base_uri = serializer->base_uri; + raptor_uri* base_uri_copy = NULL; + raptor_namespace* at_nspace; + raptor_xml_element* at_map_root_element; + raptor_qname *at_map_root_qname; + int i; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + xml_writer = rss_serializer->xml_writer; + at_nspace = rss_serializer->nspaces[ATOMTRIPLES_NS]; + + at_map_root_qname = raptor_new_qname_from_namespace_local_name(serializer->world, at_nspace, + (const unsigned char*)map_element_name, NULL); + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + at_map_root_element = raptor_new_xml_element(at_map_root_qname, NULL, + base_uri_copy); + + raptor_xml_writer_start_element(xml_writer, at_map_root_element); + + /* Walk list of fields mapped atom to rss */ + for(i = 0; raptor_atom_to_rss[i].from != RAPTOR_RSS_FIELD_UNKNOWN; i++) { + int from_f = raptor_atom_to_rss[i].from; + int to_f = raptor_atom_to_rss[i].to; + const raptor_rss_field_info* from_field_info = &raptor_rss_fields_info[from_f]; + const raptor_rss_field_info* to_field_info = &raptor_rss_fields_info[to_f]; + raptor_xml_element* at_map_element; + raptor_qname *at_map_qname; + raptor_qname** at_map_attrs; + const char* predicate_prefix; + unsigned char* ruri_string; + + /* Do not rewrite to atom0.3 terms */ + if(to_field_info->nspace == ATOM0_3_NS) + continue; + + /* atom:feed only contains some fields that are mapped */ + if(is_feed && !(from_f == RAPTOR_RSS_FIELD_ATOM_ID || + from_f == RAPTOR_RSS_FIELD_ATOM_UPDATED || + from_f == RAPTOR_RSS_FIELD_ATOM_RIGHTS || + from_f == RAPTOR_RSS_FIELD_ATOM_TITLE)) + continue; + + predicate_prefix = raptor_rss_namespaces_info[from_field_info->nspace].prefix; + if(!predicate_prefix) + continue; + + /* <at:map property="{property URI}">{atom element}</at:map> */ + at_map_qname = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + at_nspace, + map_element_name, + NULL); + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + at_map_element = raptor_new_xml_element(at_map_qname, NULL, base_uri_copy); + + + at_map_attrs = RAPTOR_CALLOC(raptor_qname**, 1, sizeof(raptor_qname*)); + ruri_string = raptor_uri_to_relative_uri_string(base_uri, + serializer->world->rss_fields_info_uris[to_f]); + at_map_attrs[0] = raptor_new_qname(rss_serializer->nstack, + (const unsigned char*)"property", + ruri_string); + raptor_free_memory(ruri_string); + raptor_xml_element_set_attributes(at_map_element, at_map_attrs, 1); + + raptor_xml_writer_start_element(xml_writer, at_map_element); + raptor_xml_writer_cdata(xml_writer, (const unsigned char*)predicate_prefix); + raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)":", 1); + raptor_xml_writer_cdata(xml_writer, + (const unsigned char*)from_field_info->name); + raptor_xml_writer_end_element(xml_writer, at_map_element); + + raptor_free_xml_element(at_map_element); + } + + raptor_xml_writer_end_element(xml_writer, at_map_root_element); + + raptor_free_xml_element(at_map_root_element); +} + + + +/* atom-specific feed XML elements */ +static void +raptor_rss10_emit_atom_feed(raptor_serializer *serializer, + raptor_rss_item *item) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_xml_writer* xml_writer; + raptor_uri *base_uri = serializer->base_uri; + raptor_uri* base_uri_copy = NULL; + raptor_xml_element* atom_link_element; + raptor_qname *atom_link_qname; + raptor_qname** atom_link_attrs; + raptor_namespace* atom_nspace; + unsigned char* ruri_string; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + atom_nspace = rss_serializer->nspaces[ATOM1_0_NS]; + xml_writer = rss_serializer->xml_writer; + + atom_link_qname = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + atom_nspace, + (const unsigned char*)"link", + NULL); + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + atom_link_element = raptor_new_xml_element(atom_link_qname, NULL, + base_uri_copy); + + atom_link_attrs = RAPTOR_CALLOC(raptor_qname**, 2, sizeof(raptor_qname*)); + ruri_string = raptor_uri_to_relative_uri_string(base_uri, item->uri); + + atom_link_attrs[0] = raptor_new_qname(rss_serializer->nstack, + (const unsigned char*)"href", + ruri_string); + raptor_free_memory(ruri_string); + atom_link_attrs[1] = raptor_new_qname(rss_serializer->nstack, + (const unsigned char*)"rel", + (const unsigned char*)"self"); + raptor_xml_element_set_attributes(atom_link_element, atom_link_attrs, 2); + + raptor_xml_writer_empty_element(xml_writer, atom_link_element); + + raptor_free_xml_element(atom_link_element); + + if(rss_serializer->rss_triples_mode == 2) { + raptor_rss10_emit_atom_triples_map(serializer, 1, + (const unsigned char*)"feedmap"); + raptor_rss10_emit_atom_triples_map(serializer, 0, + (const unsigned char*)"entrymap"); + } +} + + +/* emit the RSS 1.0-specific rdf:Seq and rss:item XML elements */ +static void +raptor_rss10_emit_rss_items(raptor_serializer *serializer) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_xml_writer* xml_writer; + raptor_uri *base_uri = serializer->base_uri; + raptor_uri* base_uri_copy = NULL; + raptor_xml_element* rss_items_predicate; + int i; + raptor_qname *rdf_Seq_qname; + raptor_xml_element *rdf_Seq_element; + int size; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + + if(!raptor_sequence_size(rss_serializer->items)) + return; + + xml_writer = rss_serializer->xml_writer; + + rdf_Seq_qname = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"Seq", + NULL); + + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + rdf_Seq_element = raptor_new_xml_element(rdf_Seq_qname, NULL, base_uri_copy); + + /* make the <rss:items><rdf:Seq><rdf:li /> .... </rdf:Seq></rss:items> */ + + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + rss_items_predicate = raptor_new_xml_element(raptor_qname_copy(serializer->world->rss_fields_info_qnames[RAPTOR_RSS_FIELD_ITEMS]), NULL, base_uri_copy); + + raptor_xml_writer_start_element(xml_writer, rss_items_predicate); + + raptor_xml_writer_start_element(xml_writer, rdf_Seq_element); + + size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + raptor_rss_item* item_item; + raptor_qname *rdf_li_qname; + raptor_xml_element *rdf_li_element; + raptor_qname **attrs; + unsigned char* ruri_string; + + item_item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + rdf_li_qname = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"li", + NULL); + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + rdf_li_element = raptor_new_xml_element(rdf_li_qname, NULL, base_uri_copy); + attrs = RAPTOR_CALLOC(raptor_qname**, 1, sizeof(raptor_qname*)); + ruri_string = raptor_uri_to_relative_uri_string(base_uri, item_item->uri); + attrs[0] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"resource", + ruri_string); + raptor_free_memory(ruri_string); + raptor_xml_element_set_attributes(rdf_li_element, attrs, 1); + + raptor_xml_writer_empty_element(xml_writer, rdf_li_element); + + raptor_xml_writer_newline(xml_writer); + + raptor_free_xml_element(rdf_li_element); + } + + raptor_xml_writer_end_element(xml_writer, rdf_Seq_element); + + raptor_free_xml_element(rdf_Seq_element); + + raptor_xml_writer_end_element(xml_writer, rss_items_predicate); + + raptor_free_xml_element(rss_items_predicate); +} + + +/* emit a block of RDF/XML depending on the rssTriples option mode */ +static void +raptor_rss10_emit_rdfxml_item_triples(raptor_serializer *serializer, + raptor_rss_item *item) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_xml_writer* xml_writer; + raptor_xml_element* root_element = NULL; + raptor_serializer* ser = NULL; + raptor_uri* base_uri = NULL; + int t_max_count = raptor_sequence_size(item->triples); + int t_count; + int t; + int is_atom; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + + if(rss_serializer->rss_triples_mode == 0 || !item->triples) + return; + + xml_writer = rss_serializer->xml_writer; + is_atom = rss_serializer->is_atom; + + /* can only use atom-triples with atom serializer */ + if(rss_serializer->rss_triples_mode == 2 && !is_atom) + return; + + /* can only use rdf-xml with rss-1.0 serializer */ + if(rss_serializer->rss_triples_mode == 1 && is_atom) + return; + + t_count = 0; + for(t = 0; t < t_max_count; t++) { + if(raptor_sequence_get_at(item->triples, t)) + t_count++; + } + if(!t_count) + return; + + RAPTOR_DEBUG2("Serializing %d triples\n", t_count); + + if(is_atom) { + raptor_namespace* at_nspace = rss_serializer->nspaces[ATOMTRIPLES_NS]; + raptor_qname* root_qname; + + /* atom:md with no attribute */ + root_qname = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + at_nspace, + (const unsigned char*)"md", + NULL); + if(!root_qname) + goto oom; + + base_uri = serializer->base_uri; + if(base_uri) + base_uri = raptor_uri_copy(base_uri); + + /* after this root_element owns root_qname and (this copy of) base_uri */ + root_element = raptor_new_xml_element(root_qname, NULL, base_uri); + if(!root_element) { + if(base_uri) + raptor_free_uri(base_uri); + raptor_free_qname(root_qname); + } else + raptor_xml_writer_start_element(xml_writer, root_element); + } + + ser = raptor_new_serializer(rss_serializer->world, "rdfxml-abbrev"); + if(!ser) + goto oom; + + raptor_rdfxmla_serialize_set_xml_writer(ser, xml_writer, + rss_serializer->nstack); + raptor_rdfxmla_serialize_set_write_rdf_RDF(ser, 0); + raptor_rdfxmla_serialize_set_single_node(ser, item->uri); + if(rss_serializer->rss_triples_mode == 2) { + /* raptor_rdfxmla_serialize_set_write_typed_nodes(ser, 0); */ + } + + /* after this call, ser does + * NOT own serializer->iostream and will not destroy it + * when raptor_free_serializer(ser) is called. + */ + raptor_serializer_start_to_iostream(ser, base_uri, serializer->iostream); + + for(t = 0; t < t_max_count; t++) { + raptor_statement* s; + s = (raptor_statement*)raptor_sequence_get_at(item->triples, t); + if(s) + raptor_serializer_serialize_statement(ser, s); + } + + raptor_serializer_serialize_end(ser); + + if(is_atom) + raptor_xml_writer_end_element(xml_writer, root_element); + + oom: + if(ser) + raptor_free_serializer(ser); + + if(root_element) + raptor_free_xml_element(root_element); +} + + +/** + * raptor_rss10_ensure_atom_field_zero_one: + * @item: RSS item object + * @f: ATOM field type + * + * INTERNAL - Check that the given item @field appears 0 or 1 times + */ +static void +raptor_rss10_ensure_atom_field_zero_one(raptor_rss_item* item, + raptor_rss_fields_type f) +{ + raptor_rss_field* field = item->fields[f]; + if(!field) + return; + + if(field->next) { + /* more than 1 value so delete rest of values */ + raptor_rss_field* next = field->next; + field->next = NULL; + + do { + field = next; + + next = field->next; + field->next = NULL; + raptor_rss_field_free(field); + } while(next); + } + +} + + +/** + * raptor_rss10_ensure_atom_feed_valid: + * @rss_serializer: serializer object + * + * INTERNAL - Ensure the atom items have all the fields they need: + * <id> & <title> & <updated> + * plus: + * <link rel='alternate' ...> OR <content>.. + * + */ +static int +raptor_rss10_ensure_atom_feed_valid(raptor_rss10_serializer_context *rss_serializer) +{ + int is_atom; + int i; + raptor_rss_item* item; + raptor_rss_model* rss_model; + time_t now = 0; + int size; + +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + if(!gettimeofday(&tv, NULL)) + now = tv.tv_sec; +#else + now = time(NULL); +#endif + + is_atom = rss_serializer->is_atom; + rss_model = &rss_serializer->model; + + if(!is_atom) + return 0; + + item = rss_model->common[RAPTOR_RSS_CHANNEL]; + if(item) { + int f; + + /* atom:id is required */ + f = RAPTOR_RSS_FIELD_ATOM_ID; + if(!item->fields[f]) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + field->uri = raptor_uri_copy(item->uri); + raptor_rss_item_add_field(item, f, field); + } + + /* atom:updated is required */ + f = RAPTOR_RSS_FIELD_ATOM_UPDATED; + if(!item->fields[f]) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + raptor_rss_set_date_field(field, now); + raptor_rss_item_add_field(item, f, field); + } + + /* atom:content is forbidden in feed */ + f = RAPTOR_RSS_FIELD_ATOM_CONTENT; + if(item->fields[f]) { + raptor_rss_field_free(item->fields[f]); + item->fields[f] = NULL; + } + + /* atom:summary is forbidden in feed */ + f = RAPTOR_RSS_FIELD_ATOM_SUMMARY; + if(item->fields[f]) { + raptor_rss_field_free(item->fields[f]); + item->fields[f] = NULL; + } + + /* These fields can appear 0 or 1 times on a feed */ + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_ICON); + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_LOGO); + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_RIGHTS); + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_SUBTITLE); + } + + + size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + + /* atom:id - defaults to item URI */ + if(!item->fields[RAPTOR_RSS_FIELD_ATOM_ID]) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + field->uri = raptor_uri_copy(item->uri); + raptor_rss_item_add_field(item, RAPTOR_RSS_FIELD_ATOM_ID, field); + } + + /* atom:title - defaults to "untitled" */ + if(!item->fields[RAPTOR_RSS_FIELD_ATOM_TITLE]) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + field->value = RAPTOR_MALLOC(unsigned char*, 9); + memcpy(field->value, "untitled", 9); + raptor_rss_item_add_field(item, RAPTOR_RSS_FIELD_ATOM_TITLE, field); + } + + /* atom:updated - defaults to now time */ + if(!item->fields[RAPTOR_RSS_FIELD_ATOM_UPDATED]) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + raptor_rss_set_date_field(field, now); + raptor_rss_item_add_field(item, RAPTOR_RSS_FIELD_ATOM_UPDATED, field); + } + + /* enforce there is either an atom:content OR atom:link (rel = alternate) + * by adding a link to {item URI} if missing + */ + if(!item->fields[RAPTOR_RSS_FIELD_ATOM_CONTENT] && + !item->fields[RAPTOR_RSS_FIELD_ATOM_LINK]) { + raptor_rss_field* field = raptor_rss_new_field(rss_serializer->world); + field->uri = raptor_uri_copy(item->uri); + raptor_rss_item_add_field(item, RAPTOR_RSS_FIELD_ATOM_LINK, field); + } + + /* These fields can appear 0 or 1 times on an entry */ + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_PUBLISHED); + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_RIGHTS); + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_SOURCE); + raptor_rss10_ensure_atom_field_zero_one(item, + RAPTOR_RSS_FIELD_ATOM_SUMMARY); + } + + return 0; +} + + +static void +raptor_rss10_emit_item(raptor_serializer* serializer, + raptor_rss_item *item, int item_type, + int emit_container) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_xml_writer* xml_writer; + raptor_rss_model* rss_model; + raptor_uri *base_uri = serializer->base_uri; + raptor_xml_element *element = NULL; + raptor_qname **attrs = NULL; + raptor_uri* base_uri_copy = NULL; + int fi; + int is_atom; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + +#ifdef RAPTOR_DEBUG + if(!item) { + RAPTOR_FATAL3("Tried to emit NULL item of type %d - %s\n", item_type, + raptor_rss_items_info[item_type].name); + } +#endif + + xml_writer = rss_serializer->xml_writer; + is_atom = rss_serializer->is_atom; + rss_model = &rss_serializer->model; + + if(!item->fields_count) { + int i; + int size = raptor_sequence_size(rss_serializer->enclosures); + + for(i = 0; i < size; i++) { + raptor_rss_item *enclosure_item; + enclosure_item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->enclosures, i); + /* If the item and enclosure item have the same URI, move the + * enclosure fields to the item. Assumed that they got conflated + * previously such as when the enclosure url = the guid + */ + if(enclosure_item->uri && + raptor_uri_equals(item->uri, enclosure_item->uri)) { + int j; + for(j = 0; j < RAPTOR_RSS_FIELDS_SIZE; j++) { + if(j != RAPTOR_RSS_RDF_ENCLOSURE_TYPE && + j != RAPTOR_RSS_RDF_ENCLOSURE_LENGTH && + j != RAPTOR_RSS_RDF_ENCLOSURE_URL) { + item->fields[j] = enclosure_item->fields[j]; + enclosure_item->fields[j] = NULL; + item->fields_count++; + enclosure_item->fields_count--; + } + } + break; + } + } + } + + if(!item->fields_count) + return; + + if(emit_container) { + raptor_qname* qname_copy; + + qname_copy = raptor_qname_copy(serializer->world->rss_types_info_qnames[item->node_typei]); + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + element = raptor_new_xml_element(qname_copy, NULL, base_uri_copy); + + if(!is_atom && item->uri) { + unsigned char* ruri_string; + attrs = RAPTOR_CALLOC(raptor_qname**, 1, sizeof(raptor_qname*)); + ruri_string = raptor_uri_to_relative_uri_string(base_uri, item->uri); + attrs[0] = raptor_new_qname_from_namespace_local_name(serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"about", + ruri_string); + raptor_free_memory(ruri_string); + raptor_xml_element_set_attributes(element, attrs, 1); + } + + raptor_xml_writer_start_element(xml_writer, element); + } + + + for(fi = 0; fi < RAPTOR_RSS_FIELDS_SIZE; fi++) { + raptor_rss_fields_type f = (raptor_rss_fields_type)fi; + raptor_rss_field* field; + + if(f == RAPTOR_RSS_FIELD_ITEMS) + /* emitting the RSS items rdf:Seq block is done after this loop */ + continue; + + if(!serializer->world->rss_fields_info_uris[f]) + continue; + + if(f == RAPTOR_RSS_FIELD_ATOM_AUTHOR) { + int typei; + + if(!is_atom) + continue; + + if(item_type != RAPTOR_RSS_CHANNEL) + continue; + + typei = RAPTOR_ATOM_AUTHOR; + if(!rss_model->common[typei]) { + raptor_rss_item* author_item; + + /* No atom author was present so make a new atom:author item + * then either promote the string to an atom:name field OR + * use "unknown" + */ + author_item = raptor_rss_model_add_common(rss_model, + (raptor_rss_type)typei); + + author_item->node_type = &raptor_rss_items_info[typei]; + author_item->node_typei = typei; + + /* FIXME - uses _:author as bnode name - should make a new + * genid for each author node. This is OK because there + * is a check above that there is only 1 author per FEED. + */ + author_item->term = raptor_new_term_from_blank(serializer->world, + (unsigned char*)"author"); + + + /* Move atom:name author field, or create a dummy one */ + f = RAPTOR_RSS_FIELD_ATOM_NAME; + if(item->fields[f]) { + field = item->fields[f]; + item->fields[f] = NULL; + } else { + field = raptor_rss_new_field(serializer->world); + field->value = RAPTOR_MALLOC(unsigned char*, 8); + memcpy(field->value, "unknown", 8); + } + raptor_rss_item_add_field(author_item, RAPTOR_RSS_FIELD_ATOM_NAME, + field); + + /* Move atom author fields if found: atom:uri and atom:email + * are only used inside Person constructs + */ + f = RAPTOR_RSS_FIELD_ATOM_URI; + if(item->fields[f]) { + field = item->fields[f]; + raptor_rss_item_add_field(author_item, f, field); + item->fields[f] = NULL; + } + f = RAPTOR_RSS_FIELD_ATOM_EMAIL; + if(item->fields[f]) { + field = item->fields[f]; + raptor_rss_item_add_field(author_item, f, field); + item->fields[f] = NULL; + } + } + + RAPTOR_DEBUG3("Emitting type %i - %s\n", typei, + raptor_rss_items_info[typei].name); + raptor_rss10_emit_item(serializer, rss_model->common[typei], typei, 1); + continue; + } + + + for(field = item->fields[f]; field; field = field->next) { + raptor_xml_element* predicate; + + /* Use atom:summary in preference */ + if(is_atom && f == RAPTOR_RSS_FIELD_DESCRIPTION) + continue; + + base_uri_copy = base_uri ? raptor_uri_copy(base_uri) : NULL; + predicate = raptor_new_xml_element(raptor_qname_copy(serializer->world->rss_fields_info_qnames[f]), NULL, base_uri_copy); + + if(is_atom && field->uri) { + unsigned char* ruri_string; + size_t len; + raptor_uri* my_base_uri = base_uri; + + if(f == RAPTOR_RSS_FIELD_ATOM_ID) + my_base_uri = NULL; + + ruri_string = raptor_uri_to_relative_counted_uri_string(my_base_uri, + field->uri, + &len); + + if(f == RAPTOR_RSS_FIELD_ATOM_LINK && + !item->fields[RAPTOR_RSS_FIELD_ATOM_CONTENT]) { + /* atom:link to URI and there is no atom:content */ + raptor_qname **predicate_attrs = NULL; + predicate_attrs = RAPTOR_CALLOC(raptor_qname**, 2, + sizeof(raptor_qname*)); + predicate_attrs[0] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + NULL, + (const unsigned char*)"href", + ruri_string); + predicate_attrs[1] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + NULL, + (const unsigned char*)"rel", + (const unsigned char*)"alternate"); + field->value = NULL; + raptor_xml_element_set_attributes(predicate, predicate_attrs, 2); + raptor_xml_writer_empty_element(xml_writer, predicate); + } else if(f == RAPTOR_RSS_FIELD_ATOM_CONTENT) { + /* <atom:content src="{uri value}" type="{type}" /> */ + raptor_qname **predicate_attrs = NULL; + const unsigned char* content_type; + raptor_rss_field* content_type_field; + + /* get the type */ + content_type_field = item->fields[RAPTOR_RSS_FIELD_AT_CONTENT_TYPE]; + if(content_type_field && content_type_field->value) + content_type = content_type_field->value; + else + content_type = (const unsigned char*)"text/html"; + + predicate_attrs = RAPTOR_CALLOC(raptor_qname**, 2, + sizeof(raptor_qname*)); + predicate_attrs[0] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + NULL, + (const unsigned char*)"src", + ruri_string); + predicate_attrs[1] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + NULL, + (const unsigned char*)"type", + (const unsigned char*)content_type); + /* free at:contentType field - no need to emit it */ + if(content_type_field) { + raptor_rss_field_free(content_type_field); + item->fields[RAPTOR_RSS_FIELD_AT_CONTENT_TYPE] = NULL; + } + + field->value = NULL; + raptor_xml_element_set_attributes(predicate, predicate_attrs, 2); + raptor_xml_writer_empty_element(xml_writer, predicate); + } else { + raptor_xml_writer_start_element(xml_writer, predicate); + raptor_xml_writer_cdata_counted(xml_writer, ruri_string, + (unsigned int)len); + raptor_xml_writer_end_element(xml_writer, predicate); + } + raptor_free_memory(ruri_string); + + } else if(field->uri) { + raptor_uri* enclosure_uri = field->uri; + raptor_rss_item *enclosure_item = NULL; + int i; + + if(f == RAPTOR_RSS_FIELD_ENCLOSURE && item_type == RAPTOR_RSS_ITEM) { + int size = raptor_sequence_size(rss_serializer->enclosures); + for(i = 0; i < size; i++) { + enclosure_item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->enclosures, i); + if(enclosure_item->uri && raptor_uri_equals(enclosure_uri, + enclosure_item->uri)) + break; + } + if(enclosure_item) { + int attr_count = 0; + unsigned char* ruri_string; + + attrs = RAPTOR_CALLOC(raptor_qname**, 3, sizeof(raptor_qname*)); + ruri_string = raptor_uri_to_relative_uri_string(base_uri, field->uri); + attrs[attr_count] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"resource", + ruri_string); + raptor_free_memory(ruri_string); + attr_count++; + + if(enclosure_item->fields[RAPTOR_RSS_RDF_ENCLOSURE_TYPE] && enclosure_item->fields[RAPTOR_RSS_RDF_ENCLOSURE_TYPE]->value) { + attrs[attr_count] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->nspaces[RSS2_0_ENC_NS], + (const unsigned char*)raptor_rss_fields_info[RAPTOR_RSS_RDF_ENCLOSURE_TYPE].name, + (const unsigned char*)enclosure_item->fields[RAPTOR_RSS_RDF_ENCLOSURE_TYPE]->value); + attr_count++; + } + + if(enclosure_item->fields[RAPTOR_RSS_RDF_ENCLOSURE_LENGTH] && enclosure_item->fields[RAPTOR_RSS_RDF_ENCLOSURE_LENGTH]->value) { + attrs[attr_count] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->nspaces[RSS2_0_ENC_NS], + (const unsigned char*)raptor_rss_fields_info[RAPTOR_RSS_RDF_ENCLOSURE_LENGTH].name, + (const unsigned char*)enclosure_item->fields[RAPTOR_RSS_RDF_ENCLOSURE_LENGTH]->value); + attr_count++; + } + raptor_xml_element_set_attributes(predicate, attrs, attr_count); + } else { + RAPTOR_DEBUG2("Enclosure item with URI %s could not be found in list of enclosures\n", raptor_uri_as_string(enclosure_uri)); + } + } else { + unsigned char* ruri_string; + + /* not an rss:item with an rss:enclosure field */ + attrs = RAPTOR_CALLOC(raptor_qname**, 1, sizeof(raptor_qname*)); + ruri_string = raptor_uri_to_relative_uri_string(base_uri, field->uri); + attrs[0] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"resource", + ruri_string); + raptor_free_memory(ruri_string); + raptor_xml_element_set_attributes(predicate, attrs, 1); + } + raptor_xml_writer_empty_element(xml_writer, predicate); + } else if(field->value) { + /* not a URI, must be a literal */ + int is_xhtml_content = field->is_xml; + int prefer_cdata = (!is_atom && f == RAPTOR_RSS_FIELD_CONTENT_ENCODED); + + if(is_xhtml_content && !prefer_cdata) { + raptor_qname **predicate_attrs = NULL; + predicate_attrs = RAPTOR_CALLOC(raptor_qname**, 1, + sizeof(raptor_qname*)); + if(is_atom) + predicate_attrs[0] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + NULL, + (const unsigned char*)"type", + (const unsigned char*)"xhtml"); + else + predicate_attrs[0] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->rdf_nspace, + (const unsigned char*)"parseType", + (const unsigned char*)"Literal"); + raptor_xml_element_set_attributes(predicate, predicate_attrs, 1); + } + + raptor_xml_writer_start_element(xml_writer, predicate); + + if(is_xhtml_content) { + if(prefer_cdata) + raptor_xml_writer_raw_counted(xml_writer, + (const unsigned char*)"<![CDATA[", 9); + raptor_xml_writer_raw(xml_writer, (const unsigned char*)field->value); + if(prefer_cdata) + raptor_xml_writer_raw_counted(xml_writer, + (const unsigned char*)"]]>", 3); + } else + raptor_xml_writer_cdata(xml_writer, (const unsigned char*)field->value); + raptor_xml_writer_end_element(xml_writer, predicate); + } else { + RAPTOR_DEBUG3("Field %u - %s had no URI or literal value\n", + f, raptor_rss_fields_info[f].name); + } + raptor_free_xml_element(predicate); + } + } + + + if(item_type == RAPTOR_RSS_CHANNEL) { + if(is_atom) + raptor_rss10_emit_atom_feed(serializer, item); + + if(!is_atom) + raptor_rss10_emit_rss_items(serializer); + } + + /* Add an RDF/XML block with remaining triples if Atom */ + if(item->triples && raptor_sequence_size(item->triples)) + raptor_rss10_emit_rdfxml_item_triples(serializer, item); + + if(emit_container) { + raptor_xml_writer_end_element(xml_writer, element); + raptor_free_xml_element(element); + } + +} + + +/** + * raptor_rss10_serialize_end: + * @serializer: serializer object + * + * INTERNAL (raptor_serializer_factory API) - End a serializing + * + * Return value: non-0 on failure + */ +static int +raptor_rss10_serialize_end(raptor_serializer* serializer) +{ + raptor_rss10_serializer_context *rss_serializer; + raptor_rss_model* rss_model; + int i; + raptor_xml_writer* xml_writer; +#ifdef RAPTOR_DEBUG + int triple_count = 0; +#endif + int is_atom; + raptor_qname **attrs = NULL; + int attrs_count = 0; + raptor_uri* entry_uri = NULL; + raptor_rss_item* entry_item = NULL; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + rss_model = &rss_serializer->model; + is_atom = rss_serializer->is_atom; + + raptor_rss10_build_items(rss_serializer); + + raptor_rss10_move_leftover_statements(rss_serializer); + + raptor_rss10_move_anonymous_statements(rss_serializer); + + if(is_atom) { + char* entry_uri_string; + + raptor_rss10_ensure_atom_feed_valid(rss_serializer); + + raptor_rss10_remove_mapped_fields(rss_serializer); + + entry_uri_string = RAPTOR_OPTIONS_GET_STRING(serializer, + RAPTOR_OPTION_ATOM_ENTRY_URI); + if(entry_uri_string) { + int size = raptor_sequence_size(rss_serializer->items); + entry_uri = raptor_new_uri(rss_serializer->world, + (const unsigned char*)entry_uri_string); + for(i = 0; i < size; i++) { + raptor_rss_item* item; + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + if(raptor_uri_equals(item->uri, entry_uri)) { + entry_item = item; + break; + } + } + if(!entry_item) { + RAPTOR_DEBUG2("Entry URI %s was not found in list of items\n", + raptor_uri_as_string(entry_uri)); + raptor_free_uri(entry_uri); + entry_uri = NULL; + } + } + + } + +#ifdef RAPTOR_DEBUG + if(1) { + int size = raptor_sequence_size(rss_serializer->triples); + for(i = 0; i < size; i++) { + raptor_statement* t; + t = (raptor_statement*)raptor_sequence_get_at(rss_serializer->triples, i); + if(t) { + fprintf(stderr, " %d: ", i); + raptor_statement_print(t, stderr); + fputc('\n', stderr); + triple_count++; + } + } + RAPTOR_DEBUG2("Starting with %d stored triples\n", triple_count); + } +#endif + + if(!rss_model->common[RAPTOR_RSS_CHANNEL]) { + raptor_log_error(serializer->world, RAPTOR_LOG_LEVEL_ERROR, NULL, + "No RSS channel found"); + if(entry_uri) + raptor_free_uri(entry_uri); + return 1; + } + + + if(rss_serializer->xml_writer) + raptor_free_xml_writer(rss_serializer->xml_writer); + + xml_writer = raptor_new_xml_writer(rss_serializer->world, + rss_serializer->nstack, + serializer->iostream); + rss_serializer->xml_writer = xml_writer; + raptor_xml_writer_set_option(xml_writer, + RAPTOR_OPTION_WRITER_AUTO_INDENT, NULL, 1); + raptor_xml_writer_set_option(xml_writer, + RAPTOR_OPTION_WRITER_AUTO_EMPTY, NULL, 1); + + raptor_rss10_build_xml_names(serializer, (is_atom && entry_uri)); + + if(serializer->base_uri && + RAPTOR_OPTIONS_GET_NUMERIC(serializer, RAPTOR_OPTION_WRITE_BASE_URI)) { + const unsigned char* base_uri_string; + + attrs = RAPTOR_CALLOC(raptor_qname**, 1, sizeof(raptor_qname*)); + + base_uri_string = raptor_uri_as_string(serializer->base_uri); + attrs[attrs_count++] = raptor_new_qname_from_namespace_local_name(rss_serializer->world, + rss_serializer->xml_nspace, + (const unsigned char*)"base", + base_uri_string); + } + + if(attrs_count) + raptor_xml_element_set_attributes(rss_serializer->root_element, attrs, + attrs_count); + else + raptor_xml_element_set_attributes(rss_serializer->root_element, NULL, 0); + + raptor_xml_writer_start_element(xml_writer, rss_serializer->root_element); + + + if(entry_item) { + RAPTOR_DEBUG1("Emitting entry\n"); + raptor_rss10_emit_item(serializer, entry_item, RAPTOR_RSS_ITEM, 0); + raptor_xml_writer_raw_counted(xml_writer, (const unsigned char*)"\n", 1); + } else { + i = RAPTOR_RSS_CHANNEL; + RAPTOR_DEBUG3("Emitting type %i - %s\n", i, raptor_rss_items_info[i].name); + raptor_rss10_emit_item(serializer, rss_model->common[i], i, !is_atom); + raptor_xml_writer_raw_counted(xml_writer, (const unsigned char*)"\n", 1); + + if(rss_model->items_count) { + int size = raptor_sequence_size(rss_serializer->items); + for(i = 0; i < size; i++) { + raptor_rss_item* item; + item = (raptor_rss_item*)raptor_sequence_get_at(rss_serializer->items, i); + raptor_rss10_emit_item(serializer, item, RAPTOR_RSS_ITEM, 1); + raptor_xml_writer_raw_counted(xml_writer, (const unsigned char*)"\n", 1); + } + + } + + for(i = RAPTOR_RSS_CHANNEL + 1; i < RAPTOR_RSS_COMMON_SIZE; i++) { + raptor_rss_item* item; + + if(is_atom) { + /* atom 1.0 only serializes rss:item (channel is done above) */ + if(i != RAPTOR_RSS_ITEM) + continue; + } else { + /* rss 1.0 ignores atom:author for now - FIXME */ + if(i == RAPTOR_ATOM_AUTHOR) + continue; + } + + for(item = rss_model->common[i]; item; item = item->next) { + RAPTOR_DEBUG3("Emitting type %i - %s\n", i, + raptor_rss_items_info[i].name); + raptor_rss10_emit_item(serializer, item, i, 1); + } + } + } + + + if(entry_uri) + raptor_free_uri(entry_uri); + + raptor_xml_writer_end_element(xml_writer, rss_serializer->root_element); + + raptor_free_xml_element(rss_serializer->root_element); + + raptor_xml_writer_newline(xml_writer); + + raptor_xml_writer_flush(xml_writer); + + return 0; +} + + +/* add a namespace */ +static int +raptor_rss10_serialize_declare_namespace_from_namespace(raptor_serializer* serializer, + raptor_namespace *nspace) +{ + raptor_rss10_serializer_context* rss_serializer; + int i; + int size; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + + size = raptor_sequence_size(rss_serializer->user_namespaces); + for(i = 0; i < size; i++) { + raptor_namespace* ns; + ns = (raptor_namespace*)raptor_sequence_get_at(rss_serializer->user_namespaces, i); + + /* If prefix is already declared, ignore it */ + if(!ns->prefix && !nspace->prefix) + return 1; + + if(ns->prefix && nspace->prefix && + !strcmp((const char*)ns->prefix, (const char*)nspace->prefix)) + return 1; + + if(ns->uri && nspace->uri && + raptor_uri_equals(ns->uri, nspace->uri)) + return 1; + } + + nspace = raptor_new_namespace_from_uri(rss_serializer->nstack, + nspace->prefix, nspace->uri, + 0); + if(!nspace) + return 1; + + raptor_sequence_push(rss_serializer->user_namespaces, nspace); + return 0; +} + + +/* add a namespace */ +static int +raptor_rss10_serialize_declare_namespace(raptor_serializer* serializer, + raptor_uri *uri, + const unsigned char *prefix) +{ + raptor_rss10_serializer_context* rss_serializer; + raptor_namespace *ns; + int rc; + + rss_serializer = (raptor_rss10_serializer_context*)serializer->context; + + ns = raptor_new_namespace_from_uri(rss_serializer->nstack, prefix, uri, 0); + rc = raptor_rss10_serialize_declare_namespace_from_namespace(serializer, ns); + raptor_free_namespace(ns); + + return rc; +} + + + +/** + * raptor_rss10_serialize_finish_factory: + * @factory: serializer factory + * + * INTERNAL (raptor_serializer_factory API) - finish the serializer factory + */ +static void +raptor_rss10_serialize_finish_factory(raptor_serializer_factory* factory) +{ + +} + + +static const char* rss10_names[3] = { "rss-1.0", NULL}; + +static const char* const rss10_uri_strings[2] = { +"http://purl.org/rss/1.0/spec", + NULL +}; + +#define RSS10_TYPES_COUNT 5 +static const raptor_type_q rss10_types[RSS10_TYPES_COUNT + 1] = { + { "application/rss+xml", 19, 10}, + { "application/rss", 15, 3}, + { "text/rss", 8, 3}, + { "application/xml", 15, 3}, + { "text/xml", 8, 3}, + { NULL, 0, 0} +}; + +static int +raptor_rss10_serializer_register_factory(raptor_serializer_factory *factory) +{ + factory->desc.names = rss10_names; + factory->desc.mime_types = rss10_types; + + factory->desc.label = "RSS 1.0"; + factory->desc.uri_strings = rss10_uri_strings; + + factory->context_length = sizeof(raptor_rss10_serializer_context); + + factory->init = raptor_rss10_serialize_init; + factory->terminate = raptor_rss10_serialize_terminate; + factory->declare_namespace = raptor_rss10_serialize_declare_namespace; + factory->declare_namespace_from_namespace = raptor_rss10_serialize_declare_namespace_from_namespace; + factory->serialize_start = raptor_rss10_serialize_start; + factory->serialize_statement = raptor_rss10_serialize_statement; + factory->serialize_end = raptor_rss10_serialize_end; + factory->finish_factory = raptor_rss10_serialize_finish_factory; + + return 0; +} + + + +static const char* atom_names[3] = { "atom", NULL}; + +static const char* const atom_uri_strings[2] = { + "http://www.ietf.org/rfc/rfc4287.txt", + NULL +}; + +#define ATOM_TYPES_COUNT 1 +static const raptor_type_q atom_types[ATOM_TYPES_COUNT + 1] = { + { "application/atom+xml", 20, 10}, + { NULL, 0, 0} +}; + +static int +raptor_atom_serializer_register_factory(raptor_serializer_factory *factory) +{ + factory->desc.names = atom_names; + factory->desc.mime_types = atom_types; + + factory->desc.label = "Atom 1.0"; + factory->desc.uri_strings = atom_uri_strings; + + factory->context_length = sizeof(raptor_rss10_serializer_context); + + factory->init = raptor_rss10_serialize_init; + factory->terminate = raptor_rss10_serialize_terminate; + factory->declare_namespace = raptor_rss10_serialize_declare_namespace; + factory->declare_namespace_from_namespace = raptor_rss10_serialize_declare_namespace_from_namespace; + factory->serialize_start = raptor_rss10_serialize_start; + factory->serialize_statement = raptor_rss10_serialize_statement; + factory->serialize_end = raptor_rss10_serialize_end; + factory->finish_factory = raptor_rss10_serialize_finish_factory; + + return 0; +} + + + +int +raptor_init_serializer_rss10(raptor_world* world) +{ + return !raptor_serializer_register_factory(world, + &raptor_rss10_serializer_register_factory); +} + +int +raptor_init_serializer_atom(raptor_world* world) +{ + return !raptor_serializer_register_factory(world, + &raptor_atom_serializer_register_factory); +} + |