ttl_loader.c 14 KB
Newer Older
1
/*
2
 * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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.
 */

Ivan Frade's avatar
Ivan Frade committed
20 21
#include "ttl_loader.h"
#include <glib/gstdio.h>
22
#include <gio/gio.h>
Ivan Frade's avatar
Ivan Frade committed
23

24
#include <libtracker-data/tracker-sparql-query.h>
Ivan Frade's avatar
Ivan Frade committed
25 26 27 28 29 30 31 32 33

/* Ontology classes */
#define RDFS_CLASS "http://www.w3.org/2000/01/rdf-schema#Class"
#define RDF_PROPERTY "http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"
#define RDFS_SUBCLASSOF  "http://www.w3.org/2000/01/rdf-schema#subClassOf"
#define RDFS_TYPE "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
#define RDFS_RANGE "http://www.w3.org/2000/01/rdf-schema#range"
#define RDFS_DOMAIN "http://www.w3.org/2000/01/rdf-schema#domain"
#define RDFS_COMMENT "http://www.w3.org/2000/01/rdf-schema#comment"
34
#define RDFS_LABEL "http://www.w3.org/2000/01/rdf-schema#label"
Ivan Frade's avatar
Ivan Frade committed
35 36 37 38
#define RDFS_SUBPROPERTYOF "http://www.w3.org/2000/01/rdf-schema#subPropertyOf"

#define NRL_MAX_CARDINALITY "http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#maxCardinality"

39
/* #define TRACKER_NAMESPACE "http://www.tracker-project.org/ontologies/tracker#Namespace" */
40 41
#define TRACKER_NS "http://www.tracker-project.org/ontologies/tracker#"
#define TRACKER_NOTIFY TRACKER_NS "notify"
42
#define TRACKER_FTS_INDEXED TRACKER_NS "fulltextIndexed"
43
#define TRACKER_FTS_WEIGHT TRACKER_NS "weight"
44
#define TRACKER_PREFIX TRACKER_NS "prefix"
Ivan Frade's avatar
Ivan Frade committed
45

46 47
#define NAO_DEPRECATED "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#deprecated"

Ivan Frade's avatar
Ivan Frade committed
48 49 50 51 52
/* Ontology description */
#define DSC_PREFIX "http://www.tracker-project.org/temp/dsc#"

#define DSC_ONTOLOGY DSC_PREFIX "Ontology"
#define DSC_TITLE DSC_PREFIX "title"
53
#define DSC_DESCRIPTION DSC_PREFIX "description"
Ivan Frade's avatar
Ivan Frade committed
54 55 56
#define DSC_AUTHOR DSC_PREFIX "author"
#define DSC_EDITOR DSC_PREFIX "editor"
#define DSC_CONTRIBUTOR DSC_PREFIX "contributor"
57
#define DSC_GITLOG DSC_PREFIX "gitlog"
58
#define DSC_UPSTREAM DSC_PREFIX "upstream"
Ivan Frade's avatar
Ivan Frade committed
59 60
#define DSC_BASEURI DSC_PREFIX "baseUrl"
#define DSC_RELPATH DSC_PREFIX "relativePath"
61
#define DSC_LOCALPREFIX DSC_PREFIX "localPrefix"
62
#define DSC_COPYRIGHT DSC_PREFIX "copyright"
Ivan Frade's avatar
Ivan Frade committed
63

64
static gboolean
65
string_to_boolean (const gchar *str) {
66 67 68 69 70 71 72
	if (!g_strcmp0 (str, "true")) {
		return TRUE;
	} else if (!g_strcmp0 (str, "false")) {
		return FALSE;
	} else {
		g_error ("Unable to map '%s' into boolean", str);
	}
73 74 75
}


Ivan Frade's avatar
Ivan Frade committed
76
static void
77 78 79 80
load_in_memory (Ontology    *ontology,
                const gchar *turtle_subject,
                const gchar *turtle_predicate,
                const gchar *turtle_object)
