#include "desc.h" LSUP_rc LSR_desc_new_multi (LSUP_Graph *const *data, LSR_Desc **rsrc_p) { LSUP_rc rc = LSUP_OK; LSR_Desc *rsrc; MALLOC_GUARD (rsrc, LSUP_MEM_ERR); uuid_generate_random (rsrc->id); // Default context graph. rsrc->main_data = LSUP_graph_new ( LSUP_default_ctx, LSUP_STORE_HTABLE, NULL, NULL, 0); LSUP_GraphIterator *lu_it, *add_it, *admin_add_it = NULL; LSUP_Term *dest_s, *dest_p, *dest_o; LSUP_Triple src_spo_s, dest_spo_s; LSUP_Triple *src_spo = &src_spo_s, *dest_spo = &dest_spo_s; LSUP_Term *rsrc_uri = LSR_id_to_urn (rsrc->id, NULL); LSUP_Term *rdf_t = LSUP_iriref_new ("rdf:type", LSUP_default_nsm); // Count graphs inserted and allocate space. size_t ct = 0; while (data[ct]) ct++; rsrc->user_data = calloc (sizeof (*rsrc->user_data), ct + 1); if (UNLIKELY (! rsrc->user_data)) return LSUP_MEM_ERR; /* BEGIN adding user data. */ // Loop over input graphs. for (size_t i = 0; i < ct; i++) { LSUP_Term *gr_uri = LSUP_term_copy (LSUP_graph_uri (data[i])); LSUP_Term *rel_uri = LSUP_iriref_relative (rsrc_uri, gr_uri); if (strstr (rel_uri->data, "#__") == rel_uri->data) { log_error ("Fragment URI cannot start with double underscore."); rc = LSUP_VALUE_ERR; } LSUP_term_free (rel_uri); if (rc < 0) goto finally; rsrc->user_data[i] = LSUP_graph_new ( gr_uri, LSUP_STORE_HTABLE, NULL, NULL, 0); add_it = LSUP_graph_add_init (rsrc->user_data[i]); lu_it = LSUP_graph_lookup (rsrc->user_data[i], NULL, NULL, NULL, NULL); // Loop over graph triples. while (LSUP_graph_iter_next (lu_it, &src_spo) == LSUP_OK) { dest_s = LSUP_IS_IRI (src_spo->s) ? LSUP_iriref_relative (rsrc_uri, src_spo->s) : src_spo->s; dest_p = LSUP_term_copy (src_spo->p); dest_o = LSUP_IS_IRI (src_spo->s) ? LSUP_iriref_relative (rsrc_uri, src_spo->s) : src_spo->s; LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); // if the pred is managed, ignore the triple and send a warning. if (hashmap_get(LSR_managed_preds, dest_spo->p)) { log_warn ( "Predicate %s is managed. Skipping triple.", dest_p->data); goto loop_end; } /* * If the subject or object is a resource, check if it exists; if * it does, add triple to user_data; if not, return an error. */ uuid_t id_tmp; LSUP_rc tmp_rc; // Check subject. if (LSR_IS_RSRC_IRI (dest_s)) { uuid_parse (dest_s->data + strlen (LSR_RSRC_PFX), id_tmp); tmp_rc = LSR_desc_get (id_tmp, NULL); if (tmp_rc != LSUP_OK) { log_error ( "Referenced subject does not exist: %s", dest_s->data + strlen (LSR_RSRC_PFX)); rc = LSUP_VALUE_ERR; goto finally; } } // Check object. if (LSR_IS_RSRC_IRI (dest_o)) { uuid_parse (dest_o->data + strlen (LSR_RSRC_PFX), id_tmp); tmp_rc = LSR_desc_get (id_tmp, NULL); if (tmp_rc != LSUP_OK) { log_error ( "Referenced object does not exist: %s", dest_o->data + strlen (LSR_RSRC_PFX)); rc = LSUP_VALUE_ERR; goto finally; } } // RDF type check. if ( LSUP_term_equals ( gr_uri, LSUP_iriref_absolute (rsrc_uri, dest_spo->s)) && LSUP_term_equals (rdf_t, dest_spo->p) ) { // If the resource is a special type, handle specific workflow. // TODO if (hashmap_get (LSR_managed_types, dest_spo->o)) { } } // Add triple to user_data. LSUP_graph_add_iter (add_it, dest_spo); loop_end: if (dest_s != src_spo->s) LSUP_term_free (dest_s); if (dest_o != src_spo->o) LSUP_term_free (dest_o); } // Add user graph metadata to default graph. admin_add_it = LSUP_graph_add_init (rsrc->main_data); dest_s = gr_uri; dest_p = LSUP_iriref_new ("rdf:type", LSUP_default_nsm); dest_o = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm); LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, dest_spo); LSUP_term_free (dest_o); dest_o = LSUP_iriref_new ("lsup:UserMetadata", LSUP_default_nsm); LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, dest_spo); LSUP_term_free (dest_o); LSUP_term_free (dest_p); // Relationship between data graph and resource. dest_p = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm); dest_o = rsrc_uri; LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, dest_spo); LSUP_term_free (dest_p); LSUP_graph_iter_free (lu_it); LSUP_graph_add_done (add_it); LSUP_graph_add_done (admin_add_it); lu_it = add_it = admin_add_it = NULL; } /* END adding user data. */ /* BEGIN adding managed (admin) data. */ LSUP_Term *gr_uri = LSR_id_to_urn (rsrc->id, "__admin"); rsrc->admin_data = LSUP_graph_new ( gr_uri, LSUP_STORE_HTABLE, NULL, NULL, 0); admin_add_it = LSUP_graph_add_init (rsrc->admin_data); dest_s = LSUP_iriref_new("", NULL); // Relative to resource URI. LSUP_Triple admin_spo_s; LSUP_Triple *admin_spo = &admin_spo_s; // RDF types. dest_p = rdf_t; dest_o = LSUP_iriref_new ("lsup:Resource", LSUP_default_nsm); LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, admin_spo); LSUP_term_free (dest_o); dest_o = LSUP_iriref_new ("lsup:DescriptiveResource", LSUP_default_nsm); LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, admin_spo); LSUP_term_free (dest_o); // Timestamps. For now, second precision is fine. time_t now; time (&now); char buf [sizeof ("0000-00-00T00:00:00Z")]; strftime (buf, sizeof (buf), "%FT%TZ", gmtime (&now)); dest_p = LSUP_iriref_new ("lsup:created", LSUP_default_nsm); dest_o = LSUP_literal_new ( buf, LSUP_iriref_new ("xsd:dateTime", LSUP_default_nsm)); LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, admin_spo); LSUP_term_free (dest_p); dest_p = LSUP_iriref_new ("lsup:lastModified", LSUP_default_nsm); LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, admin_spo); LSUP_term_free (dest_p); LSUP_term_free (dest_o); LSUP_graph_add_done (admin_add_it); /* END adding admin data. */ /* BEGIN adding graph metadata (main). */ admin_add_it = LSUP_graph_add_init (rsrc->main_data); LSUP_term_free (dest_s); dest_s = gr_uri; dest_p = rdf_t; dest_o = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm); LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, dest_spo); LSUP_term_free (dest_o); dest_o = LSUP_iriref_new ("lsup:AdminMetadata", LSUP_default_nsm); LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, dest_spo); LSUP_term_free (dest_o); // Relationship between data graph and resource. dest_p = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm); dest_o = rsrc_uri; LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, dest_spo); LSUP_term_free (dest_p); LSUP_graph_add_done (admin_add_it); admin_add_it = NULL; /* END adding graph metadata. */ finally: LSUP_term_free (rsrc_uri); LSUP_term_free (rdf_t); if (rc < 0) goto fail; rsrc->flags |= LSR_RS_DIRTY; *rsrc_p = rsrc; return rc; fail: LSUP_graph_iter_free (lu_it); LSUP_graph_add_done (add_it); LSUP_graph_add_done (admin_add_it); LSR_desc_free (rsrc); *rsrc_p = NULL; return rc; } LSUP_rc LSR_desc_store (const LSR_Desc *rsrc) { // TODO Make atomic. Needs to implement transactions in backend. LSR_Desc *old_rsrc; LSUP_rc rc = LSR_desc_get (rsrc->id, &old_rsrc); if (UNLIKELY (rc < 0)) return rc; // Remove all existing user graphs. if (old_rsrc) { LSUP_Term *main_data_urn = LSUP_graph_uri (old_rsrc->main_data); // TODO Handle managed preds and types. // TODO Handle conflict between disjoint managed types. // TODO Retain created and created_by. for (size_t i = 0; old_rsrc->user_data[i] != NULL; i++) { LSUP_Term *gr_uri = LSUP_graph_uri (old_rsrc->user_data[i]); size_t ct; // Remove triples from user graph. LSUP_graph_remove (old_rsrc->user_data[i], NULL, NULL, NULL, &ct); log_debug ( "Removed %lu triples from graph %s", ct, gr_uri->data); // Remove user graph metadata. LSUP_graph_remove (old_rsrc->main_data, gr_uri, NULL, NULL, NULL); LSUP_graph_remove (old_rsrc->main_data, NULL, NULL, gr_uri, NULL); } } // Add new triples. for (size_t i = 0; rsrc->user_data[i] != NULL; i++) { LSUP_Term *gr_uri = LSUP_graph_uri (rsrc->user_data[i]); LSUP_graph_store (rsrc->user_data[i], NULL, NULL); } // Update admin data. LSUP_graph_store (rsrc->admin_data, NULL, NULL); // Update graph metadata. LSUP_graph_store (rsrc->main_data, NULL, NULL); } LSUP_rc LSUP_desc_update (LSR_id id, LSUP_Term **remove, LSUP_Triple *add) { } LSUP_rc LSR_desc_get (const uuid_t id, LSR_Desc **rsrc_p) { LSUP_rc rc = LSUP_OK; LSUP_Graph *main_gr = LSUP_graph_new ( LSUP_default_ctx, LSR_DEFAULT_BACKEND, NULL, NULL, 0); LSUP_Term *s = LSR_id_to_urn (id, NULL), *p = LSUP_iriref_new ("rdf:type", LSUP_default_nsm), *o = LSUP_iriref_new ("lsup:Resource", LSUP_default_nsm); LSUP_Triple *spo = LSUP_triple_new (s, p, o); LSUP_triple_free (spo); if (!LSUP_graph_contains (main_gr, spo)) { rc = LSUP_NORESULT; goto finally; } LSUP_term_free (o); o = LSUP_iriref_new ("lsup:DescriptiveResource", LSUP_default_nsm); spo = LSUP_triple_new (s, p, o); if (!LSUP_graph_contains (main_gr, spo)) { log_error ("%s is not a descriptive resource.", o->data); rc = LSUP_NORESULT; goto finally; } LSUP_term_free (p); p = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm); size_t ct = 0, i = 0; // Find all graphs making up the resource. LSUP_GraphIterator *it = LSUP_graph_lookup (main_gr, NULL, p, s, &ct); LSUP_Graph **data = calloc (sizeof (*data), ct + 1); while (LSUP_graph_iter_next (it, &spo)) { data[i] = LSUP_graph_new (spo->s, LSR_DEFAULT_BACKEND, NULL, NULL, 0); if (! data[i++]) break; // Last slot remains NULL (sentinel). } LSUP_graph_iter_free (it); rc = LSR_desc_new_multi (data, rsrc_p); i = 0; while (i < ct) LSUP_graph_free (data[i++]); free (data); finally: LSUP_triple_free (spo); LSUP_term_free (s); LSUP_term_free (p); LSUP_term_free (o); LSUP_graph_free (main_gr); return rc; } LSUP_Graph * LSR_desc_metadata (const LSR_Desc *rsrc) { uuid_t uuid; char *rsrc_uri_str = LSUP_graph_uri (rsrc->admin_data)->data, *frag = "#metadata-", uuid_str [UUID_STR_LEN], id_str [8], *uri_str = malloc ( strlen (frag) + strlen (rsrc_uri_str) + sizeof (id_str)); if (UNLIKELY (!uri_str)) return NULL; uuid_generate_random (uuid); uuid_unparse_lower (uuid, uuid_str); strncpy (id_str, uuid_str, sizeof (id_str) - 1); sprintf (uri_str, "%s%s%s", rsrc_uri_str, frag, id_str); LSUP_Graph *res = LSUP_graph_copy (rsrc->admin_data); if (LIKELY (res)) LSUP_graph_set_uri (res, LSUP_iriref_new (uri_str, NULL)); free (uri_str); free (rsrc_uri_str); free (uuid); return res; } LSUP_Graph ** LSR_desc_user_data (const LSR_Desc *rsrc) { uuid_t uuid; char *rsrc_uri_str = LSUP_graph_uri (rsrc->admin_data)->data, *frag = "#metadata-", uuid_str [UUID_STR_LEN], id_str [8], *uri_str = malloc ( strlen (frag) + strlen (rsrc_uri_str) + sizeof (id_str)); if (UNLIKELY (!uri_str)) return NULL; size_t ct = 0; while (rsrc->user_data[ct]) ct ++; LSUP_Graph **res = malloc (sizeof *res * (ct + 1)); if (UNLIKELY (!res)) return NULL; for (size_t i = 0; i < ct; i++) { uuid_generate_random (uuid); uuid_unparse_lower (uuid, uuid_str); strncpy (id_str, uuid_str, sizeof (id_str) - 1); sprintf (uri_str, "%s%s%s", rsrc_uri_str, frag, id_str); res[i] = LSUP_graph_copy (rsrc->user_data[i]); if (LIKELY (res)) LSUP_graph_set_uri (res[i], LSUP_iriref_new (uri_str, NULL)); } res[ct] = NULL; free (uri_str); free (rsrc_uri_str); free (uuid); return res; } void LSR_desc_free (LSR_Desc *rsrc) { size_t i = 0; while (rsrc->user_data[i]) LSUP_graph_free (rsrc->user_data[i++]); free (rsrc->user_data); LSUP_graph_free (rsrc->admin_data); LSUP_graph_free (rsrc->main_data); free (rsrc); } LSUP_rc LSR_desc_store (const LSR_Desc *rsrc) { // TODO Make atomic. Needs to implement transactions in backend. LSR_Desc *old_rsrc; LSUP_rc rc = LSR_desc_get (rsrc->id, &old_rsrc); if (UNLIKELY (rc < 0)) return rc; // Remove all existing user graphs. if (rc == LSUP_OK) { //LSUP_Term *main_data_urn = LSUP_graph_uri (old_rsrc->main_data); for (size_t i = 0; old_rsrc->user_data[i] != NULL; i++) { LSUP_Term *gr_uri = LSUP_graph_uri (old_rsrc->user_data[i]); size_t ct; // Remove triples from user graph. LSUP_graph_remove (old_rsrc->user_data[i], NULL, NULL, NULL, &ct); log_debug ( "Removed %lu triples from graph %s", ct, gr_uri->data); // Remove user graph metadata. LSUP_graph_remove (old_rsrc->main_data, gr_uri, NULL, NULL, NULL); LSUP_graph_remove (old_rsrc->main_data, NULL, NULL, gr_uri, NULL); } } else rc = LSUP_OK; // Add new triples. for (size_t i = 0; rsrc->user_data[i] != NULL; i++) { //LSUP_Term *gr_uri = LSUP_graph_uri (rsrc->user_data[i]); log_trace ("Storing data graph #%lu", i); LSUP_graph_store (rsrc->user_data[i], NULL, NULL); } // Update admin data. LSUP_graph_store (rsrc->admin_data, NULL, NULL); // Update graph metadata. LSUP_graph_store (rsrc->main_data, NULL, NULL); return rc; } LSUP_rc LSUP_desc_update (LSR_id id, LSUP_Term **remove, LSUP_Triple *add) { LSUP_rc rc = LSUP_OK; return rc; }