summaryrefslogtreecommitdiff
path: root/docs/tools/tracker-docgen-xml.c
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tools/tracker-docgen-xml.c')
-rw-r--r--docs/tools/tracker-docgen-xml.c649
1 files changed, 649 insertions, 0 deletions
diff --git a/docs/tools/tracker-docgen-xml.c b/docs/tools/tracker-docgen-xml.c
new file mode 100644
index 000000000..c95ca89e7
--- /dev/null
+++ b/docs/tools/tracker-docgen-xml.c
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
+ *
+ * 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 <glib/gprintf.h>
+#include <gio/gio.h>
+
+#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, "<refsect3 id='%s.predefined-instances'>", id);
+ g_fprintf (f, "<title>Predefined instances</title><para>");
+ g_fprintf (f, "%s has the following predefined instances: ", klass->shortname);
+ g_fprintf (f, "<itemizedlist>\n");
+
+ for (l = klass->instances; l; l = l->next) {
+ g_fprintf (f, "<listitem><para>");
+ g_fprintf (f, "<literal>%s</literal>", (gchar*) l->data);
+ g_fprintf (f, "</para></listitem>\n");
+ }
+
+ g_fprintf (f, "</itemizedlist></para></refsect3>\n");
+}
+
+static void
+print_class_hierarchy (FILE *f,
+ TrackerOntologyClass *klass,
+ TrackerOntologyModel *model)
+{
+ GPtrArray *strings;
+ const gchar *id;
+ gint i;
+
+ strings = class_get_parent_hierarchy_strings (klass, model);
+
+ if (!strings)
+ return;
+
+ id = klass->shortname;
+
+ g_fprintf (f, "<refsect3 id='%s.hierarchy'>", id);
+ g_fprintf (f, "<title>Class hierarchy</title>");
+ g_fprintf (f, "<screen>");
+
+ for (i = 0; i < strings->len; i++) {
+ HierarchyString *str = g_ptr_array_index (strings, i);
+ g_fprintf (f, " %s\n", str->str->str);
+ }
+
+ g_fprintf (f, "</screen></refsect3>\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, "<link linkend=\"%s\">", flag_property_link);
+ g_fprintf (f, "<inlinemediaobject>");
+ g_fprintf (f, "<imageobject><imagedata fileref=\"%s\" /></imageobject>", icon_name);
+ g_fprintf (f, "<alt>%s</alt>", flag_description);
+ g_fprintf (f, "</inlinemediaobject>");
+ g_fprintf (f, "</link>");
+}
+
+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 <keyword> entries are
+ * generated by gtkdoc-mkhtml2. This is needed for xrefs to work between the
+ * libtracker-sparql and nepomuk ontology docs.
+ */
+ g_fprintf (f, "<refsect3 role=\"struct_members\" id=\"%s.properties\">", id);
+ g_fprintf (f, "<title>Properties</title>");
+
+ g_fprintf (f, "<informaltable frame=\"none\"><tgroup cols=\"4\">");
+ g_fprintf (f, "<thead><row><entry>Name</entry><entry>Type</entry><entry>Notes</entry><entry>Description</entry></row></thead>");
+
+ g_fprintf (f, "<tbody>");
+ 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, "<row role=\"member\">");
+
+ /* Property name column */
+ g_fprintf (f, "<entry role=\"struct_member_name\">");
+ /* This id is globally unique and can be used for internal links.
+ * We abuse <structfield> so that gtkdoc-mkhtml2 creates a usable link. */
+ g_fprintf (f, "<para><structfield id=\"%s\">%s</structfield></para>", prop_id, basename);
+ /* This anchor is unique within the refentry and can be used for external links */
+ g_fprintf (f, "<anchor id='%s' />", basename);
+ g_fprintf (f, "<indexterm zone='%s'><primary sortas='%s'>%s</primary></indexterm>",
+ prop_id, shortname, shortname);
+ g_fprintf (f, "</entry>");
+
+ /* Type column */
+ g_fprintf (f, "<entry>");
+ g_fprintf (f, "<link linkend=\"%s\">%s</link>", type_class_id, type_name);
+ g_fprintf (f, "</entry>");
+
+ /* Flags column */
+ g_fprintf (f, "<entry>");
+
+ 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 <literal>fts:match</literal>");
+ }
+
+ g_fprintf (f, "</entry>");
+
+ /* Description column */
+ g_fprintf (f, "<entry>");
+ if (prop->description) {
+ g_fprintf (f, "<para>%s</para>", prop->description);
+ }
+ g_fprintf (f, "</entry>");
+ g_fprintf (f, "</row>");
+ }
+
+ g_fprintf (f, "</tbody></tgroup>");
+ g_fprintf (f, "</informaltable>");
+
+ g_fprintf (f, "</refsect3>");
+}
+
+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, "<anchor id='%s' />\n", name);
+
+ g_fprintf (f, "<refsect2 role='rdf-class' id='%s'>\n", id);
+ g_fprintf (f, "<title>%s</title>\n", name);
+
+ if (klass->description || klass->deprecated || klass->notify) {
+ g_fprintf (f, "<refsect3 id='%s.description'>\n", id);
+ g_fprintf (f, " <title>Description</title>\n");
+
+ if (klass->description) {
+ g_fprintf (f, " %s", klass->description);
+ }
+
+ if (klass->deprecated) {
+ g_fprintf (f, "<para>");
+ print_flag (f, "nrl-deprecated", "icon-deprecated.svg", "Deprecated icon");
+ g_fprintf (f, "This class is deprecated.");
+ g_fprintf (f, "</para>");
+ }
+
+ if (klass->notify) {
+ g_fprintf (f, "<para>");
+ print_flag (f, "nrl-notify", "icon-notify.svg", "Notify icon");
+ g_fprintf (f, "This class emits notifications about changes, and can "
+ "be monitored using <link linkend=\"TrackerNotifier\">TrackerNotifier</link>.");
+ g_fprintf (f, "</para>");
+ }
+ g_fprintf (f, "</refsect3>\n");
+ }
+
+ if (klass->specification) {
+ g_fprintf (f, "<refsect3 id='%s.specification'>\n", id);
+ g_fprintf (f, " <title>Specification</title>\n");
+ g_fprintf (f, " <ulink url=\"%s\" />", klass->specification);
+ g_fprintf (f, "</refsect3>\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, "</refsect2>\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, "<refsect2 role='rdf-property-list' id='%s'>\n", section_id);
+ g_fprintf (f, "<title>Additional properties for %s</title>\n", short_classname);
+
+ g_fprintf (f, "<refsect3>\n");
+ g_fprintf (f, " <title>Description</title>\n");
+ g_fprintf (f, " <para>Properties this ontology defines which can describe %s resources.</para>",
+ short_classname);
+ g_fprintf (f, "</refsect3>\n");
+
+ print_property_table (f, model, section_id, properties_for_class);
+ g_fprintf (f, "</refsect2>\n");
+
+ g_free (section_id);
+}
+
+static void
+print_itemized_list (FILE *f, GList *list)
+{
+ GList *it;
+
+ g_fprintf (f, "<itemizedlist>\n");
+ for (it = list; it != NULL; it = it->next) {
+ g_fprintf (f, "<listitem>%s</listitem>\n", (gchar *)it->data);
+ }
+ g_fprintf (f, "</itemizedlist>\n");
+}
+
+static void
+print_people_list (FILE *f,
+ const gchar *role,
+ GList *list)
+{
+ if (!list) {
+ return;
+ }
+
+ g_fprintf (f, "<varlistentry>\n");
+ g_fprintf (f, " <term>%s</term>\n", role);
+ g_fprintf (f, " <listitem>\n");
+ print_itemized_list (f, list);
+ g_fprintf (f, " </listitem>\n");
+ g_fprintf (f, "</varlistentry>\n");
+}
+
+static void
+print_link_as_varlistentry (FILE *f,
+ const gchar *term,
+ const gchar *link_text,
+ const gchar *link)
+{
+ g_fprintf (f, " <varlistentry>\n");
+ g_fprintf (f," <term>%s</term>\n", term);
+ if (link) {
+ g_fprintf (f,
+ " <listitem><para><ulink url=\"%s\">%s</ulink></para></listitem>\n",
+ link, link_text);
+ } else {
+ g_fprintf (f, " <listitem><para>Not available</para></listitem>\n");
+ }
+ g_fprintf (f, " </varlistentry>\n");
+}
+
+#if 0
+static void
+print_deprecated_message (FILE *f)
+{
+ g_fprintf (f, "<note>\n");
+ g_fprintf (f, "<title>Note:</title>\n");
+ g_fprintf (f, "<para>This item is deprecated</para>\n");
+ g_fprintf (f, "</note>\n");
+}
+#endif
+
+static void
+print_xml_header (FILE *f, TrackerOntologyDescription *desc)
+{
+ g_fprintf (f, "<?xml version='1.0' encoding='UTF-8'?>\n");
+ g_fprintf (f, "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook XML V4.5//EN\"\n"
+ " \"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd\" [\n");
+ g_fprintf (f, "<!ENTITY %% local.common.attrib \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'\">\n");
+ g_fprintf (f, "]>");
+
+ g_fprintf (f, "<refentry id='%s' xmlns:xi=\"http://www.w3.org/2003/XInclude\">\n", desc->localPrefix);
+ g_fprintf (f, "<refmeta>\n");
+ g_fprintf (f, " <refentrytitle>%s</refentrytitle>\n", desc->title);
+ g_fprintf (f, "</refmeta>\n");
+ g_fprintf (f, "<refnamediv>\n");
+ g_fprintf (f, "<refname>%s</refname>", desc->title);
+ g_fprintf (f, "<refpurpose>%s</refpurpose>", desc->description);
+ g_fprintf (f, "</refnamediv>\n");
+}
+
+static void
+print_xml_footer (FILE *f, TrackerOntologyDescription *desc)
+{
+ g_fprintf (f, "<refsect1>\n");
+ g_fprintf (f, "<title>Credits and Copyright</title>\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, "<varlistentry>\n");
+ g_fprintf (f, " <term>Copyright:</term>\n");
+ g_fprintf (f, " <listitem>\n");
+ g_fprintf (f, "<para>%s</para>\n", desc->copyright);
+ g_fprintf (f, " </listitem>\n");
+ g_fprintf (f, "</varlistentry>\n");
+ }
+
+ g_fprintf (f, "</refsect1>\n");
+ g_fprintf (f, "</refentry>\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, "<refsynopsisdiv>\n");
+ g_fprintf (f, "<synopsis>\n");
+ g_fprintf (f, "@prefix %s: &lt;%s&gt;\n", desc->localPrefix, desc->baseUrl);
+ g_fprintf (f, "</synopsis>\n");
+ g_fprintf (f, "</refsynopsisdiv>\n");
+}
+
+static void
+print_toc_classes (FILE *f,
+ TrackerOntologyModel *model,
+ const char *id,
+ GList *classes)
+{
+ GList *l;
+
+ if (!classes)
+ return;
+
+ g_fprintf (f, "<refsect1 id=\"%s.classes\">", id);
+ g_fprintf (f, "<title>Classes</title>");
+
+ 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, "<link linkend=\"%s\">%s</link>", id, basename);
+ }
+
+ g_fprintf (f, "</refsect1>");
+}
+
+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, "<refsect1 id=\"%s.extra_properties\">", id);
+ g_fprintf (f, "<title>Additional Properties</title>");
+
+ 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, "<link linkend=\"%s\">%s</link>", prop_id, basename);
+ }
+ }
+
+ g_fprintf (f, "</refsect1>");
+}
+
+/* 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, "<xi:include href='%s'><xi:fallback/></xi:include>",
+ introduction);
+ }
+
+ if (classes != NULL) {
+ g_fprintf (f, "<refsect1 id='%s-classes'>\n", description->localPrefix);
+ g_fprintf (f, "<title>Class Details</title>\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, "</refsect1>\n");
+ }
+
+ if (g_hash_table_size (extra_properties) > 0) {
+ g_fprintf (f, "<refsect1 id='%s-extra-properties'>\n", description->localPrefix);
+ g_fprintf (f, "<title>Property Details</title>\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, "</refsect1>\n");
+ }
+
+ print_xml_footer (f, description);
+
+ g_free (upper_name);
+ g_free (introduction);
+ g_object_unref (file);
+ fclose (f);
+}