Ivan Frade's avatar
Ivan Frade committed
81
{
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	g_return_if_fail (ontology != NULL);

	if (!g_strcmp0 (turtle_predicate, RDFS_TYPE)) {
		/* It is a definition of class or property */
		if (!g_strcmp0 (turtle_object, RDFS_CLASS)) {
			g_hash_table_insert (ontology->classes,
			                     g_strdup (turtle_subject),
			                     ttl_model_class_new (turtle_subject));

		} else if (!g_strcmp0 (turtle_object, RDF_PROPERTY)) {
			g_hash_table_insert (ontology->properties,
			                     g_strdup (turtle_subject),
			                     ttl_model_property_new (turtle_subject));

		} else {
			/* xxx:a-default-instance a xxx:Class */
			OntologyClass *def;

			def = g_hash_table_lookup (ontology->classes, turtle_object);
			if (def) {
				def->instances = g_list_prepend (def->instances,
				                                 g_strdup (turtle_subject));
			}
			/* g_print ("FIXME Ignoring %s %s %s\n",
			   turtle_subject, turtle_predicate, turtle_object);
			*/
		}

	} else if (!g_strcmp0 (turtle_predicate, RDFS_SUBCLASSOF)) {
		/*
		 * A subclass of B:
		 *  - Add B in A->superclasses list
		 *  - Add A in B->subclasses list (if B is in this ontology!)
		 */
		OntologyClass *def;

		def = g_hash_table_lookup (ontology->classes, turtle_subject);
		if (!def) {
			g_error ("Something wrong");
		}

		def->superclasses = g_list_prepend (def->superclasses,
		                                    g_strdup (turtle_object));

		def = g_hash_table_lookup (ontology->classes, turtle_object);
		if (def) {
			def->subclasses = g_list_prepend (def->subclasses,
			                                  g_strdup (turtle_subject));
		}
	} else if (!g_strcmp0 (turtle_predicate, TRACKER_NOTIFY)) {
		/*
		 * A tracker:notify TRUE
		 */
		OntologyClass *def;

		def = g_hash_table_lookup (ontology->classes, turtle_subject);
		if (!def) {
			g_error ("Something wrong");
		}

		def->notify = string_to_boolean (turtle_object);

144 145 146 147 148 149 150 151 152 153 154 155 156
	} else if (!g_strcmp0 (turtle_predicate, TRACKER_FTS_INDEXED)) {
		/*
		 * A tracker:fulltextIndexed TRUE
		 */
		OntologyProperty *prop;

		prop = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (!prop) {
			g_error ("Something wrong");
		}

		prop->fulltextIndexed = string_to_boolean (turtle_object);

157 158 159 160 161 162 163 164 165 166 167 168 169
	} else if (!g_strcmp0 (turtle_predicate, TRACKER_FTS_WEIGHT)) {
		/*
		 * A tracker:weight X
		 */
		OntologyProperty *prop;

		prop = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (!prop) {
			g_error ("Something wrong");
		}

		prop->weight = g_strdup (turtle_object);

170 171 172 173 174 175
	} else if (!g_strcmp0 (turtle_predicate, TRACKER_PREFIX)) {
		/* A tracker:prefix on a tracker:Namespace */
		g_hash_table_insert (ontology->prefixes,
				     g_strdup (turtle_subject),
				     g_strdup (turtle_object));

176 177 178 179 180 181 182 183 184 185 186 187
	} else if (!g_strcmp0 (turtle_predicate, RDFS_COMMENT)) {
		OntologyClass *klass;
		OntologyProperty *prop;

		klass = g_hash_table_lookup (ontology->classes, turtle_subject);
		if (klass) {
			klass->description = g_strdup (turtle_object);
		} else {
			prop = g_hash_table_lookup (ontology->properties, turtle_subject);
			if (prop) {
				prop->description = g_strdup (turtle_object);
			} else {
188
				g_error ("Error in comment (%s doesn't exist)", turtle_subject);
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
			}
		}

	} else if (!g_strcmp0 (turtle_predicate, RDFS_DOMAIN)) {
		/*
		 * (prop A) has domain (class B)
		 *  -> add B in A->domain
		 *  -> add A in B->in_domain_of (if B is defined in this ontology!)
		 */
		OntologyProperty *prop;
		OntologyClass *klass;

		prop = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (!prop) {
			g_error ("Strange error in domain (%s doesnt exist!)",
			         turtle_subject);
		}
		prop->domain = g_list_prepend (prop->domain, g_strdup (turtle_object));

		klass = g_hash_table_lookup (ontology->classes, turtle_object);
		if (klass) {
			klass->in_domain_of = g_list_prepend (klass->in_domain_of,
			                                      g_strdup (turtle_subject));
		}

	} else if (!g_strcmp0 (turtle_predicate, RDFS_RANGE)) {
		/*
		 * (prop A) has range (class B)
		 *  -> add B in A->range
		 *  -> add A in B->in_range_of (if B is defined in this ontology!)
		 */
		OntologyProperty *prop;
		OntologyClass *klass;

		prop = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (!prop) {
			g_error ("Strange error in domain (%s doesnt exist!)",
			         turtle_subject);
		}
		prop->range = g_list_prepend (prop->range, g_strdup (turtle_object));

		klass = g_hash_table_lookup (ontology->classes, turtle_object);
		if (klass) {
			klass->in_range_of = g_list_prepend (klass->in_range_of,
			                                     g_strdup (turtle_subject));
		}
	} else if (!g_strcmp0 (turtle_predicate, NRL_MAX_CARDINALITY)) {
		OntologyProperty *prop;

		prop = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (!prop) {
			g_error ("Strange error in max cardinality (%s doesnt exist!)",
			         turtle_subject);
		}
		prop->max_cardinality = g_strdup (turtle_object);

	} else if (!g_strcmp0 (turtle_predicate, RDFS_SUBPROPERTYOF)) {
		/*
		 * (prop A) is subproperty of (prop B)
		 *  -> add B in A->superproperties
		 *  -> add A in B->subproperties (if B is in this ontology)
		 */
		OntologyProperty *propA, *propB;

		propA = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (!propA) {
			g_error ("Strange error in subpropertyof (%s doesnt exist!)",
			         turtle_subject);
		}
		propA->superproperties = g_list_prepend (propA->superproperties,
		                                         g_strdup (turtle_object));

		propB = g_hash_table_lookup (ontology->properties, turtle_object);
		if (propB) {
			propB->subproperties = g_list_prepend (propB->subproperties,
			                                       g_strdup (turtle_subject));
		}

	} else if (!g_strcmp0 (turtle_predicate, NAO_DEPRECATED)) {
		/*
		 * X nao:deprecated true
		 *
271 272 273 274 275 276
		 * This can apply to classes OR properties OR
		 * namespaces!
		 *
		 * NOTE: there is no way to check if we're dealing
		 * with a namespace or not, so we don't error here if
		 * we can't verify the property of class.
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
		 */
		OntologyProperty *prop;
		OntologyClass *klass;

		prop = g_hash_table_lookup (ontology->properties, turtle_subject);
		if (prop) {
			prop->deprecated = string_to_boolean (turtle_object);
		} else {
			/* Try with a class */
			klass = g_hash_table_lookup (ontology->classes, turtle_subject);
			if (klass) {
				klass->deprecated = string_to_boolean (turtle_object);
			}
		}

	} else if (!g_strcmp0 (turtle_predicate, RDFS_LABEL)) {
		/* Intentionaly ignored */
	} else {
		/* DEBUG
		   g_print ("UNHANDLED %s %s %s\n",
		   turtle_subject, turtle_predicate, turtle_object);
		*/
	}
Ivan Frade's avatar
Ivan Frade committed
300 301 302 303

}

