#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. LSUP_Term *main_data_urn = LSR_id_to_urn (rsrc->id, "__main"); rsrc->main_data = LSUP_graph_new (NULL, main_data_urn, LSUP_default_nsm); LSUP_term_free (main_data_urn); log_debug ( "Main data graph: %s", LSUP_graph_uri(rsrc->main_data)->data); PCHECK (LSR_desc_update (rsrc, NULL, data), finally); LSUP_Term *rsrc_uri = LSR_id_to_urn (rsrc->id, NULL); /* BEGIN adding managed (admin) data. */ LSUP_Term *admin_uri = LSR_id_to_urn (rsrc->id, "__admin"); rsrc->admin_data = LSUP_graph_new (NULL, admin_uri, NULL); log_debug ( "Admin data graph (@%p): %s", LSUP_graph_uri (rsrc->admin_data), LSUP_graph_uri (rsrc->admin_data)->data); // Calculate timestamp. time_t now; time (&now); char tstamp [sizeof ("0000-00-00T00:00:00Z")]; strftime (tstamp, sizeof (tstamp), "%FT%TZ", gmtime (&now)); LSUP_Term *s = LSUP_iriref_new("", NULL), // Relative to resource URI. *created_t = LSUP_iriref_new ("lsup:created", LSUP_default_nsm), *rsrc_t = LSUP_iriref_new ("lsup:Resource", LSUP_default_nsm), *desc_rsrc_t = LSUP_iriref_new ( "lsup:DescriptiveResource", LSUP_default_nsm), *ts_t = LSUP_literal_new ( tstamp, LSUP_iriref_new ("xsd:dateTime", LSUP_default_nsm)); LSUP_Triple *admin_trp[] = { LSUP_triple_new (s, LSR_rdf_t, rsrc_t), LSUP_triple_new (s, LSR_rdf_t, desc_rsrc_t), LSUP_triple_new (s, created_t, ts_t), NULL, }; rc = LSUP_graph_add (rsrc->admin_data, admin_trp, NULL); PCHECK (rc, finally); for (size_t i = 0; admin_trp[i]; i++) free (admin_trp[i]); LSUP_term_free (s); LSUP_term_free (created_t); LSUP_term_free (rsrc_t); LSUP_term_free (desc_rsrc_t); LSUP_term_free (ts_t); /* END adding admin data. */ /* BEGIN adding graph metadata (main). */ LSUP_Term *meta_t = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm), *admin_meta_t = LSUP_iriref_new ( "lsup:AdminMetadata", LSUP_default_nsm), *topic_t = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm); LSUP_Triple *main_trp[] = { LSUP_triple_new (admin_uri, LSR_rdf_t, meta_t), LSUP_triple_new (admin_uri, LSR_rdf_t, admin_meta_t), LSUP_triple_new (admin_uri, topic_t, rsrc_uri), NULL }; rc = LSUP_graph_add (rsrc->main_data, main_trp, NULL); for (size_t i = 0; main_trp[i]; i++) free (main_trp[i]); LSUP_term_free (meta_t); LSUP_term_free (admin_meta_t); LSUP_term_free (topic_t); /* END adding graph metadata. */ finally: LSUP_term_free (rsrc_uri); LSUP_term_free (admin_uri); if (rc < 0) goto fail; rsrc->flags |= LSR_RS_DIRTY; *rsrc_p = rsrc; return rc; fail: LSR_desc_free (rsrc); *rsrc_p = NULL; return rc; } 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) { LSR_Desc *old_rsrc; PRCCK (LSR_desc_get (rsrc->id, &old_rsrc)); /* * BEGIN txn */ void *txn; LSUP_store_begin (LSR_store, 0, &txn); // Remove all existing user graphs. if (old_rsrc) { // 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. PCHECK (LSUP_graph_remove_txn ( txn, old_rsrc->user_data[i], NULL, NULL, NULL, &ct), fail); log_debug ("Removed %lu triples from graph %s", ct, gr_uri->data); // Remove user graph metadata. PCHECK (LSUP_graph_remove_txn ( txn, old_rsrc->main_data, gr_uri, NULL, NULL, NULL), fail); PCHECK (LSUP_graph_remove_txn ( txn, old_rsrc->main_data, NULL, NULL, gr_uri, NULL), fail); } } LSUP_Graph *tmp_gr; // Add new triples. LSUP_rc rc = LSUP_NOACTION; for (size_t i = 0; rsrc->user_data[i] != NULL; i++) { tmp_gr = LSUP_graph_new ( LSR_store, LSUP_graph_uri (rsrc->user_data[i]), NULL); if (UNLIKELY (!tmp_gr)) { rc = LSUP_MEM_ERR; goto fail; } PCHECK (LSUP_graph_copy_contents_txn ( txn, rsrc->user_data[i], tmp_gr), fail); LSUP_graph_free (tmp_gr); } // Update admin data. tmp_gr = LSUP_graph_new ( LSR_store, LSUP_graph_uri (rsrc->admin_data), NULL); if (UNLIKELY (!tmp_gr)) { rc = LSUP_MEM_ERR; goto fail; } PCHECK (LSUP_graph_copy_contents_txn ( txn, rsrc->admin_data, tmp_gr), fail); LSUP_graph_free (tmp_gr); // Update graph metadata. tmp_gr = LSUP_graph_new ( LSR_store, LSUP_graph_uri (rsrc->main_data), NULL); if (UNLIKELY (!tmp_gr)) { rc = LSUP_MEM_ERR; goto fail; } PCHECK (LSUP_graph_copy_contents_txn ( txn, rsrc->main_data, tmp_gr), fail); LSUP_graph_free (tmp_gr); PCHECK (LSUP_store_commit (LSR_store, txn), fail); /* * END txn */ return LSUP_OK; fail: LSUP_store_abort (LSR_store, txn); return rc; } LSUP_rc LSUP_desc_update (LSR_id id, LSUP_Term **remove, LSUP_Triple *add) { return LSUP_OK; } 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 (LSR_store, LSUP_default_ctx, NULL); if (!main_gr) return LSUP_DB_ERR; LSUP_Triple *spo = LSUP_triple_new ( LSR_id_to_urn (id, NULL), LSUP_iriref_new ("rdf:type", LSUP_default_nsm), LSUP_iriref_new ("lsup:Resource", LSUP_default_nsm) ); if (!LSUP_graph_contains (main_gr, spo)) { rc = LSUP_NORESULT; goto finally; } LSUP_term_free (spo->o); spo->o = LSUP_iriref_new ("lsup:DescriptiveResource", LSUP_default_nsm); if (!LSUP_graph_contains (main_gr, spo)) { log_error ("%s is not a descriptive resource.", spo->o->data); rc = LSUP_NORESULT; goto finally; } LSUP_term_free (spo->p); spo->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, spo->p, spo->s, &ct); LSUP_Graph **data = calloc (sizeof (*data), ct + 1); while (LSUP_graph_iter_next (it, &spo)) { data[i] = LSUP_graph_new (LSR_store, spo->s, NULL); 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_graph_free (main_gr); return rc; } LSUP_Graph * LSR_desc_metadata (const LSR_Desc *rsrc) { LSUP_Graph *res = LSUP_graph_new ( LSR_store, LSUP_graph_uri (rsrc->admin_data), NULL); LSUP_graph_copy_contents (rsrc->admin_data, res); return res; } LSUP_Graph ** LSR_desc_user_data (const LSR_Desc *rsrc) { size_t ct = 0; while (rsrc->user_data[ct++]); LSUP_Graph **res = malloc (sizeof (*res) * (ct + 1)); if (UNLIKELY (!res)) return NULL; for (size_t i = 0; i < ct; i++) { res[i] = LSUP_graph_new ( LSR_store, LSUP_graph_uri (rsrc->user_data[i]), NULL); LSUP_graph_copy_contents (rsrc->user_data[i], res[i]); } res[ct] = NULL; return res; } LSUP_rc LSR_desc_update ( LSR_Desc *rsrc, LSUP_Graph *const *rm_data, LSUP_Graph *const *add_data) { LSUP_rc rc = LSUP_NOACTION; LSUP_Term *rsrc_uri = LSR_id_to_urn (rsrc->id, NULL); size_t ct; /* * REMOVE user data. */ if (rm_data) { // Count graphs to be removed. ct = 0; while (add_data[ct]) ct++; for (size_t i = 0; i < ct; i++) { LSUP_Term *gr_uri = LSUP_graph_uri (rm_data[i]); // TODO remove ops. // TODO if graph is empty after removal, remove it. LSUP_term_free (gr_uri); } } /* * ADD user data. */ // Count graphs inserted and allocate space. ct = 0; while (add_data[ct]) ct++; rsrc->user_data = calloc (sizeof (*rsrc->user_data), ct + 1); if (UNLIKELY (! rsrc->user_data)) return LSUP_MEM_ERR; LSUP_Triple spo_s; LSUP_Triple *spo = &spo_s; LSUP_GraphIterator *lu_it, // Lookup iterator. *add_it; // Main graph add iterator. // Loop over input graphs. for (size_t i = 0; i < ct; i++) { LSUP_Term *gr_uri = LSUP_graph_uri (add_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 (NULL, gr_uri, NULL); log_debug ( "User data graph (@%p): %s", LSUP_graph_uri(rsrc->user_data[i]), LSUP_graph_uri(rsrc->user_data[i])->data); 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. LSUP_Term *dest_s, *dest_p, *dest_o; LSUP_Triple *src_spo; 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 (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, 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_NS), 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); rc = LSUP_VALUE_ERR; goto finally; } } // Check object. if (LSR_IS_RSRC_IRI (dest_o)) { uuid_parse (dest_o->data + strlen (LSR_RSRC_NS), 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_NS)); rc = LSUP_VALUE_ERR; goto finally; } } // RDF type check. if ( LSUP_term_equals ( gr_uri, LSUP_iriref_absolute (rsrc_uri, spo->s)) && LSUP_term_equals (LSR_rdf_t, spo->p) ) { // If the resource is a special type, handle specific workflow. if (hashmap_get (LSR_managed_types, spo->o)) { if ( strcmp (spo->o->data, "lsup:List") == 0 || strcmp (spo->o->data, "lsup:ListItem") == 0 || strcmp (spo->o->data, "lsup:Set") == 0 || strcmp (spo->o->data, "lsup:Proxy") == 0) { // TODO } else { log_error ( "%s is a managed predicate and cannot " "be used on creation.", spo->o->data); rc = LSUP_VALUE_ERR; goto finally; } } } // Add triple to user_data. LSUP_graph_add_iter (add_it, 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); } /* * UPDATE graph metadata. */ // Add user graph metadata to default graph. LSUP_GraphIterator *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 (spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, spo); LSUP_term_free (dest_o); dest_o = LSUP_iriref_new ("lsup:UserMetadata", LSUP_default_nsm); LSUP_triple_init (spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, 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 (spo, dest_s, dest_p, dest_o); LSUP_graph_add_iter (admin_add_it, 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; } finally: LSUP_term_free (rsrc_uri); if (lu_it) LSUP_graph_iter_free (lu_it); if (add_it) LSUP_graph_add_done (add_it); return rc; }