/* * Copyright (C) 2009, Nokia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include #include #include "tracker-docgen-xml.h" #include "tracker-utils.h" typedef struct { TrackerOntologyModel *model; TrackerOntologyDescription *description; FILE *output; } CallbackInfo; static void print_predefined_instances (FILE *f, TrackerOntologyClass *klass, TrackerOntologyModel *model) { const gchar *id; GList *l; if (!klass->instances) return; id = klass->shortname; g_fprintf (f, "", id); g_fprintf (f, "Predefined instances"); g_fprintf (f, "%s has the following predefined instances: ", klass->shortname); g_fprintf (f, "\n"); for (l = klass->instances; l; l = l->next) { g_fprintf (f, ""); g_fprintf (f, "%s", (gchar*) l->data); g_fprintf (f, "\n"); } g_fprintf (f, "\n"); } static void print_class_hierarchy (FILE *f, TrackerOntologyClass *klass, TrackerOntologyModel *model) { GPtrArray *strings; const gchar *id; guint i; strings = class_get_parent_hierarchy_strings (klass, model); if (!strings) return; id = klass->shortname; g_fprintf (f, "", id); g_fprintf (f, "Class hierarchy"); g_fprintf (f, ""); for (i = 0; i < strings->len; i++) { HierarchyString *str = g_ptr_array_index (strings, i); if (str->class == klass) { g_fprintf (f, " %s%s%s\n", str->before->str, str->class->shortname, str->link_label->str, str->after->str); } else { g_fprintf (f, " %s%s%s\n", str->before->str, str->class->shortname, str->link_label->str, str->after->str); } } g_fprintf (f, "\n"); g_ptr_array_unref (strings); } static void print_flag (FILE *f, const gchar *flag_property_link, const gchar *icon_name, const gchar *flag_description) { /* This must not contain any linebreaks, or gtkdoc-fixxrefs will not * resolve the link. See https://gitlab.gnome.org/GNOME/gtk-doc/-/issues/122 */ g_fprintf (f, "", flag_property_link); g_fprintf (f, ""); g_fprintf (f, "", icon_name); g_fprintf (f, "%s", flag_description); g_fprintf (f, ""); g_fprintf (f, ""); } static void print_property_table (FILE *f, TrackerOntologyModel *model, const char *id, GList *properties) { GList *l, *m; g_autoptr(GList) properties_sorted = NULL; if (!properties) return; properties_sorted = g_list_sort (g_list_copy (properties), (GCompareFunc) strcmp); /* We (ab)use the "struct_members" role to ensure devhelp2 entries are * generated by gtkdoc-mkhtml2. This is needed for xrefs to work between the * libtracker-sparql and nepomuk ontology docs. */ g_fprintf (f, "", id); g_fprintf (f, "Properties"); g_fprintf (f, ""); g_fprintf (f, "NameTypeNotesDescription"); g_fprintf (f, ""); for (l = properties_sorted; l; l = l->next) { TrackerOntologyProperty *prop; TrackerOntologyClass *range; const gchar *shortname = NULL, *basename = NULL, *type_name = NULL, *type_class_id = NULL, *prop_id = NULL; prop = tracker_ontology_model_get_property (model, l->data); range = tracker_ontology_model_get_class (model, prop->range->data); prop_id = shortname = prop->shortname; basename = prop->basename; type_name = range->basename; type_class_id = range->shortname; g_fprintf (f, ""); /* Property name column */ g_fprintf (f, ""); /* This id is globally unique and can be used for internal links. * We abuse so that gtkdoc-mkhtml2 creates a usable link. */ g_fprintf (f, "%s", prop_id, basename); /* This anchor is unique within the refentry and can be used for external links */ g_fprintf (f, "", basename); g_fprintf (f, "%s", prop_id, shortname, shortname); g_fprintf (f, ""); /* Type column */ g_fprintf (f, ""); g_fprintf (f, "%s", type_class_id, type_name); g_fprintf (f, ""); /* Flags column */ g_fprintf (f, ""); if (prop->deprecated) { print_flag (f, "nrl-deprecated", "icon-deprecated.svg", "This property is deprecated."); } if (prop->superproperties) { for (m = prop->superproperties; m; m = m->next) { const gchar *shortname = NULL, *superprop_id = NULL; g_autofree gchar *message = NULL; TrackerOntologyProperty *superprop; superprop = tracker_ontology_model_get_property (model, m->data); shortname = superprop_id = superprop->shortname; message = g_strdup_printf ("This property extends %s", shortname); print_flag (f, superprop_id, "icon-superproperty.svg", message); } } if (prop->max_cardinality != NULL && atoi (prop->max_cardinality) == 1) { /* Single valued properties are most common, so we don't display this. */ } else { g_autofree gchar *message = NULL; if (prop->max_cardinality != NULL && atoi (prop->max_cardinality) > 0) { message = g_strdup_printf ("This property can have a maximum of %i values", *prop->max_cardinality); } else { message = g_strdup_printf ("This property can have multiple values."); } print_flag (f, "nrl-maxCardinality", "icon-multivalue.svg", message); } if (prop->fulltextIndexed) { print_flag (f, "nrl-fulltextIndexed", "icon-fulltextindexed.svg", "This property is full-text-indexed, and can be looked up through fts:match"); } g_fprintf (f, ""); /* Description column */ g_fprintf (f, ""); if (prop->description) { g_fprintf (f, "%s", prop->description); } g_fprintf (f, ""); g_fprintf (f, ""); } g_fprintf (f, ""); g_fprintf (f, ""); g_fprintf (f, ""); } static void print_ontology_class (TrackerOntologyModel *model, TrackerOntologyClass *klass, FILE *f) { const gchar *name = NULL, *id = NULL; g_return_if_fail (f != NULL); name = klass->basename; id = klass->shortname; /* Anchor for external links. */ g_fprintf (f, "\n", name); g_fprintf (f, "\n", id); g_fprintf (f, "%s\n", name); if (klass->description || klass->deprecated || klass->notify) { g_fprintf (f, "\n", id); g_fprintf (f, " Description\n"); if (klass->description) { g_fprintf (f, " %s", klass->description); } if (klass->deprecated) { g_fprintf (f, ""); print_flag (f, "nrl-deprecated", "icon-deprecated.svg", "Deprecated icon"); g_fprintf (f, "This class is deprecated."); g_fprintf (f, ""); } if (klass->notify) { g_fprintf (f, ""); print_flag (f, "nrl-notify", "icon-notify.svg", "Notify icon"); g_fprintf (f, "This class emits notifications about changes, and can " "be monitored using TrackerNotifier."); g_fprintf (f, ""); } g_fprintf (f, "\n"); } if (klass->specification) { g_fprintf (f, "\n", id); g_fprintf (f, " Specification\n"); g_fprintf (f, " ", klass->specification); g_fprintf (f, "\n"); } print_class_hierarchy (f, klass, model); print_predefined_instances (f, klass, model); print_property_table (f, model, id, klass->in_domain_of); g_fprintf (f, "\n"); } static void print_ontology_extra_properties (TrackerOntologyModel *model, const char *ontology_prefix, const char *classname, GList *properties_for_class, FILE *f) { TrackerOntologyClass *klass; const gchar *short_classname = NULL, *class_id = NULL; gchar *section_id = NULL; g_return_if_fail (f != NULL); klass = tracker_ontology_model_get_class (model, classname); short_classname = class_id = klass->shortname; section_id = g_strconcat (ontology_prefix, ".", class_id, NULL); g_fprintf (f, "\n", section_id); g_fprintf (f, "Additional properties for %s\n", short_classname); g_fprintf (f, "\n"); g_fprintf (f, " Description\n"); g_fprintf (f, " Properties this ontology defines which can describe %s resources.", short_classname); g_fprintf (f, "\n"); print_property_table (f, model, section_id, properties_for_class); g_fprintf (f, "\n"); g_free (section_id); } static void print_itemized_list (FILE *f, GList *list) { GList *it; g_fprintf (f, "\n"); for (it = list; it != NULL; it = it->next) { g_fprintf (f, "%s\n", (gchar *)it->data); } g_fprintf (f, "\n"); } static void print_people_list (FILE *f, const gchar *role, GList *list) { if (!list) { return; } g_fprintf (f, "\n"); g_fprintf (f, " %s\n", role); g_fprintf (f, " \n"); print_itemized_list (f, list); g_fprintf (f, " \n"); g_fprintf (f, "\n"); } static void print_link_as_varlistentry (FILE *f, const gchar *term, const gchar *link_text, const gchar *link) { g_fprintf (f, " \n"); g_fprintf (f," %s\n", term); if (link) { g_fprintf (f, " %s\n", link, link_text); } else { g_fprintf (f, " Not available\n"); } g_fprintf (f, " \n"); } #if 0 static void print_deprecated_message (FILE *f) { g_fprintf (f, "\n"); g_fprintf (f, "Note:\n"); g_fprintf (f, "This item is deprecated\n"); g_fprintf (f, "\n"); } #endif static void print_xml_header (FILE *f, TrackerOntologyDescription *desc) { g_fprintf (f, "\n"); g_fprintf (f, "\n"); g_fprintf (f, "]>"); g_fprintf (f, "\n", desc->localPrefix); g_fprintf (f, "\n"); g_fprintf (f, " %s\n", desc->title); g_fprintf (f, "\n"); g_fprintf (f, "\n"); g_fprintf (f, "%s", desc->title); g_fprintf (f, "%s", desc->description); g_fprintf (f, "\n"); } static void print_xml_footer (FILE *f, TrackerOntologyDescription *desc) { g_fprintf (f, "\n"); g_fprintf (f, "Credits and Copyright\n"); print_people_list (f, "Authors:", desc->authors); print_people_list (f, "Editors:", desc->editors); print_people_list (f, "Contributors:", desc->contributors); print_link_as_varlistentry (f, "Upstream:", "Upstream version", desc->upstream); print_link_as_varlistentry (f, "ChangeLog:", "Tracker changes", desc->gitlog); if (desc->copyright) { g_fprintf (f, "\n"); g_fprintf (f, " Copyright:\n"); g_fprintf (f, " \n"); g_fprintf (f, "%s\n", desc->copyright); g_fprintf (f, " \n"); g_fprintf (f, "\n"); } g_fprintf (f, "\n"); g_fprintf (f, "\n"); } /* By default we list properties under their respective class. * * Ontologies can contain properties whose class is in a different * ontology, and we treat these specially as 'extra properties'. * * This functions returns a hash table mapping class name to the * extra properties provided for that class. */ static GHashTable * get_extra_properties (TrackerOntologyModel *model, GList *classes, GList *properties) { GList *l, *c; GHashTable *extra_properties; GHashTableIter iter; gchar *classname; GList *properties_for_class; extra_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (l = properties; l; l = l->next) { TrackerOntologyProperty *prop; gboolean has_domain_in_this_ontology = FALSE; prop = tracker_ontology_model_get_property (model, l->data); for (c = prop->domain; c; c = c->next) { TrackerOntologyDescription *desc = NULL; TrackerOntologyClass *klass; gchar *prefix; const gchar *sep; klass = tracker_ontology_model_get_class (model, c->data); sep = strstr (klass->shortname, ":"); if (sep) { prefix = g_strndup (klass->shortname, sep - klass->shortname); desc = tracker_ontology_model_get_description (model, prefix); g_free (prefix); } has_domain_in_this_ontology = desc != NULL; } if (!has_domain_in_this_ontology) { for (c = prop->domain; c; c = c->next) { const gchar *classname; GList *list; classname = c->data; list = g_hash_table_lookup (extra_properties, classname); list = g_list_append (list, prop->propertyname); g_hash_table_insert (extra_properties, g_strdup (classname), list); } } } g_hash_table_iter_init (&iter, extra_properties); while (g_hash_table_iter_next (&iter, (gpointer *)&classname, (gpointer *)&properties_for_class)) { properties_for_class = g_list_sort (properties_for_class, (GCompareFunc) strcmp); g_hash_table_iter_replace (&iter, properties_for_class); } return extra_properties; } static void print_synopsis (FILE *f, TrackerOntologyDescription *desc) { g_fprintf (f, "\n"); g_fprintf (f, "\n"); g_fprintf (f, "@prefix %s: <%s>\n", desc->localPrefix, desc->baseUrl); g_fprintf (f, "\n"); g_fprintf (f, "\n"); } static void print_toc_classes (FILE *f, TrackerOntologyModel *model, const char *id, GList *classes) { GList *l; if (!classes) return; g_fprintf (f, "", id); g_fprintf (f, "Classes"); for (l = classes; l; l = l->next) { TrackerOntologyClass *klass; const char *basename = NULL, *id = NULL; klass = tracker_ontology_model_get_class (model, l->data); basename = klass->basename; id = klass->shortname; if (l != classes) { g_fprintf (f, ", "); } g_fprintf (f, "%s", id, basename); } g_fprintf (f, ""); } static void print_toc_extra_properties (FILE *f, TrackerOntologyModel *model, const char *id, GHashTable *extra_properties) { GList *props_for_class, *c, *l; g_autoptr(GList) classes = NULL; gboolean print_comma = FALSE; if (g_hash_table_size (extra_properties) == 0) return; g_fprintf (f, "", id); g_fprintf (f, "Additional Properties"); classes = g_hash_table_get_keys (extra_properties); classes = g_list_sort (classes, (GCompareFunc)strcmp); for (c = classes; c; c = c->next) { gchar *classname; classname = c->data; props_for_class = g_hash_table_lookup (extra_properties, classname); for (l = props_for_class; l; l = l->next) { TrackerOntologyProperty *prop; const char *basename = NULL, *prop_id = NULL; prop = tracker_ontology_model_get_property (model, l->data); basename = prop->basename; prop_id = prop->shortname; if (print_comma) { g_fprintf (f, ", "); } else { print_comma = TRUE; } g_fprintf (f, "%s", prop_id, basename); } } g_fprintf (f, ""); } /* Generate docbook XML document for one ontology. */ void ttl_xml_print (TrackerOntologyDescription *description, TrackerOntologyModel *model, const gchar *prefix, GFile *output_location, const gchar *description_dir) { gchar *upper_name, *path, *introduction, *basename, *filename; g_autoptr(GList) classes = NULL, properties = NULL, extra_classes = NULL; g_autoptr(GHashTable) extra_properties = NULL; GFile *file; GList *l; FILE *f; filename = g_strdup_printf ("%s-ontology.xml", description->localPrefix); file = g_file_get_child (output_location, filename); g_free (filename); path = g_file_get_path (file); f = fopen (path, "w"); g_assert (f != NULL); g_free (path); upper_name = g_ascii_strup (description->localPrefix, -1); classes = tracker_ontology_model_list_classes (model, prefix); properties = tracker_ontology_model_list_properties (model, prefix); extra_properties = get_extra_properties (model, classes, properties); print_xml_header (f, description); print_synopsis (f, description); print_toc_classes (f, model, description->localPrefix, classes); print_toc_extra_properties (f, model, description->localPrefix, extra_properties); basename = g_strdup_printf ("%s-introduction.xml", description->localPrefix); introduction = g_build_filename (description_dir, basename, NULL); g_free (basename); if (g_file_test (introduction, G_FILE_TEST_EXISTS)) { g_fprintf (f, "", introduction); } if (classes != NULL) { g_fprintf (f, "\n", description->localPrefix); g_fprintf (f, "Class Details\n"); for (l = classes; l; l = l->next) { TrackerOntologyClass *klass; klass = tracker_ontology_model_get_class (model, l->data); print_ontology_class (model, klass, f); } g_fprintf (f, "\n"); } if (g_hash_table_size (extra_properties) > 0) { g_fprintf (f, "\n", description->localPrefix); g_fprintf (f, "Property Details\n"); extra_classes = g_hash_table_get_keys (extra_properties); extra_classes = g_list_sort (extra_classes, (GCompareFunc)strcmp); for (l = extra_classes; l; l = l->next) { gchar *classname; GList *properties_for_class; classname = l->data; properties_for_class = g_hash_table_lookup (extra_properties, classname); if (properties_for_class) { print_ontology_extra_properties (model, description->localPrefix, classname, properties_for_class, f); } } g_fprintf (f, "\n"); } print_xml_footer (f, description); g_free (upper_name); g_free (introduction); g_object_unref (file); fclose (f); }