/* -*- Mode: c; c-basic-offset: 2 -*- * * raptor_serialize_dot.c - Serialize RDF graph to GraphViz DOT format * * Copyright (C) 2004-2010, David Beckett http://www.dajobe.org/ * Copyright (C) 2004-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 #include #include #include #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif /* Raptor includes */ #include "raptor2.h" #include "raptor_internal.h" /* * Raptor dot serializer object */ typedef struct { raptor_namespace_stack *nstack; raptor_sequence *namespaces; raptor_sequence *resources; raptor_sequence *literals; raptor_sequence *bnodes; } raptor_dot_context; /* add a namespace */ static int raptor_dot_serializer_declare_namespace_from_namespace(raptor_serializer* serializer, raptor_namespace *nspace) { raptor_dot_context * context = (raptor_dot_context *)serializer->context; int i; for(i = 0 ; i < raptor_sequence_size(context->namespaces) ; i++ ) { raptor_namespace * ns; ns = (raptor_namespace *)raptor_sequence_get_at(context->namespaces, i); /* If prefix is already declared, ignore it */ if((!ns->prefix && !nspace->prefix) || (ns->prefix && nspace->prefix && !strcmp((const char*)ns->prefix, (const char*)nspace->prefix)) || (ns->uri && nspace->uri && raptor_uri_equals(ns->uri, nspace->uri)) ) return 1; } nspace = raptor_new_namespace_from_uri(context->nstack, nspace->prefix, nspace->uri, 0); if(!nspace) return 1; raptor_sequence_push(context->namespaces, nspace); return 0; } /* add a namespace */ static int raptor_dot_serializer_declare_namespace(raptor_serializer* serializer, raptor_uri* uri, const unsigned char *prefix) { raptor_dot_context * context = (raptor_dot_context *)serializer->context; raptor_namespace *ns; int rc; ns = raptor_new_namespace_from_uri(context->nstack, prefix, uri, 0); rc = raptor_dot_serializer_declare_namespace_from_namespace(serializer, ns); raptor_free_namespace(ns); return rc; } /* create a new serializer */ static int raptor_dot_serializer_init(raptor_serializer *serializer, const char *name) { raptor_dot_context * context = (raptor_dot_context *)serializer->context; /* Setup namespace handling */ context->nstack = raptor_new_namespaces(serializer->world, 1); context->namespaces = raptor_new_sequence((raptor_data_free_handler)raptor_free_namespace, NULL); /* We keep a list of nodes to avoid duplication (which isn't * critical in graphviz, but why bloat the file?) */ context->resources = raptor_new_sequence((raptor_data_free_handler)raptor_free_term, NULL); context->literals = raptor_new_sequence((raptor_data_free_handler)raptor_free_term, NULL); context->bnodes = raptor_new_sequence((raptor_data_free_handler)raptor_free_term, NULL); return 0; } /** * raptor_dot_iostream_write_string: * @iostr: #raptor_iostream to write to * @string: UTF-8 string to write * @len: length of UTF-8 string * or \0 for no escaping. * * Write an UTF-8 string, escaped for graphviz. * * Return value: non-0 on failure. **/ static int raptor_dot_iostream_write_string(raptor_iostream *iostr, const unsigned char *string) { unsigned char c; for( ; (c = *string) ; string++ ) { if( (c == '\\') || (c == '"') || (c == '|') || (c == '{') || (c == '}') ) { raptor_iostream_write_byte('\\', iostr); raptor_iostream_write_byte(c, iostr); } else if( c == '\n' ) { raptor_iostream_write_byte('\\', iostr); raptor_iostream_write_byte('n', iostr); } else raptor_iostream_write_byte(c, iostr); } return 0; } static void raptor_dot_serializer_write_term_type(raptor_serializer * serializer, raptor_term_type type) { switch(type) { case RAPTOR_TERM_TYPE_LITERAL: raptor_iostream_write_byte('L', serializer->iostream); break; case RAPTOR_TERM_TYPE_BLANK: raptor_iostream_write_byte('B', serializer->iostream); break; case RAPTOR_TERM_TYPE_URI: raptor_iostream_write_byte('R', serializer->iostream); break; case RAPTOR_TERM_TYPE_UNKNOWN: raptor_iostream_write_byte('?', serializer->iostream); break; } } static void raptor_dot_serializer_write_uri(raptor_serializer* serializer, raptor_uri* uri) { raptor_dot_context* context = (raptor_dot_context*)serializer->context; unsigned char* full = raptor_uri_as_string(uri); int i; for(i = 0 ; i < raptor_sequence_size(context->namespaces) ; i++ ) { raptor_namespace* ns = (raptor_namespace*)raptor_sequence_get_at(context->namespaces, i); const unsigned char* ns_uri_string; size_t ns_uri_string_len; ns_uri_string = raptor_uri_as_counted_string(ns->uri, &ns_uri_string_len); if(!strncmp((char*)full, (char*)ns_uri_string, ns_uri_string_len) ) { const unsigned char* prefix = raptor_namespace_get_prefix(ns); if(prefix) { raptor_iostream_string_write(prefix, serializer->iostream); raptor_iostream_write_byte(':', serializer->iostream); } raptor_iostream_string_write(full + ns_uri_string_len, serializer->iostream); return; } } raptor_iostream_string_write(full, serializer->iostream); } static void raptor_dot_serializer_write_term(raptor_serializer * serializer, raptor_term* term) { switch(term->type) { case RAPTOR_TERM_TYPE_LITERAL: raptor_dot_iostream_write_string(serializer->iostream, term->value.literal.string); if(term->value.literal.language) { raptor_iostream_write_byte('|', serializer->iostream); raptor_iostream_string_write("Language: ", serializer->iostream); raptor_iostream_string_write(term->value.literal.language, serializer->iostream); } if(term->value.literal.datatype) { raptor_iostream_write_byte('|', serializer->iostream); raptor_iostream_string_write("Datatype: ", serializer->iostream); raptor_dot_serializer_write_uri(serializer, term->value.literal.datatype); } break; case RAPTOR_TERM_TYPE_BLANK: raptor_iostream_counted_string_write("_:", 2, serializer->iostream); raptor_iostream_string_write(term->value.blank.string, serializer->iostream); break; case RAPTOR_TERM_TYPE_URI: raptor_dot_serializer_write_uri(serializer, term->value.uri); break; case RAPTOR_TERM_TYPE_UNKNOWN: default: raptor_log_error_formatted(serializer->world, RAPTOR_LOG_LEVEL_ERROR, NULL, "Triple has unsupported term type %u", term->type); } } /* Check the list to see if the node is a duplicate. If not, add it * to the list. */ static void raptor_dot_serializer_assert_node(raptor_serializer* serializer, raptor_term* assert_node) { raptor_dot_context* context = (raptor_dot_context*)serializer->context; raptor_sequence* seq = NULL; int i; /* Which list are we searching? */ switch(assert_node->type) { case RAPTOR_TERM_TYPE_URI: seq = context->resources; break; case RAPTOR_TERM_TYPE_BLANK: seq = context->bnodes; break; case RAPTOR_TERM_TYPE_LITERAL: seq = context->literals; break; case RAPTOR_TERM_TYPE_UNKNOWN: break; } for(i = 0 ; i < raptor_sequence_size(seq) ; i++ ) { raptor_term* node = (raptor_term*)raptor_sequence_get_at(seq, i); if(raptor_term_equals(node, assert_node)) return; } raptor_sequence_push(seq, raptor_term_copy(assert_node)); } /* start a serialize */ static int raptor_dot_serializer_start(raptor_serializer* serializer) { raptor_iostream_string_write((const unsigned char*)"digraph {\n\trankdir = LR;\n\tcharset=\"utf-8\";\n\n", serializer->iostream); return 0; } static int raptor_dot_serializer_write_colors(raptor_serializer* serializer, raptor_term_type type) { char* value; switch(type) { case RAPTOR_TERM_TYPE_URI: value = RAPTOR_OPTIONS_GET_STRING(serializer, RAPTOR_OPTION_RESOURCE_BORDER); if(value) { raptor_iostream_string_write((const unsigned char*)", color=", serializer->iostream); raptor_iostream_string_write((const unsigned char*)value, serializer->iostream); } else raptor_iostream_string_write((const unsigned char*)", color = blue", serializer->iostream); value = RAPTOR_OPTIONS_GET_STRING(serializer, RAPTOR_OPTION_RESOURCE_FILL); if(value) { raptor_iostream_string_write((const unsigned char*)", style = filled, fillcolor=", serializer->iostream); raptor_iostream_string_write((const unsigned char*)value, serializer->iostream); } break; case RAPTOR_TERM_TYPE_BLANK: value = RAPTOR_OPTIONS_GET_STRING(serializer, RAPTOR_OPTION_BNODE_BORDER); if(value) { raptor_iostream_string_write((const unsigned char*)", color=", serializer->iostream); raptor_iostream_string_write((const unsigned char*)value, serializer->iostream); } else raptor_iostream_string_write((const unsigned char*)", color = green", serializer->iostream); value = RAPTOR_OPTIONS_GET_STRING(serializer, RAPTOR_OPTION_BNODE_FILL); if(value) { raptor_iostream_string_write((const unsigned char*)", style = filled, fillcolor=", serializer->iostream); raptor_iostream_string_write((const unsigned char*)value, serializer->iostream); } break; case RAPTOR_TERM_TYPE_LITERAL: value = RAPTOR_OPTIONS_GET_STRING(serializer, RAPTOR_OPTION_LITERAL_BORDER); if(value) { raptor_iostream_string_write((const unsigned char*)", color=", serializer->iostream); raptor_iostream_string_write((const unsigned char*)value, serializer->iostream); } value = RAPTOR_OPTIONS_GET_STRING(serializer, RAPTOR_OPTION_LITERAL_FILL); if(value) { raptor_iostream_string_write((const unsigned char*)", style = filled, fillcolor=", serializer->iostream); raptor_iostream_string_write((const unsigned char*)value, serializer->iostream); } break; case RAPTOR_TERM_TYPE_UNKNOWN: default: break; } return 0; } /* end a serialize */ static int raptor_dot_serializer_end(raptor_serializer* serializer) { raptor_dot_context* context = (raptor_dot_context*)serializer->context; raptor_term* node; int i; /* Print our nodes. */ raptor_iostream_string_write((const unsigned char*)"\n\t// Resources\n", serializer->iostream); for(i = 0 ; i < raptor_sequence_size(context->resources) ; i++ ) { node = (raptor_term*)raptor_sequence_get_at(context->resources, i); raptor_iostream_string_write((const unsigned char*)"\t\"R", serializer->iostream); raptor_dot_serializer_write_term(serializer, node); raptor_iostream_string_write((const unsigned char*)"\" [ label=\"", serializer->iostream); raptor_dot_serializer_write_term(serializer, node); raptor_iostream_string_write((const unsigned char*)"\", shape = ellipse", serializer->iostream); raptor_dot_serializer_write_colors(serializer, RAPTOR_TERM_TYPE_URI); raptor_iostream_string_write((const unsigned char*)" ];\n", serializer->iostream); } raptor_free_sequence(context->resources); raptor_iostream_string_write((const unsigned char*)"\n\t// Anonymous nodes\n", serializer->iostream); for(i = 0 ; i < raptor_sequence_size(context->bnodes) ; i++ ) { node = (raptor_term *)raptor_sequence_get_at(context->bnodes, i); raptor_iostream_string_write((const unsigned char*)"\t\"B", serializer->iostream); raptor_dot_serializer_write_term(serializer, node); raptor_iostream_string_write((const unsigned char*)"\" [ label=\"", serializer->iostream); raptor_iostream_string_write((const unsigned char*)"\", shape = circle", serializer->iostream); raptor_dot_serializer_write_colors(serializer, RAPTOR_TERM_TYPE_BLANK); raptor_iostream_string_write((const unsigned char*)" ];\n", serializer->iostream); } raptor_free_sequence(context->bnodes); raptor_iostream_string_write((const unsigned char*)"\n\t// Literals\n", serializer->iostream); for(i = 0 ; i < raptor_sequence_size(context->literals) ; i++ ) { node = (raptor_term*)raptor_sequence_get_at(context->literals, i); raptor_iostream_string_write((const unsigned char*)"\t\"L", serializer->iostream); raptor_dot_serializer_write_term(serializer, node); raptor_iostream_string_write((const unsigned char*)"\" [ label=\"", serializer->iostream); raptor_dot_serializer_write_term(serializer, node); raptor_iostream_string_write((const unsigned char*)"\", shape = record", serializer->iostream); raptor_dot_serializer_write_colors(serializer, RAPTOR_TERM_TYPE_LITERAL); raptor_iostream_string_write((const unsigned char*)" ];\n", serializer->iostream); } raptor_free_sequence(context->literals); raptor_iostream_string_write((const unsigned char*)"\n\tlabel=\"\\n\\nModel:\\n", serializer->iostream); if(serializer->base_uri) raptor_iostream_string_write(raptor_uri_as_string(serializer->base_uri), serializer->iostream); else raptor_iostream_string_write("(Unknown)", serializer->iostream); if(raptor_sequence_size(context->namespaces)) { raptor_iostream_string_write((const unsigned char*)"\\n\\nNamespaces:\\n", serializer->iostream); for(i = 0 ; i < raptor_sequence_size(context->namespaces) ; i++ ) { raptor_namespace* ns; const unsigned char* prefix; ns = (raptor_namespace*)raptor_sequence_get_at(context->namespaces, i); prefix = raptor_namespace_get_prefix(ns); if(prefix) { raptor_iostream_string_write((const unsigned char*)ns->prefix, serializer->iostream); raptor_iostream_string_write((const unsigned char*)": ", serializer->iostream); } raptor_iostream_string_write(raptor_uri_as_string(ns->uri), serializer->iostream); raptor_iostream_string_write((const unsigned char*)"\\n", serializer->iostream); } raptor_free_sequence(context->namespaces); } raptor_iostream_string_write((const unsigned char*)"\";\n", serializer->iostream); raptor_iostream_string_write((const unsigned char*) "}\n", serializer->iostream); return 0; } /* destroy a serializer */ static void raptor_dot_serializer_terminate(raptor_serializer* serializer) { /* raptor_dot_context* context = (raptor_dot_context*)serializer->context; */ /* Everything should have been freed in raptor_dot_serializer_end */ } /* serialize a statement */ static int raptor_dot_serializer_statement(raptor_serializer* serializer, raptor_statement *statement) { /* Cache the nodes for later. */ raptor_dot_serializer_assert_node(serializer, statement->subject); raptor_dot_serializer_assert_node(serializer, statement->object); raptor_iostream_string_write((const unsigned char*)"\t\"", serializer->iostream); raptor_dot_serializer_write_term_type(serializer, statement->subject->type); raptor_dot_serializer_write_term(serializer, statement->subject); raptor_iostream_string_write((const unsigned char*)"\" -> \"", serializer->iostream); raptor_dot_serializer_write_term_type(serializer, statement->object->type); raptor_dot_serializer_write_term(serializer, statement->object); raptor_iostream_string_write((const unsigned char*)"\" [ label=\"", serializer->iostream); raptor_dot_serializer_write_term(serializer, statement->predicate); raptor_iostream_string_write((const unsigned char*)"\" ];\n", serializer->iostream); return 0; } static const char* const dot_names[2] = { "dot", NULL}; static const char* const dot_uri_strings[2] = { "http://www.graphviz.org/doc/info/lang.html", NULL }; #define DOT_TYPES_COUNT 1 static const raptor_type_q dot_types[DOT_TYPES_COUNT + 1] = { { "text/x-graphviz", 15, 5}, { NULL, 0, 0} }; static int raptor_dot_serializer_register_factory(raptor_serializer_factory *factory) { factory->desc.names = dot_names; factory->desc.mime_types = dot_types; factory->desc.label = "GraphViz DOT format"; factory->desc.uri_strings = dot_uri_strings;; factory->context_length = sizeof(raptor_dot_context); factory->init = raptor_dot_serializer_init; factory->declare_namespace = raptor_dot_serializer_declare_namespace; factory->declare_namespace_from_namespace = raptor_dot_serializer_declare_namespace_from_namespace; factory->serialize_start = raptor_dot_serializer_start; factory->serialize_statement = raptor_dot_serializer_statement; factory->serialize_end = raptor_dot_serializer_end; factory->finish_factory = NULL; factory->terminate = raptor_dot_serializer_terminate; return 0; } int raptor_init_serializer_dot(raptor_world* world) { return !raptor_serializer_register_factory(world, &raptor_dot_serializer_register_factory); }