static void
304 305 306 307
load_description (OntologyDescription *desc,
                  const gchar         *turtle_subject,
                  const gchar         *turtle_predicate,
                  const gchar         *turtle_object)
Ivan Frade's avatar
Ivan Frade committed
308
{
309 310 311 312
	if (!g_strcmp0 (turtle_predicate, RDFS_TYPE)) {
		g_assert (!g_strcmp0 (turtle_object, DSC_ONTOLOGY));
	} else if (!g_strcmp0 (turtle_predicate, DSC_TITLE)) {
		desc->title = g_strdup (turtle_object);
313 314
	} else if (!g_strcmp0 (turtle_predicate, DSC_DESCRIPTION)) {
		desc->description = g_strdup (turtle_object);
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
	} else if (!g_strcmp0 (turtle_predicate, DSC_UPSTREAM)) {
		desc->upstream = g_strdup (turtle_object);
	} else if (!g_strcmp0 (turtle_predicate, DSC_AUTHOR)) {
		desc->authors = g_list_prepend (desc->authors, g_strdup (turtle_object));
	} else if (!g_strcmp0 (turtle_predicate, DSC_EDITOR)) {
		desc->editors = g_list_prepend (desc->editors, g_strdup (turtle_object));
	} else if (!g_strcmp0 (turtle_predicate, DSC_CONTRIBUTOR)) {
		desc->contributors = g_list_prepend (desc->contributors,
		                                     g_strdup (turtle_object));
	} else if (!g_strcmp0 (turtle_predicate, DSC_GITLOG)) {
		desc->gitlog = g_strdup (turtle_object);
	} else if (!g_strcmp0 (turtle_predicate, DSC_BASEURI)) {
		desc->baseUrl = g_strdup (turtle_object);
	} else if (!g_strcmp0 (turtle_predicate, DSC_RELPATH)) {
		desc->relativePath = g_strdup (turtle_object);
	} else if (!g_strcmp0 (turtle_predicate, DSC_LOCALPREFIX)) {
		desc->localPrefix = g_strdup (turtle_object);
	} else if (!g_strcmp0 (turtle_predicate, DSC_COPYRIGHT)) {
		desc->copyright = g_strdup (turtle_object);
	} else {
		g_critical ("Unhandled element %s", turtle_predicate);
	}
Ivan Frade's avatar
Ivan Frade committed
337 338 339
}

Ontology *
340
ttl_loader_new_ontology (void)
Ivan Frade's avatar
Ivan Frade committed
341
{
342
	Ontology *ontology;
Ivan Frade's avatar
Ivan Frade committed
343

344 345 346 347 348
	ontology = g_new0 (Ontology, 1);
	ontology->classes = g_hash_table_new_full (g_str_hash,
	                                           g_str_equal,
	                                           g_free,
	                                           (GDestroyNotify)ttl_model_class_free);
Ivan Frade's avatar
Ivan Frade committed
349

350 351 352 353
	ontology->properties = g_hash_table_new_full (g_str_hash,
	                                              g_str_equal,
	                                              g_free,
	                                              (GDestroyNotify)ttl_model_property_free);
354 355 356
	ontology->prefixes = g_hash_table_new_full (g_str_hash,
						    g_str_equal,
						    g_free, g_free);
357
	return ontology;
Ivan Frade's avatar
Ivan Frade committed
358 359
}

360 361 362
void
ttl_loader_load_ontology (Ontology    *ontology,
                          GFile       *ttl_file)
363
{
364 365
	TrackerTurtleReader *reader;
	GError *error = NULL;
366

367
	g_return_if_fail (G_IS_FILE (ttl_file));
368

369
	reader = tracker_turtle_reader_new (ttl_file, NULL);
370

371 372 373 374 375
	while (error == NULL && tracker_turtle_reader_next (reader, &error)) {
		load_in_memory (ontology,
		                tracker_turtle_reader_get_subject (reader),
		                tracker_turtle_reader_get_predicate (reader),
		                tracker_turtle_reader_get_object (reader));
376 377
	}

378
	g_object_unref (reader);
379

380 381 382 383
	if (error) {
		g_message ("Turtle parse error: %s", error->message);
		g_error_free (error);
	}
384 385
}

386 387 388
void
ttl_loader_load_prefix_from_description (Ontology            *ontology,
                                         OntologyDescription *description)
389
{
390 391 392 393
	if (!g_hash_table_lookup (ontology->prefixes, description->baseUrl)) {
		g_hash_table_insert (ontology->prefixes,
		                     g_strdup (description->baseUrl),
		                     g_strdup (description->localPrefix));
394 395 396
	}
}

Ivan Frade's avatar
Ivan Frade committed
397
OntologyDescription *
398
ttl_loader_load_description (GFile *file)
Ivan Frade's avatar
Ivan Frade committed
399
{
400
	OntologyDescription *desc;
401
	TrackerTurtleReader *reader;
Ivan Frade's avatar
Ivan Frade committed
402
	GError *error = NULL;
403

404
	desc = ttl_model_description_new ();
405
	reader = tracker_turtle_reader_new (file, NULL);
406 407 408 409

	while (error == NULL && tracker_turtle_reader_next (reader, &error)) {
		load_description (desc,
		                  tracker_turtle_reader_get_subject (reader),
410 411
		                  tracker_turtle_reader_get_predicate (reader),
		                  tracker_turtle_reader_get_object (reader));
412 413 414 415 416 417 418 419
	}

	g_object_unref (reader);

	if (error) {
		g_message ("Turtle parse error: %s", error->message);
		g_error_free (error);
	}
Ivan Frade's avatar
Ivan Frade committed
420

421
	return desc;
Ivan Frade's avatar
Ivan Frade committed
422 423 424
}


425 426
void
ttl_loader_free_ontology (Ontology *ontology)
Ivan Frade's avatar
Ivan Frade committed
427
{
428 429
	g_hash_table_destroy (ontology->classes);
	g_hash_table_destroy (ontology->properties);
430
	g_hash_table_destroy (ontology->prefixes);
431
	g_free (ontology);
Ivan Frade's avatar
Ivan Frade committed
432 433 434 435 436
}

void
ttl_loader_free_description (OntologyDescription *desc)
{
437
	ttl_model_description_free (desc);
Ivan Frade's avatar
Ivan Frade committed
438
}