dee-1.2.7+15.04.20150304/0000755000015300001610000000000012475676370014626 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/build/0000755000015300001610000000000012475676370015725 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/build/Makefile.am0000644000015300001610000000002412475676210017746 0ustar pbuserpbgroup00000000000000SUBDIRS = autotools dee-1.2.7+15.04.20150304/build/autotools/0000755000015300001610000000000012475676370017756 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/build/autotools/gcov.m40000644000015300001610000000501412475676210021147 0ustar pbuserpbgroup00000000000000# Checks for existence of coverage tools: # * gcov # * lcov # * genhtml # * gcovr # # Sets ac_cv_check_gcov to yes if tooling is present # and reports the executables to the variables LCOV, GCOVR and GENHTML. AC_DEFUN([AC_TDD_GCOV], [ AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov], [enable coverage testing with gcov]), [use_gcov=$enableval], [use_gcov=no]) AM_CONDITIONAL(HAVE_GCOV, test "x$use_gcov" = "xyes") if test "x$use_gcov" = "xyes"; then # we need gcc: if test "$GCC" != "yes"; then AC_MSG_ERROR([GCC is required for --enable-gcov]) fi # Check if ccache is being used AC_CHECK_PROG(SHTOOL, shtool, shtool) if test "$SHTOOL"; then AS_CASE([`$SHTOOL path $CC`], [*ccache*], [gcc_ccache=yes], [gcc_ccache=no]) fi if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.]) fi lcov_version_list="1.6 1.7 1.8 1.9 1.10" AC_CHECK_PROG(LCOV, lcov, lcov) AC_CHECK_PROG(GENHTML, genhtml, genhtml) if test "$LCOV"; then AC_CACHE_CHECK([for lcov version], glib_cv_lcov_version, [ glib_cv_lcov_version=invalid lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'` for lcov_check_version in $lcov_version_list; do if test "$lcov_version" = "$lcov_check_version"; then glib_cv_lcov_version="$lcov_check_version (ok)" fi done ]) else lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list" AC_MSG_ERROR([$lcov_msg]) fi case $glib_cv_lcov_version in ""|invalid[)] lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)." AC_MSG_ERROR([$lcov_msg]) LCOV="exit 0;" ;; esac if test -z "$GENHTML"; then AC_MSG_ERROR([Could not find genhtml from the lcov package]) fi ac_cv_check_gcov=yes ac_cv_check_lcov=yes # Remove all optimization flags from CFLAGS changequote({,}) CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'` changequote([,]) # Add the special gcc flags COVERAGE_CFLAGS="-O0 --coverage" COVERAGE_CXXFLAGS="-O0 --coverage" COVERAGE_LDFLAGS="-lgcov" # Check availability of gcovr AC_CHECK_PROG(GCOVR, gcovr, gcovr) if test -z "$GCOVR"; then ac_cv_check_gcovr=no else ac_cv_check_gcovr=yes fi fi ]) # AC_TDD_GCOV dee-1.2.7+15.04.20150304/build/autotools/Makefile.am0000644000015300001610000000003612475676210022002 0ustar pbuserpbgroup00000000000000EXTRA_DIST = introspection.m4 dee-1.2.7+15.04.20150304/build/autotools/introspection.m40000644000015300001610000000661412475676210023120 0ustar pbuserpbgroup00000000000000dnl -*- mode: autoconf -*- dnl Copyright 2009 Johan Dahlin dnl dnl This file is free software; the author(s) gives unlimited dnl permission to copy and/or distribute it, with or without dnl modifications, as long as this notice is preserved. dnl # serial 1 m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], [ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([LT_INIT],[$0])dnl setup libtool first dnl enable/disable introspection m4_if([$2], [require], [dnl enable_introspection=yes ],[dnl AC_ARG_ENABLE(introspection, AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], [Enable introspection for this build]),, [enable_introspection=auto]) ])dnl AC_MSG_CHECKING([for gobject-introspection]) dnl presence/version checking AS_CASE([$enable_introspection], [no], [dnl found_introspection="no (disabled, use --enable-introspection to enable)" ],dnl [yes],[dnl PKG_CHECK_EXISTS([gobject-introspection-1.0],, AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) ],dnl [auto],[dnl PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) ],dnl [dnl AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) ])dnl AC_MSG_RESULT([$found_introspection]) INTROSPECTION_SCANNER= INTROSPECTION_COMPILER= INTROSPECTION_GENERATE= INTROSPECTION_GIRDIR= INTROSPECTION_TYPELIBDIR= if test "x$found_introspection" = "xyes"; then INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection fi AC_SUBST(INTROSPECTION_SCANNER) AC_SUBST(INTROSPECTION_COMPILER) AC_SUBST(INTROSPECTION_GENERATE) AC_SUBST(INTROSPECTION_GIRDIR) AC_SUBST(INTROSPECTION_TYPELIBDIR) AC_SUBST(INTROSPECTION_CFLAGS) AC_SUBST(INTROSPECTION_LIBS) AC_SUBST(INTROSPECTION_MAKEFILE) AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") ]) dnl Usage: dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], [ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) ]) dnl Usage: dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], [ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) ]) dee-1.2.7+15.04.20150304/autogen.sh0000755000015300001610000000025512475676210016622 0ustar pbuserpbgroup00000000000000#!/bin/sh srcdir=`dirname $0` PKG_NAME="dee" which gnome-autogen.sh || { echo "You need gnome-common from GNOME SVN" exit 1 } USE_GNOME2_MACROS=1 \ . gnome-autogen.sh dee-1.2.7+15.04.20150304/bindings/0000755000015300001610000000000012475676370016423 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/bindings/Makefile.am0000644000015300001610000000002212475676210020442 0ustar pbuserpbgroup00000000000000SUBDIRS = python dee-1.2.7+15.04.20150304/bindings/README0000644000015300001610000000064212475676210017276 0ustar pbuserpbgroup00000000000000Currently none of these bindings are installed or even properly working. Python ------ Currently not working because it's blocked on https://bugzilla.gnome.org/show_bug.cgi?id=638915 "Array of GVariants passed as NULL" in pygobject. If you want to play around copy the Dee.py file into the gi.overrides module, fx. $prefix/lib/python2.7/site-packages/gi/overrides/ and you should be able to run the Python samples. dee-1.2.7+15.04.20150304/bindings/python/0000755000015300001610000000000012475676370017744 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/bindings/python/Makefile.am0000644000015300001610000000014712475676210021773 0ustar pbuserpbgroup00000000000000pygioverridesdir = $(PYGI_OVERRIDES_DIR) pygioverrides_PYTHON = \ Dee.py EXTRA_DIST = Dee.py dee-1.2.7+15.04.20150304/bindings/python/Dee.py0000644000015300001610000001542012475676210021006 0ustar pbuserpbgroup00000000000000from gi.overrides import override from gi.importer import modules Dee = modules['Dee']._introspection_module from gi.repository import GLib __all__ = [] class RowWrapper: def __init__ (self, model, itr): self.model = model self.itr = itr self.__initialized = True def __getitem__ (self, column): return self.model.get_value(self.itr, column) def __setitem__ (self, column, val): self.model.set_value (self.itr, column, val) def __getattr__ (self, name): col_index = self.model.get_column_index (name) if col_index < 0: raise AttributeError("object has no attribute '%s'" % name) return self.model.get_value (self.itr, col_index) def __setattr__ (self, name, value): if not "_RowWrapper__initialized" in self.__dict__: self.__dict__[name] = value return col_index = self.model.get_column_index (name) if col_index < 0: raise AttributeError("object has no attribute '%s'" % name) self.model.set_value (self.itr, col_index, value) def __iter__ (self): for column in range(self.model.get_n_columns()): yield self.model.get_value (self.itr, column) def __len__ (self): return self.model.get_n_columns() def __str__ (self): return "(%s)" % ", ".join(map(str,self)) def __eq__ (self, other): if not isinstance (other, RowWrapper): return False if self.model != other.model: return False return self.itr == other.itr class Model(Dee.Model): def __init__(self): Dee.Model.__init__(self) def set_schema (self, *args): self.set_schema_full (tuple(args)) def set_column_names (self, *args): self.set_column_names_full (tuple(args)) def _build_row (self, args, kwargs): schema = self.get_schema() result = [None] * len(schema) if len(args) > 0: for i, arg in enumerate(args): if isinstance(arg, GLib.Variant): result[i] = arg else: result[i] = GLib.Variant(schema[i], arg) # check if result.count(None) > 0: raise RuntimeError("Not all columns were set") else: names = self.get_column_names() dicts = [None] * len(schema) if len(names) == 0: raise RuntimeError("Column names were not set") for col_name, arg in kwargs.items(): if names.count(col_name) > 0: col_index = names.index(col_name) variant = arg if isinstance(arg, GLib.Variant) else GLib.Variant(schema[col_index], arg) result[col_index] = variant else: col_schema, col_index = self.get_field_schema(col_name) if col_schema: variant = arg if isinstance(arg, GLib.Variant) else GLib.Variant(col_schema, arg) colon_index = col_name.find("::") field_name = col_name if colon_index < 0 else col_name[colon_index+2:] if dicts[col_index] is None: dicts[col_index] = {} dicts[col_index][field_name] = variant else: raise RuntimeError("Unknown column name: %s" % col_name) # finish vardict creation for index, d in enumerate(dicts): if d: result[index] = GLib.Variant(schema[index], d) # handle empty dicts (no "xrange" in python3) for i in range(len(schema)): if result[i] is None and schema[i] == "a{sv}": result[i] = GLib.Variant(schema[i], {}) # checks num_unset = result.count(None) if num_unset > 0: col_name = names[result.index(None)] raise RuntimeError("Column '%s' was not set" % col_name) return result def prepend (self, *args, **kwargs): return self.prepend_row (self._build_row(args, kwargs)) def append (self, *args, **kwargs): return self.append_row (self._build_row(args, kwargs)) def insert (self, pos, *args, **kwargs): return self.insert_row (pos, self._build_row(args, kwargs)) def insert_before (self, iter, *args, **kwargs): return self.insert_row_before (iter, self._build_row(args, kwargs)) def insert_row_sorted (self, row_spec, sort_func, data): return self.insert_row_sorted_with_sizes (row_spec, sort_func, data) def insert_sorted (self, sort_func, *args, **kwargs): return self.insert_row_sorted (self._build_row(args, kwargs), sort_func, None) def find_row_sorted (self, row_spec, sort_func, data): return self.find_row_sorted_with_sizes (row_spec, sort_func, data) def find_sorted (self, sort_func, *args, **kwargs): return self.find_row_sorted (self._build_row(args, kwargs), sort_func, None) def get_schema (self): return Dee.Model.get_schema(self) def get_value (self, itr, column): return Dee.Model.get_value (self, itr, column).unpack() def set_value (self, itr, column, value): var = GLib.Variant (self.get_column_schema(column), value) if isinstance (itr, int): itr = self.get_iter_at_row(itr) Dee.Model.set_value (self, itr, column, var) def __getitem__ (self, itr): if isinstance (itr, int): itr = self.get_iter_at_row(itr) return RowWrapper(self, itr) def __setitem__ (self, itr, row): max_col = self.get_n_columns () for column, value in enumerate (row): if column >= max_col: raise IndexError("Too many columns in row assignment: %s" % column) self.set_value (itr, column, value) def get_row (self, itr): return self[itr] def __iter__ (self): itr = self.get_first_iter () last = self.get_last_iter () while itr != last: yield self.get_row(itr) itr = self.next(itr) raise StopIteration def __len__ (self): return self.get_n_rows() class ModelIter(Dee.ModelIter): def __init__(self): Dee.ModelIter.__init__(self) def __eq__ (self, other): if not isinstance (other, ModelIter): return False return repr(self) == repr(other) Model = override(Model) __all__.append('Model') ModelIter = override(ModelIter) __all__.append('ModelIter') dee-1.2.7+15.04.20150304/Makefile.am0000644000015300001610000000126612475676210016660 0ustar pbuserpbgroup00000000000000ACLOCAL_AMFLAGS = -I build/autotools include $(top_srcdir)/Makefile.am.coverage SUBDIRS = build src doc examples tools vapi bindings pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = dee-1.0.pc if HAVE_ICU pkgconfig_DATA += dee-icu-1.0.pc endif if WANT_TESTS SUBDIRS += tests # Test framework .PHONY: check-report full-report check-headless check-report full-report check-headless: $(MAKE) -C tests/ $(@) endif CLEANFILES = dee-1.0.pc DISTCLEANFILES = dee-1.0.pc EXTRA_DIST = \ autogen.sh \ dee-1.0.pc.in \ dee-icu-1.0.pc.in \ COPYING.GPL DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --with-pygi-overrides-dir='$$(pyexecdir)'/gi/overrides benchmark: cd tests && make benchmark dee-1.2.7+15.04.20150304/tests/0000755000015300001610000000000012475676370015770 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/tests/test-analyzer.c0000644000015300001610000001055512475676210020735 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include typedef struct { DeeAnalyzer *analyzer; DeeTermList *terms; } Fixture; static void setup (Fixture *fix, gconstpointer data); static void text_setup (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup (Fixture *fix, gconstpointer data) { fix->analyzer = dee_analyzer_new (); fix->terms = g_object_new (DEE_TYPE_TERM_LIST, NULL); } static void text_setup (Fixture *fix, gconstpointer data) { fix->analyzer = DEE_ANALYZER (dee_text_analyzer_new ()); fix->terms = g_object_new (DEE_TYPE_TERM_LIST, NULL); } static void teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->analyzer); g_object_unref (fix->terms); fix->analyzer = NULL; fix->terms = NULL; } static void test_simple (Fixture *fix, gconstpointer data) { dee_analyzer_tokenize (fix->analyzer, "tok", fix->terms); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "tok"); dee_term_list_clear (fix->terms); dee_analyzer_tokenize (fix->analyzer, "tok kot", fix->terms); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "tok kot"); dee_term_list_clear (fix->terms); dee_analyzer_analyze (fix->analyzer, "foobar", fix->terms, NULL); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "foobar"); } void _casefold (DeeTermList *in, DeeTermList *out, gpointer data) { int i; gchar *fold; for (i = 0; i < dee_term_list_num_terms (in); i++) { fold = g_utf8_casefold (dee_term_list_get_term (in, i), -1); dee_term_list_add_term (out, fold); g_free (fold); } } static void test_term_filter1 (Fixture *fix, gconstpointer data) { dee_analyzer_analyze (fix->analyzer, "foobar", fix->terms, NULL); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "foobar"); dee_term_list_clear (fix->terms); dee_analyzer_add_term_filter(fix->analyzer, _casefold, NULL, NULL); dee_analyzer_analyze (fix->analyzer, "FooBar", fix->terms, NULL); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "foobar"); dee_term_list_clear (fix->terms); } void test_text_analyzer_simple (Fixture *fix, gconstpointer data) { dee_analyzer_analyze (fix->analyzer, "foobar", fix->terms, NULL); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "foobar"); dee_term_list_clear (fix->terms); dee_analyzer_analyze (fix->analyzer, "FooBar ", fix->terms, NULL); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "foobar"); dee_term_list_clear (fix->terms); dee_analyzer_analyze (fix->analyzer, "foo baR", fix->terms, NULL); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 2); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "foo"); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 1), ==, "bar"); dee_term_list_clear (fix->terms); } void test_analyzer_create_suite (void) { g_test_add ("/Index/Analyzer/Simple", Fixture, 0, setup, test_simple, teardown); g_test_add ("/Index/Analyzer/TermFilter1", Fixture, 0, setup, test_term_filter1, teardown); g_test_add ("/Index/TextAnalyzer/Simple", Fixture, 0, text_setup, test_text_analyzer_simple, teardown); } dee-1.2.7+15.04.20150304/tests/test-python.py0000644000015300001610000000146612475676210020640 0ustar pbuserpbgroup00000000000000import os, unittest import gi, gi.overrides gi.overrides.__path__ = os.environ["DEE_TEST_PYGOBJECT_OVERRIDEDIR"] print "Running Python tests with overrides path '%s'" % gi.overrides.__path__ from gi.repository import Dee class TreeIndexTest (unittest.TestCase): model = None analyzer = None index = None def testEmpty (self): self.model = Dee.SequenceModel.new () self.model.set_schema ("i", "s") self.analyzer = Dee.TextAnalyzer.new () def readString (model, iter, data): return model[iter][1] self.index = Dee.TreeIndex.new (self.model, self.analyzer, readString, None) row = self.model.append (1, "one") result = self.index.lookup_one ("one") self.assertEquals (row, result) self.assertEquals (self.model[row], self.model[result]) if __name__ == '__main__': unittest.main() dee-1.2.7+15.04.20150304/tests/server-helper-client.c0000644000015300001610000000540712475676210022172 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Michal Hruby * */ #include "config.h" #include #include #include #include #include static void on_peer_found (DeePeer *peer, gchar *name, gint *n_peers) { (*n_peers)++; } static void on_peer_lost (DeePeer *peer, gchar *name, gint *n_peers) { (*n_peers)++; } static int peer_function (gchar *argv[]) { DeePeer *peer; gint n_peers = 0; peer = (DeePeer*) dee_client_new (argv[1]); g_signal_connect (peer, "peer-found", G_CALLBACK (on_peer_found), &n_peers); g_signal_connect (peer, "peer-lost", G_CALLBACK (on_peer_lost), &n_peers); if (gtx_wait_for_signal (G_OBJECT (peer), 10000, "notify::swarm-leader", NULL)) g_error ("Peer helper timed out waiting for swarm leader"); /* The main process should be swarm leaders. Not us */ g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (peer)); /* At this point we shouldn't have emitted 'peer-found' yet */ g_assert_cmpint (0, ==, n_peers); if (gtx_wait_for_signal (G_OBJECT (peer), 10000, "peer-found", NULL)) g_error ("Peer helper timed out waiting for 'peer-found' signal"); g_assert_cmpint (1, ==, n_peers); g_assert_cmpint (1, ==, g_strv_length (dee_peer_list_peers (peer))); gtx_assert_last_unref (peer); return 0; } static gint finished_children = 0; static void child_exited (GPid pid, gint status, gpointer user_data) { finished_children++; } gint main (gint argc, gchar *argv[]) { int num_clients, i; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc < 3) g_error ("Invalid invocation"); num_clients = i = atoi (argv[2]); while (i-- > 0) { GPid pid = (GPid) fork (); if (pid != 0) g_child_watch_add (pid, child_exited, NULL); else { return peer_function (argv); } } for (i = 0; i < 10; i++) { gtx_yield_main_loop (200); if (finished_children == num_clients) break; } /* Give a window of opportunity for children to * flush stdout/err before we exit */ gtx_yield_main_loop (200); g_assert_cmpint (num_clients, ==, finished_children); return 0; } dee-1.2.7+15.04.20150304/tests/Makefile.am0000644000015300001610000001212712475676210020020 0ustar pbuserpbgroup00000000000000NULL = noinst_PROGRAMS = \ test-dee \ test-benchmark \ $(NULL) AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_srcdir)/src \ -DTESTDIR=\""$(top_builddir)/tests"\" \ -DDEE_COMPILATION \ $(GCC_FLAGS) \ $(DEE_CFLAGS) \ $(MAINTAINER_CFLAGS) test_benchmark_SOURCES = \ test-benchmark.c test_benchmark_LDADD = $(top_builddir)/src/libdee-1.0.la $(DEE_LIBS) -lm benchmark: test-benchmark ./test-benchmark test_dee_SOURCES = \ test-analyzer.c \ test-dee.c \ test-filter-model.c \ test-glist-result-set.c \ test-index.c \ test-model-column.c \ test-model-complex-column.c \ test-model-readers.c \ test-model-rows.c \ test-model-signals.c \ test-model-seqnums.c \ test-model-tags.c \ test-resource-manager.c \ test-serializable.c \ test-transaction.c \ test-term-list.c \ $(top_srcdir)/src/dee-glist-result-set.h \ $(NULL) test_dee_LDADD = $(top_builddir)/src/libdee-1.0.la $(DEE_LIBS) if HAVE_ICU test_dee_SOURCES += test-icu.c endif if HAVE_GTX test_dee_SOURCES += test-model-interactions.c test_dee_SOURCES += test-peer-interactions.c test_dee_SOURCES += test-client-server.c AM_CPPFLAGS += $(GTX_CFLAGS) test_dee_LDADD += $(GTX_LIBS) model_helpers = \ model-helper-add3rows.c \ model-helper-append1.c \ model-helper-change3rows.c \ model-helper-clear3rows.c \ model-helper-clear6rows.c \ model-helper-clone3rows.c \ model-helper-clone3rows-meta.c \ model-helper-clear3add5.c \ model-helper-insert1row.c \ model-helper-introspect.c \ model-helper-remove3rows.c \ model-helper-replace.c \ model-helper-resync3rows.c \ model-helper-schemaless.c \ $(NULL) peer_helpers = \ peer-helper-1peer.c \ $(NULL) server_helpers = \ server-helper-client.c noinst_PROGRAMS += \ $(model_helpers:.c=) \ $(peer_helpers:.c=) \ $(server_helpers:.c=) \ $(NULL) model_helper_clone3rows_SOURCES = model-helper-clone3rows.c model_helper_clone3rows_LDADD = $(test_dee_LDADD) model_helper_clone3rows_meta_SOURCES = model-helper-clone3rows-meta.c model_helper_clone3rows_meta_LDADD = $(test_dee_LDADD) model_helper_add3rows_SOURCES = model-helper-add3rows.c model_helper_add3rows_LDADD = $(test_dee_LDADD) model_helper_append1_SOURCES = model-helper-append1.c model_helper_append1_LDADD = $(test_dee_LDADD) model_helper_change3rows_SOURCES = model-helper-change3rows.c model_helper_change3rows_LDADD = $(test_dee_LDADD) model_helper_remove3rows_SOURCES = model-helper-remove3rows.c model_helper_remove3rows_LDADD = $(test_dee_LDADD) model_helper_clear3rows_SOURCES = model-helper-clear3rows.c model_helper_clear3rows_LDADD = $(test_dee_LDADD) model_helper_clear6rows_SOURCES = model-helper-clear6rows.c model_helper_clear6rows_LDADD = $(test_dee_LDADD) model_helper_clear3add5_SOURCES = model-helper-clear3add5.c model_helper_clear3add5_LDADD = $(test_dee_LDADD) model_helper_insert1row_SOURCES = model-helper-insert1row.c model_helper_insert1row_LDADD = $(test_dee_LDADD) model_helper_schemaless_SOURCES = model-helper-schemaless.c model_helper_schemaless_LDADD = $(test_dee_LDADD) model_helper_introspect_SOURCES = model-helper-introspect.c model_helper_introspect_LDADD = $(test_dee_LDADD) model_helper_replace_SOURCES = model-helper-replace.c model_helper_replace_LDADD = $(test_dee_LDADD) model_helper_resync3rows_SOURCES = model-helper-resync3rows.c model_helper_resync3rows_LDADD = $(test_dee_LDADD) peer_helper_1peer_SOURCES = peer-helper-1peer.c peer_helper_1peer_LDADD = $(test_dee_LDADD) server_helper_client_SOURCES = server-helper-client.c server_helper_client_LDADD = $(test_dee_LDADD) endif # HAVE_GTX # # Python tests disabled because of https://bugzilla.gnome.org/show_bug.cgi?id=660647 # #PYTHON_TESTS_ENV_VARS= \ # PYTHONPATH=$(top_builddir)/tests:$${PYTHONPATH:+:$$PYTHONPATH} \ # GI_TYPELIB_PATH=$(top_builddir)/src:$$GI_TYPELIB_PATH \ # XDG_DATA_DIRS=$(top_builddir)/src:$XDG_DATA_DIRS:/usr/share \ # LD_LIBRARY_PATH=$(top_builddir)/src/.libs:$$LD_LIBRARY_PATH \ # DEE_TEST_PYGOBJECT_OVERRIDEDIR=$(top_srcdir)/bindings/python # #test-python: # $(PYTHON_TESTS_ENV_VARS) python test-python.py .PHONY: test test: @dbus-test-runner -m 60 --task gtester \ --parameter --verbose \ --parameter -o=test-dee-results.xml \ --parameter -k \ --parameter ./test-dee .PHONY: check-report full-report check-report: @dbus-test-runner -m 60 --task gtester \ --parameter -o=test-dee-results.xml \ --parameter -k \ --parameter ./test-dee \ && ( gtester-report test-dee-results.xml \ | sed 's/GTester Unit Test ReportGTester Unit Test Report (normal) test-dee-results.html ) \ && gnome-open ./test-dee-results.html full-report: @dbus-test-runner -m 60 --task gtester \ --parameter -o=test-dee-results.xml \ --parameter -k \ --parameter -m=slow \ --parameter ./test-dee \ && ( gtester-report test-dee-results.xml \ | sed 's/>GTester Unit Test ReportGTester Unit Test Report (normal) test-dee-results.html ) \ && ( xdg-open test-dee-results.html ) #run make test as part of make check #check-local: test test-python check-local: test clean-generic: rm -rf test-dee-results.xml test-dee-results.html dee-test-resource-manager dee-1.2.7+15.04.20150304/tests/model-helper-replace.c0000644000015300001610000000367312475676210022124 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010-2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Michal Hruby * */ #include "config.h" #include #include #include #include /* Joins an existing model, and then tries to define the column types, * and add two rows with these types */ gint main (gint argc, gchar *argv[]) { DeePeer *peer; DeeModel *model; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) { peer = DEE_PEER (g_object_new (DEE_TYPE_PEER, "swarm-name", argv[1], "swarm-owner", TRUE, NULL)); model = dee_shared_model_new_for_peer (peer); dee_model_set_schema (model, "i", "s", NULL); } else { g_critical ("Missing swarm name! Use \"%s [swarm name]\"", argv[0]); return 1; } if (gtx_wait_for_signal (G_OBJECT (model), 300, "notify::synchronized", NULL)) { g_critical ("Model never synchronized"); return 1; } g_assert (dee_peer_is_swarm_leader (peer)); dee_model_append (model, 27, "skunkworks"); dee_model_append (model, 68, "wumbo"); dee_shared_model_flush_revision_queue_sync (DEE_SHARED_MODEL (model)); gtx_yield_main_loop (500); /* And we're still the leader */ g_assert (dee_peer_is_swarm_leader (peer)); gtx_assert_last_unref (model); return 0; } dee-1.2.7+15.04.20150304/tests/test-serializable.c0000644000015300001610000001545612475676210021563 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include #include typedef struct { DeeModel *orig; DeeModel *copy; } Fixture; static void sequence_model_setup (Fixture *fix, gconstpointer data); static void sequence_model_teardown (Fixture *fix, gconstpointer data); static void shared_model_setup (Fixture *fix, gconstpointer data); static void shared_model_teardown (Fixture *fix, gconstpointer data); static void test_model_zero_rows (Fixture *fix, gconstpointer data); static void test_model_one_rows (Fixture *fix, gconstpointer data); static void test_model_two_rows (Fixture *fix, gconstpointer data); static void test_model_non_zero_seqnum_offset (Fixture *fix, gconstpointer data); void test_serializable_create_suite (void) { #define SEQUENCE_MODEL_DOMAIN "/Serializable/SequenceModel" #define SHARED_MODEL_DOMAIN "/Serializable/SharedModel" g_test_add (SEQUENCE_MODEL_DOMAIN"/ZeroRows", Fixture, 0, sequence_model_setup, test_model_zero_rows, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/ZeroRows", Fixture, 0, shared_model_setup, test_model_zero_rows, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/OneRows", Fixture, 0, sequence_model_setup, test_model_one_rows, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/OneRows", Fixture, 0, sequence_model_setup, test_model_one_rows, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/TwoRows", Fixture, 0, sequence_model_setup, test_model_two_rows, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/TwoRows", Fixture, 0, shared_model_setup, test_model_two_rows, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/NonZeroSeqnumOffset", Fixture, 0, sequence_model_setup, test_model_non_zero_seqnum_offset, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/NonZeroSeqnumOffset", Fixture, 0, shared_model_setup, test_model_non_zero_seqnum_offset, shared_model_teardown); } static void sequence_model_setup (Fixture *fix, gconstpointer data) { fix->orig = dee_sequence_model_new (); dee_model_set_schema (fix->orig, "i", "s", NULL); fix->copy = NULL; g_assert (DEE_IS_SEQUENCE_MODEL (fix->orig)); } static void sequence_model_teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->orig); fix->orig = NULL; if (fix->copy) g_object_unref (fix->copy); fix->copy = NULL; } static void shared_model_setup (Fixture *fix, gconstpointer data) { fix->orig = dee_shared_model_new ("org.example.ThisIsNotATest"); dee_model_set_schema (fix->orig, "i", "s", NULL); fix->copy = NULL; g_assert (DEE_IS_SHARED_MODEL (fix->orig)); } static void shared_model_teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->orig); fix->orig = NULL; if (fix->copy) g_object_unref (fix->copy); fix->copy = NULL; } static void dee_assert_cmpmodel (DeeModel *m1, DeeModel *m2) { guint i, j, n_cols, n_rows; DeeModelIter *row1, *row2; GVariant *v1, *v2; g_assert (m1 != NULL); g_assert (m2 != NULL); g_assert (DEE_IS_MODEL (m1)); g_assert (DEE_IS_MODEL (m2)); g_assert_cmpint (dee_model_get_n_columns (m1), ==, dee_model_get_n_columns (m2)); g_assert_cmpint (dee_model_get_n_rows (m1), ==, dee_model_get_n_rows (m2)); n_cols = dee_model_get_n_columns (m1); for (i = 0; i < n_cols; i++) { g_assert_cmpstr (dee_model_get_column_schema (m1, i), ==, dee_model_get_column_schema (m1, i)); } n_rows = dee_model_get_n_rows (m1); for (i = 0; i < n_rows; i++) { row1 = dee_model_get_iter_at_row (m1, i); row2 = dee_model_get_iter_at_row (m2, i); for (j = 0; j < n_cols; j++) { v1 = dee_model_get_value (m1, row1, j); v2 = dee_model_get_value (m2, row2, j); g_assert (g_variant_equal (v1, v2)); g_variant_unref (v1); g_variant_unref (v2); } } if (DEE_IS_SERIALIZABLE_MODEL (m1) && DEE_IS_SERIALIZABLE_MODEL (m2)) { g_assert_cmpuint(dee_serializable_model_get_seqnum (m1), ==, dee_serializable_model_get_seqnum (m2)); } if (DEE_IS_SHARED_MODEL (m1) && DEE_IS_SHARED_MODEL (m2)) { g_assert_cmpstr (dee_shared_model_get_swarm_name (DEE_SHARED_MODEL (m1)), ==, dee_shared_model_get_swarm_name (DEE_SHARED_MODEL (m2))); } } static void test_model_zero_rows (Fixture *fix, gconstpointer data) { GVariant *vdata; vdata = dee_serializable_externalize (DEE_SERIALIZABLE (fix->orig)); g_assert (vdata != NULL); fix->copy = DEE_MODEL (dee_serializable_parse_external (vdata)); dee_assert_cmpmodel (fix->orig, fix->copy); } static void test_model_one_rows (Fixture *fix, gconstpointer data) { GVariant *vdata; dee_model_append (fix->orig, 27, "Hello world"); vdata = dee_serializable_externalize (DEE_SERIALIZABLE (fix->orig)); g_assert (vdata != NULL); fix->copy = DEE_MODEL (dee_serializable_parse_external (vdata)); dee_assert_cmpmodel (fix->orig, fix->copy); } static void test_model_two_rows (Fixture *fix, gconstpointer data) { GVariant *vdata; dee_model_append (fix->orig, 27, "Hello world"); dee_model_append (fix->orig, 68, "Hola Mars"); vdata = dee_serializable_externalize (DEE_SERIALIZABLE (fix->orig)); g_assert (vdata != NULL); fix->copy = DEE_MODEL (dee_serializable_parse_external (vdata)); dee_assert_cmpmodel (fix->orig, fix->copy); } static void test_model_non_zero_seqnum_offset (Fixture *fix, gconstpointer data) { GVariant *vdata; DeeModelIter *first_row; first_row = dee_model_append (fix->orig, 27, "Hello world"); dee_model_append (fix->orig, 68, "Hola Mars"); dee_model_remove (fix->orig, first_row); vdata = dee_serializable_externalize (DEE_SERIALIZABLE (fix->orig)); g_assert (vdata != NULL); fix->copy = DEE_MODEL (dee_serializable_parse_external (vdata)); g_assert_cmpuint (1, ==, dee_model_get_n_rows (fix->copy)); g_assert_cmpuint (3, ==, dee_serializable_model_get_seqnum (fix->copy)); dee_assert_cmpmodel (fix->orig, fix->copy); } dee-1.2.7+15.04.20150304/tests/model-helper-clear6rows.c0000644000015300001610000000563012475676210022573 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Michal Hruby * */ #include "config.h" #include #include #include #include guint64 before_begin_seqnum, before_end_seqnum, after_begin_seqnum, after_end_seqnum; static void _begin_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { before_begin_seqnum = begin_seqnum; before_end_seqnum = end_seqnum; } static void _end_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { after_begin_seqnum = begin_seqnum; after_end_seqnum = end_seqnum; } static void _row_removed (DeeModel *model, DeeModelIter *iter, GSList **removed) { /* Yes, I _know_ that append() is slow, but this is a test! */ *removed = g_slist_append (*removed, iter); } /* Expects a clone with 3 rows in it, and a later transaction appending 3 more * rows and a clear. */ gint main (gint argc, gchar *argv[]) { DeeModel *model; GSList *removed; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); g_signal_connect (model, "begin-transaction", G_CALLBACK (_begin_txn), NULL); g_signal_connect (model, "end-transaction", G_CALLBACK (_end_txn), NULL); if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); /* Listen for removes */ removed = NULL; g_signal_connect (model, "row-removed", G_CALLBACK (_row_removed), &removed); /* Wait for some RowsChanged signals */ gtx_yield_main_loop (1000); g_assert_cmpint (g_slist_length (removed), ==, 6); g_assert_cmpint (dee_model_get_n_rows(model), ==, 0); /* We expected 3 additions and a clear - ie. 3 additions and 6 removals */ g_assert_cmpint (12, ==, (guint) dee_serializable_model_get_seqnum (model)); g_assert (before_begin_seqnum == after_begin_seqnum); g_assert (before_end_seqnum == after_end_seqnum); g_assert_cmpint (3, ==, (guint) before_begin_seqnum); g_assert_cmpint (12, ==, (guint) before_end_seqnum); gtx_assert_last_unref (model); g_slist_free (removed); return 0; } dee-1.2.7+15.04.20150304/tests/test-model-seqnums.c0000644000015300001610000001122712475676210021676 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * Neil Jagdish Patel * */ #include #include #include typedef struct { DeeModel *model; } SeqnumFixture; static void setup (SeqnumFixture *fix, gconstpointer data); static void teardown (SeqnumFixture *fix, gconstpointer data); static void proxy_setup (SeqnumFixture *fix, gconstpointer data); static void proxy_teardown (SeqnumFixture *fix, gconstpointer data); static void txn_setup (SeqnumFixture *fix, gconstpointer data); static void txn_teardown (SeqnumFixture *fix, gconstpointer data); static void test_getset_last (SeqnumFixture *fix, gconstpointer data); static void test_auto_inc (SeqnumFixture *fix, gconstpointer data); void test_model_seqnums_create_suite (void) { #define SEQ_DOMAIN "/Model/Sequence/Seqnums" #define PROXY_DOMAIN "/Model/Proxy/Seqnums" #define TXN_DOMAIN "/Model/Transaction/Seqnums" g_test_add (SEQ_DOMAIN"/GetSet", SeqnumFixture, 0, setup, test_getset_last, teardown); g_test_add (PROXY_DOMAIN"/GetSet", SeqnumFixture, 0, proxy_setup, test_getset_last, proxy_teardown); g_test_add (TXN_DOMAIN"/GetSet", SeqnumFixture, 0, txn_setup, test_getset_last, txn_teardown); g_test_add (SEQ_DOMAIN"/AutoInc", SeqnumFixture, 0, setup, test_auto_inc, teardown); g_test_add (PROXY_DOMAIN"/AutoInc", SeqnumFixture, 0, proxy_setup, test_auto_inc, proxy_teardown); g_test_add (TXN_DOMAIN"/AutoInc", SeqnumFixture, 0, txn_setup, test_auto_inc, txn_teardown); } static void setup (SeqnumFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "i", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); g_assert_cmpint (1, ==, dee_model_get_n_columns (fix->model)); g_assert_cmpstr ("i", ==, dee_model_get_column_schema (fix->model, 0)); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); } static void teardown (SeqnumFixture *fix, gconstpointer data) { g_object_unref (fix->model); } static void proxy_setup (SeqnumFixture *fix, gconstpointer data) { setup (fix, data); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", fix->model, NULL); g_assert (DEE_IS_PROXY_MODEL (fix->model)); } static void proxy_teardown (SeqnumFixture *fix, gconstpointer data) { g_assert (DEE_IS_PROXY_MODEL (fix->model)); g_object_unref (fix->model); } static void txn_setup (SeqnumFixture *fix, gconstpointer data) { DeeModel *dum; setup (fix, data); dum = fix->model; fix->model = dee_transaction_new (dum); g_object_unref (dum); g_assert (DEE_IS_TRANSACTION (fix->model)); } static void txn_teardown (SeqnumFixture *fix, gconstpointer data) { g_assert (DEE_IS_TRANSACTION (fix->model)); g_object_unref (fix->model); } static void test_getset_last (SeqnumFixture *fix, gconstpointer data) { DeeModel *model = fix->model; g_assert_cmpint (0, ==, dee_serializable_model_get_seqnum (model)); dee_serializable_model_set_seqnum (model, 68); g_assert_cmpint (68, ==, dee_serializable_model_get_seqnum (model)); } static void test_auto_inc (SeqnumFixture *fix, gconstpointer data) { DeeModel *model = fix->model; g_assert_cmpint (0, ==, dee_serializable_model_get_seqnum (model)); dee_model_append (fix->model, 9); g_assert_cmpint (1, ==, dee_serializable_model_get_seqnum (model)); dee_model_set (fix->model, dee_model_get_first_iter (fix->model), 10); g_assert_cmpint (2, ==, dee_serializable_model_get_seqnum (model)); dee_model_clear (fix->model); g_assert_cmpint (3, ==, dee_serializable_model_get_seqnum (model)); dee_model_append (fix->model, 11); dee_model_append (fix->model, 12); dee_model_append (fix->model, 13); g_assert_cmpint (6, ==, dee_serializable_model_get_seqnum (model)); dee_model_clear (fix->model); g_assert_cmpint (9, ==, dee_serializable_model_get_seqnum (model)); } dee-1.2.7+15.04.20150304/tests/model-helper-insert1row.c0000644000015300001610000000450612475676210022622 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include static void _row_added (DeeModel *model, DeeModelIter *iter, GSList **rows_so_far) { /* Yes, I _know_ that append() is slow, but this is a test! */ *rows_so_far = g_slist_append (*rows_so_far, iter); } /* Expects and empty clone and three rows-added signals */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; GSList *rows_added; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); /* Wait until we find the leader */ if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); /* Listen for changes */ rows_added = NULL; g_signal_connect (model, "row-added", G_CALLBACK (_row_added), &rows_added); /* Wait for some RowsAdded signals */ gtx_yield_main_loop (1000); /* Check that we got what we expected */ g_assert_cmpint (g_slist_length (rows_added), == , 1); g_assert_cmpint (dee_model_get_n_rows (model), ==, 4); iter = (DeeModelIter*) g_slist_nth (rows_added, 0)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 27); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "twentyseven"); gtx_assert_last_unref (model); g_slist_free (rows_added); return 0; } dee-1.2.7+15.04.20150304/tests/model-helper-clear3add5.c0000644000015300001610000000763612475676210022423 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Michal Hruby * */ #include "config.h" #include #include #include #include guint64 before_begin_seqnum, before_end_seqnum, after_begin_seqnum, after_end_seqnum; static void _begin_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { before_begin_seqnum = begin_seqnum; before_end_seqnum = end_seqnum; } static void _end_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { after_begin_seqnum = begin_seqnum; after_end_seqnum = end_seqnum; } static void _row_added (DeeModel *model, DeeModelIter *iter, GSList **added) { /* Yes, I _know_ that append() is slow, but this is a test! */ *added = g_slist_append (*added, iter); } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; GSList *added; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); g_signal_connect (model, "begin-transaction", G_CALLBACK (_begin_txn), NULL); g_signal_connect (model, "end-transaction", G_CALLBACK (_end_txn), NULL); if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); /* Listen for adds */ added = NULL; g_signal_connect (model, "row-added", G_CALLBACK (_row_added), &added); /* We expect the model to be cleared and 5 rows added */ gtx_yield_main_loop (1000); /* The transaction could be optimized in the future and actually just add * the two missing rows */ g_assert (g_slist_length (added) == 5 || g_slist_length (added) == 2); g_assert_cmpint (dee_model_get_n_rows (model), ==, 5); /* Check ordering and contents */ iter = dee_model_get_first_iter (model); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), ==, 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), ==, "zero"); iter = dee_model_next (model, iter); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), ==, 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), ==, "one"); iter = dee_model_next (model, iter); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), ==, 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), ==, "two"); iter = dee_model_next (model, iter); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), ==, 3); g_assert_cmpstr (dee_model_get_string (model, iter, 1), ==, "three"); iter = dee_model_next (model, iter); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), ==, 4); g_assert_cmpstr (dee_model_get_string (model, iter, 1), ==, "four"); /* Disregarding the hypothetical optimization mentioned above we need the * correct seqnum */ g_assert_cmpint (11, ==, (guint) dee_serializable_model_get_seqnum (model)); g_assert (before_begin_seqnum == after_begin_seqnum); g_assert (before_end_seqnum == after_end_seqnum); g_assert_cmpint (3, ==, (guint) before_begin_seqnum); g_assert_cmpint (11, ==, (guint) before_end_seqnum); gtx_assert_last_unref (model); g_slist_free (added); return 0; } dee-1.2.7+15.04.20150304/tests/model-helper-remove3rows.c0000644000015300001610000000657012475676210023003 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include static void _row_removed (DeeModel *model, DeeModelIter *iter, GSList **changes_so_far) { guint pos; pos = dee_model_get_position (model, iter); /* Yes, I _know_ that append() is slow, but this is a test! */ *changes_so_far = g_slist_append (*changes_so_far, GUINT_TO_POINTER (pos)); } /* Expects a clone with 5 rows in it. Rows 0, 4, and 2 will be removed remotely, * in that order. Note that removed a row shifts the order of those below it, * so we expect to see removals of rows 0, 3, and 1, when correcting for row * shifts. * * This should leave the original rows 1 and 3, now in positions 0, and 1. */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter, *orig_iter1, *orig_iter3; GSList *changes; guint pos; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 5); /* We should end up with the original rows 1 and 3, so * save pointers to those rows */ orig_iter1 = dee_model_get_iter_at_row (model, 1); orig_iter3 = dee_model_get_iter_at_row (model, 3); /* Listen for changes */ changes = NULL; g_signal_connect (model, "row-removed", G_CALLBACK (_row_removed), &changes); /* Wait for some RowsRmoved signals */ gtx_yield_main_loop (1000); /* Check that we got what we expected */ g_assert_cmpint (g_slist_length (changes), == , 3); g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); pos = GPOINTER_TO_UINT (g_slist_nth (changes, 0)->data); g_assert_cmpint (pos, == , 0); pos = GPOINTER_TO_UINT (g_slist_nth (changes, 1)->data); g_assert_cmpint (pos, == , 3); pos = GPOINTER_TO_UINT (g_slist_nth (changes, 2)->data); g_assert_cmpint (pos, == , 1); /* Now assert that the model contains the data from the original * rows 1 and 3 */ iter = dee_model_get_iter_at_row (model, 0); if (orig_iter1 != iter) g_error ("Expected original row 1 on position 0. Found row with data '%s'", dee_model_get_string (model, iter, 1)); iter = dee_model_get_iter_at_row (model, 1); if (orig_iter3 != iter) g_error ("Expected original row 3 on position 1. Found row with data '%s'", dee_model_get_string (model, iter, 1)); gtx_assert_last_unref (model); g_slist_free (changes); return 0; } dee-1.2.7+15.04.20150304/tests/test-benchmark.c0000644000015300001610000004233612475676210021044 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include #include typedef struct _Benchmark Benchmark; typedef void (*BenchmarkFunc) (Benchmark *benchmark); typedef void (*BenchmarkSetup) (Benchmark *benchmark); typedef void (*BenchmarkTeardown) (Benchmark *benchmark); typedef struct { gdouble elapsed; } RunData; struct _Benchmark { const gchar *name; BenchmarkSetup benchmark_setup; BenchmarkFunc benchmark_func; BenchmarkTeardown benchmark_teardown; guint n_runs; RunData *runs; gpointer state; }; static GList *benchmarks = NULL; static void add_benchmark (Benchmark *benchmark) { benchmarks = g_list_append (benchmarks, benchmark); } static void run_benchmark (Benchmark *bench) { GTimer *timer; guint i; gdouble total_runtime, avg_runtime, std_dev, coeff_of_var; const gchar *coeff_of_var_msg; bench->runs = g_new0 (RunData, bench->n_runs + 1); timer = g_timer_new (); total_runtime = 0; bench->benchmark_setup (bench); g_printf ("=== %s ===\n", bench->name); for (i = 0; i < bench->n_runs; i++) { g_timer_start (timer); bench->benchmark_func (bench); bench->runs[i].elapsed = g_timer_elapsed (timer, NULL); total_runtime += bench->runs[i].elapsed; /* Some benchmarks will reset their own state */ if (bench->state == NULL) bench->benchmark_setup (bench); } /* Compute average runtime */ avg_runtime = total_runtime / bench->n_runs; /* Compute standard deviation */ std_dev = 0; for (i = 0; i < bench->n_runs; i++) { gdouble delta = bench->runs[i].elapsed - avg_runtime; std_dev += delta * delta; } std_dev = sqrt (std_dev / bench->n_runs); coeff_of_var = (std_dev /avg_runtime) * 100; /* This is not a very precise way of judging the quality, * but better than rigorous hand waving */ if (coeff_of_var < 1.0) coeff_of_var_msg = "super!"; else if (coeff_of_var < 5) coeff_of_var_msg = "good"; else if (coeff_of_var < 10) coeff_of_var_msg = "acceptable"; else coeff_of_var_msg = "rejected!"; /* Print report */ g_printf ("Runs : %u\n", bench->n_runs); g_printf ("Total runtime : %fs\n", total_runtime); g_printf ("Avg. runtime : %fs\n", avg_runtime); g_printf ("Std. deviation : %fs\n", std_dev); g_printf ("Accuracy : %f%% [%s]\n", (100 - coeff_of_var), coeff_of_var_msg); g_printf ("\n"); g_timer_destroy (timer); bench->benchmark_teardown (bench); // purposely leak bench->runs. Caller may want it } static void run_benchmarks (gchar **prefixes) { GList *iter; gchar **prefix; for (iter = benchmarks; iter; iter = iter->next) { Benchmark *bench = (Benchmark*) iter->data; if (prefixes == NULL) run_benchmark (bench); else { for (prefix = prefixes; *prefix; prefix++) { if (g_str_has_prefix (bench->name, *prefix)) { run_benchmark (bench); } } } } } static void bench_seqmodel_setup (Benchmark *bench) { DeeModel *model; model = dee_sequence_model_new (); dee_model_set_schema (model, "s", "s", "s", "u", "b", NULL); bench->state = model; } static void bench_seqmodel_named_setup (Benchmark *bench) { bench_seqmodel_setup (bench); dee_model_set_column_names (bench->state, "string1", "string2", "string3", "count", "bool", NULL); } static void bench_filter_model_collator_setup (Benchmark *bench) { DeeModel *fmodel, *base_model; DeeFilter filter; base_model = dee_sequence_model_new (); dee_model_set_schema (base_model, "s", "s", "s", "u", "b", NULL); dee_filter_new_collator (0, &filter); fmodel = dee_filter_model_new (base_model, &filter); g_object_unref (base_model); bench->state = fmodel; } static void bench_filter_model_collator_desc_setup (Benchmark *bench) { DeeModel *fmodel, *base_model; DeeFilter filter; base_model = dee_sequence_model_new (); dee_model_set_schema (base_model, "s", "s", "s", "u", "b", NULL); dee_filter_new_collator_desc (0, &filter); fmodel = dee_filter_model_new (base_model, &filter); g_object_unref (base_model); bench->state = fmodel; } static gint _cmp_uint (GVariant **row1, GVariant **row2, gpointer user_data) { return g_variant_get_uint32 (row1[3]) - g_variant_get_uint32 (row2[3]); } static void bench_filter_model_sort_uint_setup (Benchmark *bench) { DeeModel *fmodel, *base_model; DeeFilter filter; base_model = dee_sequence_model_new (); dee_model_set_schema (base_model, "s", "s", "s", "u", "b", NULL); dee_filter_new_sort(_cmp_uint, NULL, NULL, &filter); fmodel = dee_filter_model_new (base_model, &filter); g_object_unref (base_model); bench->state = fmodel; } static void bench_seqmodel_read_string_setup (Benchmark *bench) { DeeModel *model; guint limit = 25000, i; model = dee_sequence_model_new (); dee_model_set_schema (model, "s", "s", "s", "u", "b", NULL); for (i = 0; i < limit; i++) { gchar *random_string = g_strdup_printf ("%"G_GINT32_FORMAT, g_test_rand_int()); dee_model_prepend (model, random_string, "Hello world", "!", 42, TRUE); g_free (random_string); } bench->state = model; } static void bench_index_setup (Benchmark *bench) { DeeModel *model; DeeIndex *index; DeeModelReader reader; guint limit = 25000, i; model = dee_sequence_model_new (); dee_model_reader_new_for_string_column (0, &reader); dee_model_set_schema (model, "s", "s", "s", "u", "b", NULL); index = DEE_INDEX (dee_tree_index_new (model, dee_analyzer_new (), &reader)); for (i = 0; i < limit; i++) { gchar *random_string = g_strdup_printf ("%"G_GINT32_FORMAT, g_test_rand_int()); dee_model_prepend (model, random_string, "Hello world", "!", 42, TRUE); g_free (random_string); } bench->state = index; } static void bench_model_append_run (Benchmark *bench) { DeeModel *model; guint limit = 2500, i; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); for (i = 0; i < limit; i++) { gchar *random_string = g_strdup_printf ("%"G_GINT32_FORMAT, g_test_rand_int ()); dee_model_append (model, random_string, "Hello world", "!", (guint32) g_test_rand_int (), TRUE); g_free (random_string); } } static void bench_model_named_append_run (Benchmark *bench) { DeeModel *model; GVariant **row_buf; guint n_cols, i; guint limit = 2500; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); n_cols = dee_model_get_n_columns (model); row_buf = g_new0 (GVariant*, n_cols + 1); for (i = 0; i < limit; i++) { gchar *random_string = g_strdup_printf ("%"G_GINT32_FORMAT, g_test_rand_int ()); dee_model_build_named_row (model, row_buf, "count", (guint32) g_test_rand_int (), "string1", random_string, "string2", "Hello world", "string3", "!", "bool", TRUE, NULL); dee_model_append_row (model, row_buf); g_free (random_string); } } static void bench_model_prepend_run (Benchmark *bench) { DeeModel *model; guint32 limit = 2500, i; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); for (i = 0; i < limit; i++) { gchar *random_string = g_strdup_printf ("%"G_GINT32_FORMAT, g_test_rand_int ()); dee_model_prepend (model, random_string, "Hello world", "!", (guint32)g_test_rand_int (), TRUE); g_free (random_string); } } static void bench_model_sorted_run (Benchmark *bench) { DeeModel *model; guint32 limit = 2500, i; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); for (i = 0; i < limit; i++) { gchar *random_string = g_strdup_printf ("%"G_GINT32_FORMAT, g_test_rand_int ()); dee_model_insert_sorted (model, _cmp_uint, NULL, random_string, "Hello world", "!", (guint32)g_test_rand_int (), TRUE); g_free (random_string); } } static void bench_model_read_string_run (Benchmark *bench) { DeeModel *model; DeeModelIter *iter, *end; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); for (iter = dee_model_get_first_iter (model), end = dee_model_get_last_iter (model); iter != end; iter = dee_model_next (model, iter)) { dee_model_get_string (model, iter, 0); } } static void bench_model_read_row_run (Benchmark *bench) { DeeModel *model; DeeModelIter *iter, *end; GVariant **row_buf; guint n_cols, i; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); n_cols = dee_model_get_n_columns (model); row_buf = g_new0 (GVariant*, n_cols + 1); for (iter = dee_model_get_first_iter (model), end = dee_model_get_last_iter (model); iter != end; iter = dee_model_next (model, iter)) { dee_model_get_row (model, iter, row_buf); for (i = 0; i < n_cols; i++) g_variant_unref (row_buf[i]); } g_free (row_buf); } static void bench_model_clear_run (Benchmark *bench) { DeeModel *model; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); dee_model_clear (model); g_assert (dee_model_get_n_rows (model) == 0); /* Force a re-run of the setup func */ bench->benchmark_teardown (bench); } static void bench_model_walk_next_run (Benchmark *bench) { DeeModel *model; DeeModelIter *iter, *end; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); for (iter = dee_model_get_first_iter (model), end = dee_model_get_last_iter (model); iter != end; iter = dee_model_next (model, iter)) { } } static void bench_model_walk_pos_run (Benchmark *bench) { DeeModel *model; guint n_rows, i; g_assert (DEE_IS_MODEL (bench->state)); model = DEE_MODEL (bench->state); n_rows = dee_model_get_n_rows (model); for (i = 0; i < n_rows; i++) { dee_model_get_iter_at_row (model, i); } } static void bench_index_prefix_search (Benchmark *bench) { DeeIndex *index; guint i; gchar *prefixes[] = { "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", "1", "2", "3", "4", "5", "6", "7", "8", "9", }; g_assert (DEE_IS_INDEX (bench->state)); index = DEE_INDEX (bench->state); for (i = 0; i < G_N_ELEMENTS (prefixes); i++) { DeeResultSet *rs = dee_index_lookup (index, prefixes[i], DEE_TERM_MATCH_PREFIX); g_assert_cmpuint (dee_result_set_get_n_rows (rs), >, 0); g_object_unref (G_OBJECT (rs)); } } static void bench_gobject_teardown (Benchmark *bench) { GObject *obj; if (bench->state) { obj = G_OBJECT (bench->state); g_object_unref (obj); bench->state = NULL; } } Benchmark seqmodel_append = { "SequenceModel.append", bench_seqmodel_setup, bench_model_append_run, bench_gobject_teardown, 100, NULL }; Benchmark seqmodel_named_append = { "SequenceModel.append.named", bench_seqmodel_named_setup, bench_model_named_append_run, bench_gobject_teardown, 100, NULL }; Benchmark seqmodel_prepend = { "SequenceModel.prepend", bench_seqmodel_setup, bench_model_prepend_run, bench_gobject_teardown, 100, NULL }; Benchmark seqmodel_sorted = { "SequenceModel.sorted", bench_seqmodel_setup, bench_model_sorted_run, bench_gobject_teardown, 100, NULL }; Benchmark seqmodel_read_string = { "SequenceModel.read_string", bench_seqmodel_read_string_setup, bench_model_read_string_run, bench_gobject_teardown, 100, NULL }; Benchmark seqmodel_read_row = { "SequenceModel.read_row", bench_seqmodel_read_string_setup, bench_model_read_row_run, bench_gobject_teardown, 100, NULL }; Benchmark seqmodel_clear = { "SequenceModel.clear", bench_seqmodel_read_string_setup, bench_model_clear_run, bench_gobject_teardown, 20, NULL }; Benchmark seqmodel_walk_next = { "SequenceModel.walk_next", bench_seqmodel_read_string_setup, bench_model_walk_next_run, bench_gobject_teardown, 1000, NULL }; Benchmark seqmodel_walk_pos = { "SequenceModel.walk_pos", bench_seqmodel_read_string_setup, bench_model_walk_pos_run, bench_gobject_teardown, 1000, NULL }; Benchmark filtermodel_collate = { "FilterModel.collate", bench_filter_model_collator_setup, bench_model_prepend_run, bench_gobject_teardown, 100, NULL }; Benchmark filtermodel_collate_desc = { "FilterModel.collate_desc", bench_filter_model_collator_desc_setup, bench_model_prepend_run, bench_gobject_teardown, 100, NULL }; Benchmark filtermodel_sort_uint = { "FilterModel.sort_uint", bench_filter_model_sort_uint_setup, bench_model_prepend_run, bench_gobject_teardown, 100, NULL }; Benchmark tree_index_prefix_search = { "TreeIndex.prefix_search", bench_index_setup, bench_index_prefix_search, bench_gobject_teardown, 25, NULL }; /* Arguments are interpreted as prefixes that benchmark names must match * in order to be run */ gint main (gint argc, gchar *argv[]) { #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif g_test_init (&argc, &argv, NULL); /* Extract NULL terminated array of prefixes from arguments */ int i; gchar **prefixes = g_new0 (gchar*, argc); for (i = 1; i < argc; i++) { prefixes[i-1] = argv[i]; } add_benchmark (&seqmodel_append); add_benchmark (&seqmodel_named_append); add_benchmark (&seqmodel_prepend); add_benchmark (&seqmodel_sorted); add_benchmark (&seqmodel_read_string); add_benchmark (&seqmodel_read_row); add_benchmark (&seqmodel_clear); add_benchmark (&seqmodel_walk_next); add_benchmark (&seqmodel_walk_pos); add_benchmark (&filtermodel_collate); add_benchmark (&filtermodel_collate_desc); add_benchmark (&filtermodel_sort_uint); add_benchmark (&tree_index_prefix_search); run_benchmarks (argc > 1 ? prefixes : NULL); g_free (prefixes); return 0; } dee-1.2.7+15.04.20150304/tests/model-helper-schemaless.c0000644000015300001610000000342712475676210022635 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include /* Joins an existing model, and then tries to define the column types, * and add two rows with these types */ gint main (gint argc, gchar *argv[]) { DeeModel *model; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); /* Important: In this test it's the *slave* that sets the schema, * the leader is schemaless at this point in time, and will * pick up the schema from the initial transaction from the slave */ dee_model_set_schema (model, "i", "s", NULL); if (gtx_wait_for_signal (G_OBJECT (model), 300, "notify::synchronized", NULL)) { g_critical ("Model never synchronized"); return 1; } dee_model_append (model, 27, "skunkworks"); dee_model_append (model, 68, "wumbo"); gtx_yield_main_loop (500); gtx_assert_last_unref (model); return 0; } dee-1.2.7+15.04.20150304/tests/model-helper-clone3rows.c0000644000015300001610000000440112475676210022575 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include static void row_added (DeeModel *model, DeeModelIter *iter, gpointer data) { gint *num_added = (gint*) data; (*num_added)++; } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; gint num_added; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif g_set_prgname ("model-helper"); if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); num_added = 0; g_signal_connect (model, "row-added", G_CALLBACK (row_added), &num_added); if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); iter = dee_model_get_iter_at_row (model, 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "zero"); iter = dee_model_get_iter_at_row (model, 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "one"); iter = dee_model_get_iter_at_row (model, 2); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "two"); gtx_assert_last_unref (model); g_assert_cmpint (num_added, ==, 3); return 0; } dee-1.2.7+15.04.20150304/tests/test-dee.c0000644000015300001610000000540012475676210017636 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by Neil Jagdish Patel * */ #include "config.h" #include #include #include void test_model_column_create_suite (void); void test_model_complex_column_create_suite (void); void test_model_rows_create_suite (void); void test_model_signals_create_suite (void); void test_model_seqnums_create_suite (void); void test_model_tags_create_suite (void); void test_filter_model_create_suite (void); void test_term_list_create_suite (void); void test_hash_index_create_suite (void); void test_analyzer_create_suite (void); void test_model_readers_create_suite (void); void test_glist_result_set_create_suite (void); void test_serializable_create_suite (void); void test_resource_manager_create_suite (void); void test_transaction_create_suite (void); #ifdef HAVE_GTX void test_model_interactions_create_suite(void); void test_peer_interactions_create_suite(void); void test_client_server_interactions_create_suite(void); #endif /* HAVE_GTX */ #ifdef HAVE_ICU void test_icu_create_suite(void); #endif /* HAVE_ICU */ gint main (gint argc, gchar *argv[]) { #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif g_test_init (&argc, &argv, NULL); setlocale (LC_ALL, ""); test_model_column_create_suite (); test_model_complex_column_create_suite (); test_model_rows_create_suite (); test_model_signals_create_suite (); test_model_seqnums_create_suite (); test_model_tags_create_suite (); test_filter_model_create_suite (); test_term_list_create_suite (); test_hash_index_create_suite (); test_analyzer_create_suite (); test_model_readers_create_suite (); test_glist_result_set_create_suite (); test_serializable_create_suite (); test_resource_manager_create_suite (); test_transaction_create_suite (); #ifdef HAVE_GTX test_model_interactions_create_suite(); test_peer_interactions_create_suite(); test_client_server_interactions_create_suite(); #else g_message ("Interactions' test suite disabled. GTX not found. You can download GTX from https://launchpad.net/gtx"); #endif /* HAVE_GTX */ #ifdef HAVE_ICU test_icu_create_suite (); #endif return g_test_run (); } dee-1.2.7+15.04.20150304/tests/test-peer-interactions.c0000644000015300001610000000646112475676210022544 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by Mikkel Kamstrup Erlandsen * */ #include #include #include #include #define TIMEOUT 100 #define PEER_NAME "com.canonical.Dee.Peer.Tests.Interactions" /* A command line that launches the appropriate peer-helper-* executable, * giving $name as first argument */ #define PEER_HELPER(helper,name) \ (gchar *[]) { "./peer-helper-"#helper, name, NULL } typedef struct { DeePeer *peer; } Fixture; static void peer_setup (Fixture *fix, gconstpointer data); static void peer_teardown (Fixture *fix, gconstpointer data); static void test_allocation (Fixture *fix, gconstpointer data); static void test_become_leader (Fixture *fix, gconstpointer data); static void test_1peer (Fixture *fix, gconstpointer data); void test_peer_interactions_create_suite (void) { #define DOMAIN "/Peer/Interactions" g_test_add (DOMAIN"/Allocation", Fixture, 0, peer_setup, test_allocation, peer_teardown); g_test_add (DOMAIN"/BecomeLeader", Fixture, 0, peer_setup, test_become_leader, peer_teardown); g_test_add (DOMAIN"/OnePeer", Fixture, 0, peer_setup, test_1peer, peer_teardown); } static void peer_setup (Fixture *fix, gconstpointer data) { fix->peer = dee_peer_new (PEER_NAME); g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (fix->peer)); g_assert (DEE_IS_PEER (fix->peer)); } static void peer_teardown (Fixture *fix, gconstpointer data) { gtx_assert_last_unref (fix->peer); /* Spin the mainloop a bit to check if we have any post-test * async effect crashing us */ gtx_yield_main_loop (200); } static void test_allocation (Fixture *fix, gconstpointer data) { /* Do nothing, this test basically just asserts that * the fix->peer is cleaned up after immediate construction */ } static void test_become_leader (Fixture *fix, gconstpointer data) { gtx_wait_for_signal (G_OBJECT (fix->peer), TIMEOUT, "notify::swarm-leader", NULL); /* Assert that we have become swarm leaders. * No other peers should be running */ g_assert_cmpint (0, !=, dee_peer_is_swarm_leader (fix->peer)); } static void test_1peer (Fixture *fix, gconstpointer data) { /* Wait for us to become swarm leaders */ gtx_wait_for_signal (G_OBJECT (fix->peer), TIMEOUT, "notify::swarm-leader", NULL); g_assert_cmpint (0, !=, dee_peer_is_swarm_leader (fix->peer)); /* We are now leaders - launch the helper */ if (gtx_wait_for_command (TESTDIR, PEER_HELPER (1peer, PEER_NAME), 1000)) g_critical ("Peer helper timed out"); gtx_assert_last_command_status (0); } dee-1.2.7+15.04.20150304/tests/model-helper-introspect.c0000644000015300001610000000616112475676210022676 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include #include static gchar* build_model_path (const gchar *model_name) { gchar *dum = g_strdup (model_name); gchar *path; path = g_strconcat ("/com/canonical/dee/model/", g_strdelimit (dum, ".", '/'), NULL); g_free (dum); return path; } /* Does DBus introspection on a remote DeeModel */ gint main (gint argc, gchar *argv[]) { GDBusConnection *conn; gchar *model_path; gchar *introspection_data; GError *error; GVariant *introspection_value; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif error = NULL; conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (error != NULL) { g_critical ("Unable to connect to session bus: %s", error->message); g_error_free (error); return 1; } model_path = build_model_path (argv[1]); error = NULL; introspection_value = g_dbus_connection_call_sync (conn, argv[1], /* name */ model_path, /* obj path */ "org.freedesktop.DBus.Introspectable", "Introspect", /* member */ NULL, /* arguments */ G_VARIANT_TYPE ("(s)"), /* repl type*/ G_DBUS_CALL_FLAGS_NONE, -1, /* timeout */ NULL, /* cancel */ &error); if (error != NULL) { g_critical ("Unable to get introspection data: %s", error->message); g_error_free (error); return 2; } if (introspection_value == NULL) { g_critical ("Introspection data was NULL"); return 3; } g_variant_get_child (introspection_value, 0, "s", &introspection_data); if (strstr (introspection_data, "Clone") == NULL) { g_critical ("Introspection data does not declare Clone method:\n%s", introspection_data); return 4; } g_variant_unref (introspection_value); g_free (introspection_data); g_free (model_path); g_object_unref (conn); return 0; } dee-1.2.7+15.04.20150304/tests/test-transaction.c0000644000015300001610000012002212475676210021424 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include typedef struct { DeeModel *txn; DeeModel *model; } Fixture; static void setup (Fixture *fix, gconstpointer data); static void setup_proxy (Fixture *fix, gconstpointer data); static void setup_shared (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup (Fixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", NULL); /* The txn must be created during the tests because we need to verify how * it works when constructed on top of various states of the fix->model */ } static void setup_basic_types (Fixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "b", "y", "i", "u", "x", "t", "d", "s", NULL); /* The txn must be created during the tests because we need to verify how * it works when constructed on top of various states of the fix->model */ } static void setup_proxy (Fixture *fix, gconstpointer data) { DeeModel *backend = dee_sequence_model_new (); dee_model_set_schema (backend, "s", "i", NULL); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", backend, NULL); /* The txn must be created during the tests because we need to verify how * it works when constructed on top of various states of the fix->model */ g_object_unref (backend); } static void setup_shared (Fixture *fix, gconstpointer data) { fix->model = dee_shared_model_new ("my.test.Model"); dee_model_set_schema (fix->model, "s", "i", NULL); /* The txn must be created during the tests because we need to verify how * it works when constructed on top of various states of the fix->model */ } static void teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->model); if (fix->txn) g_object_unref (fix->txn); } static void test_adopt_schema (Fixture *fix, gconstpointer data) { fix->txn = dee_transaction_new (fix->model); g_assert_cmpint (dee_model_get_n_columns (fix->txn), == , 2); g_assert_cmpstr (dee_model_get_column_schema (fix->txn, 0), ==, "s"); g_assert_cmpstr (dee_model_get_column_schema (fix->txn, 1), ==, "i"); g_assert (dee_transaction_get_target (DEE_TRANSACTION (fix->txn)) == fix->model); } static void test_target_0_add_1 (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *txn_iter; gchar *s; int i; GError *error; /** * Target: * Txn: I */ fix->txn = dee_transaction_new (fix->model); /* Add one row to txn */ txn_iter = dee_model_append (fix->txn, "I", 1); g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->txn)); dee_model_get (fix->txn, txn_iter, &s, &i); g_assert_cmpstr ("I", ==, s); g_assert_cmpint (1, ==, i); /* Commit and verify target */ g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } iter = dee_model_get_first_iter (fix->model); g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->model)); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("I", ==, s); g_assert_cmpint (1, ==, i); } static void test_target_1_add_1 (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *txn_iter, *txn_end_iter; gchar *s; int i; GError *error; /** * Target: A * Txn: A, I */ /* The point of this test is that with 1 row in the target and 1 new row * in the txn we can easily test out the behaviour when stepping back and * forth over old and new rows in the txn */ iter = dee_model_append (fix->model, "TwentySeven", 27); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); txn_iter = dee_model_get_first_iter (fix->txn); g_assert (txn_iter == iter); /* Append a row to txn and assert that txn and target are both as expected */ txn_iter = dee_model_append (fix->txn, "Append", 7); g_assert (txn_iter != iter); /* Orig row is unmodified */ dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); /* New row is in txn */ g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->txn)); g_assert (txn_iter == dee_model_next (fix->txn, iter)); g_assert (iter == dee_model_get_first_iter (fix->txn)); g_assert (dee_model_is_last (fix->txn, dee_model_next (fix->txn, txn_iter))); dee_model_get (fix->txn, txn_iter, &s, &i); g_assert_cmpstr ("Append", ==, s); g_assert_cmpint (7, ==, i); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); /* New row not in target */ g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); /* txn end iter is shared with target */ txn_end_iter = dee_model_get_last_iter (fix->txn); g_assert (txn_end_iter == dee_model_get_last_iter (fix->model)); /* Check that we can step backwards */ g_assert (txn_iter == dee_model_prev (fix->txn, txn_end_iter)); g_assert (iter == dee_model_prev (fix->txn, txn_iter)); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("Append", ==, s); g_assert_cmpint (7, ==, i); } static void test_target_1_change_1 (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *txn_iter, *txn_end_iter; gchar *s; int i; GError *error; /* * Target: A * Txn: A' */ iter = dee_model_append (fix->model, "TwentySeven", 27); /* Historically the pristine target seqmodel has had a bug here, * make sure we don't loose sleep over it ;-) */ g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (iter != dee_model_get_last_iter (fix->model)); g_assert (!dee_model_is_last (fix->model, iter)); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); txn_iter = dee_model_get_first_iter (fix->txn); g_assert (txn_iter == iter); /* Change the row in txn and assert that it looks right */ dee_model_set (fix->txn, iter, "TwentyOne", 21); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); g_assert (iter == dee_model_get_first_iter (fix->txn)); /* Change is not seen in target */ g_assert_cmpint (dee_model_get_n_rows (fix->model), == , 1); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); g_assert (iter == dee_model_get_first_iter (fix->model)); /* We can step to the end iter, and it is the same as that of the target */ txn_end_iter = dee_model_get_last_iter (fix->txn); g_assert (dee_model_is_last (fix->txn, txn_end_iter)); g_assert (txn_end_iter == dee_model_next (fix->txn, iter)); g_assert (txn_end_iter == dee_model_get_last_iter (fix->model)); /* We can step back from the end iter to the changed row */ g_assert (iter == dee_model_prev (fix->txn, txn_end_iter)); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); } static void test_target_2_add_3 (Fixture *fix, gconstpointer data) { DeeModelIter *iter0, *iter1, *txn_iter, *txn_middle, *end; gchar *s; int i; GError *error; /* Target: A, B * Txn: I, A, II, B, III * */ iter0 = dee_model_append (fix->model, "A", 0); iter1 = dee_model_append (fix->model, "B", 1); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); txn_iter = dee_model_get_first_iter (fix->txn); g_assert (txn_iter == iter0); dee_model_get (fix->txn, txn_iter, &s, &i); g_assert_cmpstr ("A", ==, s); g_assert_cmpint (0, ==, i); txn_iter = dee_model_next (fix->txn, txn_iter); g_assert (txn_iter == iter1); dee_model_get (fix->txn, txn_iter, &s, &i); g_assert_cmpstr ("B", ==, s); g_assert_cmpint (1, ==, i); /* Assert end iters are the same */ end = dee_model_get_last_iter (fix->txn); g_assert (end == dee_model_get_last_iter (fix->model)); /* Prepend one row to txn and * assert that txn and target are both as expected */ txn_iter = dee_model_prepend (fix->txn, "I", 11); g_assert (txn_iter != iter0 && txn_iter != iter1 && txn_iter != end); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->txn)); dee_model_get (fix->txn, txn_iter, &s, &i); g_assert_cmpstr ("I", ==, s); g_assert_cmpint (11, ==, i); g_assert (iter0 == dee_model_next (fix->txn, txn_iter)); g_assert (iter1 == dee_model_next (fix->txn, iter0)); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); /* Append one row to txn */ txn_iter = dee_model_append (fix->txn, "III", 13); g_assert (txn_iter != iter0 && txn_iter != iter1 && txn_iter != end); g_assert_cmpint (4, ==, dee_model_get_n_rows (fix->txn)); dee_model_get (fix->txn, txn_iter, &s, &i); g_assert_cmpstr ("III", ==, s); g_assert_cmpint (13, ==, i); g_assert (end == dee_model_next (fix->txn, txn_iter)); g_assert (txn_iter == dee_model_next (fix->txn, iter1)); g_assert (iter1 == dee_model_next (fix->txn, iter0)); g_assert (iter0 == dee_model_next (fix->txn, dee_model_get_first_iter (fix->txn))); g_assert (txn_iter == dee_model_prev (fix->txn, end)); g_assert (iter1 == dee_model_prev (fix->txn, txn_iter)); g_assert (iter0 == dee_model_prev (fix->txn, iter1)); g_assert (dee_model_is_first (fix->txn, dee_model_prev (fix->txn, iter0))); g_assert (dee_model_is_last (fix->txn, end)); /* Insert one row in the middle of txn */ txn_middle = dee_model_insert_before (fix->txn, iter1, "II", 12); g_assert (txn_middle != iter0 && txn_middle != iter1 && txn_middle != end); g_assert_cmpint (5, ==, dee_model_get_n_rows (fix->txn)); dee_model_get (fix->txn, txn_middle, &s, &i); g_assert_cmpstr ("II", ==, s); g_assert_cmpint (12, ==, i); g_assert (iter0 == dee_model_prev (fix->txn, txn_middle)); g_assert (iter1 == dee_model_next (fix->txn, txn_middle)); g_assert (txn_middle == dee_model_prev (fix->txn, iter1)); g_assert (txn_middle == dee_model_next (fix->txn, iter0)); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert (dee_transaction_is_committed (DEE_TRANSACTION (fix->txn))); g_assert_cmpint (5, ==, dee_model_get_n_rows (fix->model)); iter0 = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter0, &s, &i); g_assert_cmpstr ("I", ==, s); g_assert_cmpint (11, ==, i); iter0 = dee_model_next (fix->model, iter0); dee_model_get (fix->model, iter0, &s, &i); g_assert_cmpstr ("A", ==, s); g_assert_cmpint (0, ==, i); iter0 = dee_model_next (fix->model, iter0); dee_model_get (fix->model, iter0, &s, &i); g_assert_cmpstr ("II", ==, s); g_assert_cmpint (12, ==, i); iter0 = dee_model_next (fix->model, iter0); dee_model_get (fix->model, iter0, &s, &i); g_assert_cmpstr ("B", ==, s); g_assert_cmpint (1, ==, i); iter0 = dee_model_next (fix->model, iter0); dee_model_get (fix->model, iter0, &s, &i); g_assert_cmpstr ("III", ==, s); g_assert_cmpint (13, ==, i); g_assert (end == dee_model_next (fix->model, iter0)); } static void test_target_2_clear (Fixture *fix, gconstpointer data) { GError *error; /** * Target: A B * Txn: - - */ dee_model_append (fix->model, "TwentySeven", 27); dee_model_append (fix->model, "TwentyEight", 28); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); /* Clear txn */ dee_model_clear (fix->txn); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 0); /* Target is unmodified */ g_assert_cmpint (dee_model_get_n_rows (fix->model), == , 2); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert (dee_transaction_is_committed (DEE_TRANSACTION (fix->txn))); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); } static void test_target_1_clear_add_3 (Fixture *fix, gconstpointer data) { DeeModelIter *iter; GError *error; gchar *s; gint32 i; /** * Target: A B * Txn: - - I II III */ dee_model_append (fix->model, "TwentySeven", 27); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); /* Clear txn */ dee_model_clear (fix->txn); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 0); /* Target is unmodified */ g_assert_cmpint (dee_model_get_n_rows (fix->model), == , 1); /* Add rows to txn, funky order for fun */ dee_model_append (fix->txn, "II", 2); dee_model_append (fix->txn, "III", 3); dee_model_prepend (fix->txn, "I", 1); /* Txn looks like we expect before commit */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 3); iter = dee_model_get_first_iter (fix->txn); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("I", ==, s); g_assert_cmpint (1, ==, i); iter = dee_model_next (fix->txn, iter); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("II", ==, s); g_assert_cmpint (2, ==, i); iter = dee_model_next (fix->txn, iter); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("III", ==, s); g_assert_cmpint (3, ==, i); g_assert (dee_model_is_last (fix->txn, dee_model_next (fix->txn, iter))); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert (dee_transaction_is_committed (DEE_TRANSACTION (fix->txn))); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("I", ==, s); g_assert_cmpint (1, ==, i); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("II", ==, s); g_assert_cmpint (2, ==, i); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("III", ==, s); g_assert_cmpint (3, ==, i); g_assert (dee_model_is_last (fix->model, dee_model_next (fix->model, iter))); } static void test_target_5_clear_append_2 (Fixture *fix, gconstpointer data) { DeeModelIter *iter; GError *error; gchar *s; gint32 i; /** * Target: A B C D E * Txn: - - - - - F G */ dee_model_append (fix->model, "A", (gint32) 'A'); dee_model_append (fix->model, "B", (gint32) 'B'); dee_model_append (fix->model, "C", (gint32) 'C'); dee_model_append (fix->model, "D", (gint32) 'D'); dee_model_append (fix->model, "E", (gint32) 'E'); fix->txn = dee_transaction_new (fix->model); dee_model_clear (fix->txn); dee_model_append (fix->txn, "F", (gint32) 'F'); dee_model_append (fix->txn, "G", (gint32) 'G'); /* Txn looks like we expect before commit */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); iter = dee_model_get_first_iter (fix->txn); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("F", ==, s); g_assert_cmpint ((gint32) 'F', ==, i); iter = dee_model_next (fix->txn, iter); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("G", ==, s); g_assert_cmpint ((gint32) 'G', ==, i); g_assert (dee_model_is_last (fix->txn, dee_model_next (fix->txn, iter))); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was set to: %s", error->message); } g_assert (dee_transaction_is_committed (DEE_TRANSACTION (fix->txn))); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("F", ==, s); g_assert_cmpint ((gint32) 'F', ==, i); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("G", ==, s); g_assert_cmpint ((gint32) 'G', ==, i); g_assert (dee_model_is_last (fix->model, dee_model_next (fix->model, iter))); } static void test_target_0_clear_append_2 (Fixture *fix, gconstpointer data) { DeeModelIter *iter; GError *error; gchar *s; gint32 i; /** * Target: * Txn: A B */ /* The trick to this is the clear() on an empty model. * Or - hopefully that is not a trick... that is what we test ;-) */ fix->txn = dee_transaction_new (fix->model); dee_model_clear (fix->txn); dee_model_append (fix->txn, "A", (gint32) 'A'); dee_model_append (fix->txn, "B", (gint32) 'B'); /* Txn looks like we expect before commit */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); iter = dee_model_get_first_iter (fix->txn); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("A", ==, s); g_assert_cmpint ((gint32) 'A', ==, i); iter = dee_model_next (fix->txn, iter); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("B", ==, s); g_assert_cmpint ((gint32) 'B', ==, i); g_assert (dee_model_is_last (fix->txn, dee_model_next (fix->txn, iter))); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was set to: %s", error->message); } g_assert (dee_transaction_is_committed (DEE_TRANSACTION (fix->txn))); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("A", ==, s); g_assert_cmpint ((gint32) 'A', ==, i); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("B", ==, s); g_assert_cmpint ((gint32) 'B', ==, i); g_assert (dee_model_is_last (fix->model, dee_model_next (fix->model, iter))); } static void test_target_1_change_1_add_2 (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *txn_iter; gchar *s; int i; GError *error; /* * Target: - A - * Txn: I A' II */ iter = dee_model_append (fix->model, "TwentySeven", 27); g_assert (iter == dee_model_get_first_iter (fix->model)); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); txn_iter = dee_model_get_first_iter (fix->txn); g_assert (txn_iter == iter); /* Change the row in txn and assert that it looks right */ dee_model_set (fix->txn, iter, "TwentyOne", 21); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); g_assert (iter == dee_model_get_first_iter (fix->txn)); /* Change is not seen in target */ g_assert_cmpint (dee_model_get_n_rows (fix->model), == , 1); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); g_assert (iter == dee_model_get_first_iter (fix->model)); /* Add two more rows to txn */ dee_model_append (fix->txn, "ThirtyFour", 34); dee_model_prepend (fix->txn, "Eleven", 11); g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 1); g_assert_cmpint (dee_model_get_n_rows (fix->txn), ==, 3); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); dee_model_get (fix->model, dee_model_get_first_iter (fix->model), &s, &i); g_assert_cmpstr ("Eleven", ==, s); g_assert_cmpint (11, ==, i); dee_model_get (fix->model, dee_model_get_iter_at_row (fix->model, 2), &s, &i); g_assert_cmpstr ("ThirtyFour", ==, s); g_assert_cmpint (34, ==, i); } static void test_target_1_change_1_clear (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *txn_iter; gchar *s; int i; GError *error; /* * Target: A * Txn: - */ iter = dee_model_append (fix->model, "TwentySeven", 27); g_assert (iter == dee_model_get_first_iter (fix->model)); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); txn_iter = dee_model_get_first_iter (fix->txn); g_assert (txn_iter == iter); /* Change the row in txn and assert that it looks right */ dee_model_set (fix->txn, iter, "TwentyOne", 21); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); g_assert (iter == dee_model_get_first_iter (fix->txn)); /* Change is not seen in target */ g_assert_cmpint (dee_model_get_n_rows (fix->model), == , 1); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); g_assert (iter == dee_model_get_first_iter (fix->model)); /* Clear the model */ dee_model_clear (fix->txn); g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 1); g_assert_cmpint (dee_model_get_n_rows (fix->txn), ==, 0); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); } static void test_target_2_change_1_remove_1 (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *txn_iter; gchar *s; int i; GError *error; /* * Target: A B * Txn: - B' */ iter = dee_model_append (fix->model, "TwentySeven", 27); dee_model_prepend (fix->model, "Nineteen", 19); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); txn_iter = dee_model_get_iter_at_row (fix->txn, 1); g_assert (txn_iter == iter); /* Change the last row in txn and assert that it looks right */ dee_model_set (fix->txn, iter, "TwentyOne", 21); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); dee_model_get (fix->txn, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); g_assert (iter == dee_model_get_iter_at_row (fix->txn, 1)); /* Change is not seen in target */ g_assert_cmpint (dee_model_get_n_rows (fix->model), == , 2); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentySeven", ==, s); g_assert_cmpint (27, ==, i); g_assert (iter == dee_model_get_iter_at_row (fix->model, 1)); /* Remove the first row */ dee_model_remove (fix->txn, dee_model_get_first_iter (fix->txn)); g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 2); g_assert_cmpint (dee_model_get_n_rows (fix->txn), ==, 1); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->model)); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr ("TwentyOne", ==, s); g_assert_cmpint (21, ==, i); } static void test_target_2_change_remove_append (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *iter_removed; GError *error; gchar *s; gint32 i; /** * Target: A B * Txn: A' - C */ dee_model_append (fix->model, "TwentySeven", 27); dee_model_append (fix->model, "TwentyEight", 28); fix->txn = dee_transaction_new (fix->model); /* Assert that the unmodified txn is identical to the target */ g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); /* Change the first row */ iter = dee_model_get_first_iter (fix->txn); dee_model_set_value (fix->txn, iter, 0, g_variant_new_string ("***")); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); g_assert (dee_model_is_first (fix->txn, iter)); /* Remove second row */ iter_removed = dee_model_next (fix->txn, iter); dee_model_remove (fix->txn, iter_removed); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 1); g_assert (dee_model_is_first (fix->txn, iter)); /* Append a new row */ dee_model_append (fix->txn, "TehNew", 11); g_assert_cmpint (dee_model_get_n_rows (fix->txn), == , 2); g_assert (dee_model_is_first (fix->txn, iter)); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was was to: %s", error->message); } g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr (s, ==, "***"); g_assert_cmpint (i, ==, 27); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &s, &i); g_assert_cmpstr (s, ==, "TehNew"); g_assert_cmpint (i, ==, 11); } static int txn_remaining_rows = 2; void txn_on_row_added (DeeModel *txn, DeeModelIter *iter) { if (txn_remaining_rows == 2) { g_assert_cmpstr (dee_model_get_string (txn, iter, 0), ==, "A"); g_assert_cmpint (dee_model_get_int32 (txn, iter, 1), ==, (gint32) 'A'); } else if (txn_remaining_rows == 1) { g_assert_cmpstr (dee_model_get_string (txn, iter, 0), ==, "B"); g_assert_cmpint (dee_model_get_int32 (txn, iter, 1), ==, (gint32) 'B'); } else { g_critical ("Unexpected row-added signal on txn with %i remaining rows", txn_remaining_rows); } txn_remaining_rows--; } static int target_remaining_rows = 2; void target_on_row_added (DeeModel *target, DeeModelIter *iter) { if (target_remaining_rows == 2) { g_assert_cmpstr (dee_model_get_string (target, iter, 0), ==, "A"); g_assert_cmpint (dee_model_get_int32 (target, iter, 1), ==, (gint32) 'A'); } else if (target_remaining_rows == 1) { g_assert_cmpstr (dee_model_get_string (target, iter, 0), ==, "B"); g_assert_cmpint (dee_model_get_int32 (target, iter, 1), ==, (gint32) 'B'); } else { g_critical ("Unexpected row-added signal on target with %i remaining rows", target_remaining_rows); } target_remaining_rows--; } static void test_signal_order (Fixture *fix, gconstpointer data) { /* Reset global static state */ txn_remaining_rows = 2; target_remaining_rows = 2; GError *error; /** * Target: * Txn: A B */ fix->txn = dee_transaction_new (fix->model); g_assert_cmpint (txn_remaining_rows, ==, 2); g_assert_cmpint (target_remaining_rows, ==, 2); g_signal_connect (fix->model, "row-added", G_CALLBACK (target_on_row_added), NULL); g_signal_connect (fix->txn, "row-added", G_CALLBACK (txn_on_row_added), NULL); dee_model_append (fix->txn, "A", (gint32) 'A'); g_assert_cmpint (txn_remaining_rows, ==, 1); g_assert_cmpint (target_remaining_rows, ==, 2); dee_model_append (fix->txn, "B", (gint32) 'B'); g_assert_cmpint (txn_remaining_rows, ==, 0); g_assert_cmpint (target_remaining_rows, ==, 2); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit with: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was set to: %s", error->message); } g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpint (txn_remaining_rows, ==, 0); g_assert_cmpint (target_remaining_rows, ==, 0); } static void test_concurrent_modification (Fixture *fix, gconstpointer data) { GError *error; /** * Target: - (add A while txn open) * Txn: I */ fix->txn = dee_transaction_new (fix->model); dee_model_append (fix->model, "TwentySeven", 27); dee_model_clear (fix->txn); /* COMMIT */ error = NULL; if (dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction committed successfully. " "Expected concurrent modification error."); } if (!error) { g_critical ("dee_transaction_commit() returned FALSE, " "but error was not set."); } g_assert (g_error_matches (error, DEE_TRANSACTION_ERROR, DEE_TRANSACTION_ERROR_CONCURRENT_MODIFICATION)); g_assert (!dee_transaction_is_committed (DEE_TRANSACTION (fix->txn))); /* Target model should not have been cleared */ g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 1); } static void test_double_commit (Fixture *fix, gconstpointer data) { GError *error; /** * Target: - (add A while txn open) * Txn: I */ fix->txn = dee_transaction_new (fix->model); dee_model_append (fix->txn, "TwentySeven", 27); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit: %s", error->message); g_error_free (error); } if (error) { g_critical ("dee_transaction_commit() returned TRUE, " "but error was set."); } /* COMMIT.... AGAIN! */ error = NULL; if (dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction committed successfully. " "Expected because of double commit."); } if (!error) { g_critical ("dee_transaction_commit() returned FALSE, " "but error was not set."); } g_assert (g_error_matches (error, DEE_TRANSACTION_ERROR, DEE_TRANSACTION_ERROR_COMMITTED)); /* Target model should be good with 1 row from the first commit */ g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 1); } static void test_basic_types (Fixture *fix, gconstpointer data) { GError *error; fix->txn = dee_transaction_new (fix->model); dee_model_append (fix->txn, TRUE, 27, 28, 29, G_GINT64_CONSTANT (30), G_GUINT64_CONSTANT (31), 32.0, "ThirtyThree"); DeeModelIter *iter = dee_model_get_first_iter (fix->txn); g_assert (dee_model_get_bool (fix->txn, iter, 0) == TRUE); g_assert (dee_model_get_uchar (fix->txn, iter, 1) == 27); g_assert (dee_model_get_int32 (fix->txn, iter, 2) == 28); g_assert (dee_model_get_uint32 (fix->txn, iter, 3) == 29); g_assert (dee_model_get_int64 (fix->txn, iter, 4) == 30); g_assert (dee_model_get_uint64 (fix->txn, iter, 5) == 31); g_assert (ABS (dee_model_get_double (fix->txn, iter, 6) - 32.0) <= 0.001); g_assert_cmpstr ("ThirtyThree", ==, dee_model_get_string (fix->txn, iter, 7)); g_assert_cmpstr ("s", ==, g_variant_get_type_string (dee_model_get_value (fix->txn, iter, 7))); /* COMMIT */ error = NULL; if (!dee_transaction_commit (DEE_TRANSACTION (fix->txn), &error)) { g_critical ("Transaction failed to commit: %s", error->message); g_error_free (error); } if (error) { g_assert_not_reached (); } } // FIXME tags void test_transaction_create_suite (void) { #define DOMAIN "/Model/Transaction" #define PROXY_DOMAIN "/Model/Transaction/Proxy" #define SHARED_DOMAIN "/Model/Transaction/Shared" g_test_add (DOMAIN"/AdoptSchema", Fixture, 0, setup, test_adopt_schema, teardown); g_test_add (PROXY_DOMAIN"/AdoptSchema", Fixture, 0, setup_proxy, test_adopt_schema, teardown); g_test_add (DOMAIN"/Target0Add1", Fixture, 0, setup, test_target_0_add_1, teardown); g_test_add (PROXY_DOMAIN"/Target0Add1", Fixture, 0, setup_proxy, test_target_0_add_1, teardown); g_test_add (DOMAIN"/Target1Add1", Fixture, 0, setup, test_target_1_add_1, teardown); g_test_add (PROXY_DOMAIN"/Target1Add1", Fixture, 0, setup_proxy, test_target_1_add_1, teardown); g_test_add (DOMAIN"/Target1Change1", Fixture, 0, setup, test_target_1_change_1, teardown); g_test_add (PROXY_DOMAIN"/Target1Change1", Fixture, 0, setup_proxy, test_target_1_change_1, teardown); g_test_add (DOMAIN"/Target2Add3", Fixture, 0, setup, test_target_2_add_3, teardown); g_test_add (PROXY_DOMAIN"/Target2Add3", Fixture, 0, setup_proxy, test_target_2_add_3, teardown); g_test_add (DOMAIN"/Target2Clear", Fixture, 0, setup, test_target_2_clear, teardown); g_test_add (PROXY_DOMAIN"/Target2Clear", Fixture, 0, setup_proxy, test_target_2_clear, teardown); g_test_add (DOMAIN"/Target1ClearAdd3", Fixture, 0, setup, test_target_1_clear_add_3, teardown); g_test_add (PROXY_DOMAIN"/Target1ClearAdd3", Fixture, 0, setup_proxy, test_target_1_clear_add_3, teardown); g_test_add (DOMAIN"/Target5ClearAppend2", Fixture, 0, setup, test_target_5_clear_append_2, teardown); g_test_add (PROXY_DOMAIN"/Target5ClearAppend2", Fixture, 0, setup_proxy, test_target_5_clear_append_2, teardown); g_test_add (SHARED_DOMAIN"/Target5ClearAppend2", Fixture, 0, setup_shared, test_target_5_clear_append_2, teardown); g_test_add (DOMAIN"/Target0ClearAppend2", Fixture, 0, setup, test_target_0_clear_append_2, teardown); g_test_add (PROXY_DOMAIN"/Target0ClearAppend2", Fixture, 0, setup_proxy, test_target_0_clear_append_2, teardown); g_test_add (SHARED_DOMAIN"/Target0ClearAppend2", Fixture, 0, setup_shared, test_target_0_clear_append_2, teardown); g_test_add (DOMAIN"/Target1Change1Add2", Fixture, 0, setup, test_target_1_change_1_add_2, teardown); g_test_add (PROXY_DOMAIN"/Target1Change1Add2", Fixture, 0, setup_proxy, test_target_1_change_1_add_2, teardown); g_test_add (SHARED_DOMAIN"/Target1Change1Add2", Fixture, 0, setup_shared, test_target_1_change_1_add_2, teardown); g_test_add (DOMAIN"/Target1Change1Clear", Fixture, 0, setup, test_target_1_change_1_clear, teardown); g_test_add (PROXY_DOMAIN"/Target1Change1Clear", Fixture, 0, setup_proxy, test_target_1_change_1_clear, teardown); g_test_add (SHARED_DOMAIN"/Target1Change1Clear", Fixture, 0, setup_shared, test_target_1_change_1_clear, teardown); g_test_add (DOMAIN"/Target2Change1Remove1", Fixture, 0, setup, test_target_2_change_1_remove_1, teardown); g_test_add (PROXY_DOMAIN"/Target2Change1Remove1", Fixture, 0, setup_proxy, test_target_2_change_1_remove_1, teardown); g_test_add (DOMAIN"/Target2ChangeRemoveAppend", Fixture, 0, setup, test_target_2_change_remove_append, teardown); g_test_add (PROXY_DOMAIN"/Target2ChangeRemoveAppend", Fixture, 0, setup_proxy, test_target_2_change_remove_append, teardown); g_test_add (DOMAIN"/SignalOrder", Fixture, 0, setup, test_signal_order, teardown); g_test_add (PROXY_DOMAIN"/SignalOrder", Fixture, 0, setup_proxy, test_signal_order, teardown); g_test_add (SHARED_DOMAIN"/SignalOrder", Fixture, 0, setup_shared, test_signal_order, teardown); g_test_add (DOMAIN"/ConcurrentModification", Fixture, 0, setup, test_concurrent_modification, teardown); g_test_add (PROXY_DOMAIN"/ConcurrentModification", Fixture, 0, setup_proxy, test_concurrent_modification, teardown); g_test_add (DOMAIN"/DoubleCommit", Fixture, 0, setup, test_double_commit, teardown); g_test_add (PROXY_DOMAIN"/DoubleCommit", Fixture, 0, setup_proxy, test_double_commit, teardown); g_test_add (DOMAIN"/BasicTypes", Fixture, 0, setup_basic_types, test_basic_types, teardown); } dee-1.2.7+15.04.20150304/tests/test-filter-model.c0000644000015300001610000006633412475676210021501 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include typedef struct { DeeModel *model; } FilterFixture; typedef struct { gint first; gint second; } TwoIntsTuple; static void setup (FilterFixture *fix, gconstpointer data); static void setup_empty (FilterFixture *fix, gconstpointer data); static void teardown (FilterFixture *fix, gconstpointer data); static void test_empty_orig (FilterFixture *fix, gconstpointer data); static void test_append_all (FilterFixture *fix, gconstpointer data); static void test_discard_all (FilterFixture *fix, gconstpointer data); static void test_discard_all_append_notify (FilterFixture *fix, gconstpointer data); static void test_change_backend (FilterFixture *fix, gconstpointer data); static void test_collator_asc (FilterFixture *fix, gconstpointer data); static void test_collator_desc (FilterFixture *fix, gconstpointer data); static void test_key (FilterFixture *fix, gconstpointer data); static void test_any (FilterFixture *fix, gconstpointer data); static void test_regex (FilterFixture *fix, gconstpointer data); static void test_changesets (FilterFixture *fix, gconstpointer data); void test_filter_model_create_suite (void) { #define DOMAIN "/Model/Filter" g_test_add (DOMAIN"/EmptyOrig", FilterFixture, 0, setup, test_empty_orig, teardown); g_test_add (DOMAIN"/AppendAll", FilterFixture, 0, setup, test_append_all, teardown); g_test_add (DOMAIN"/DiscardAll", FilterFixture, 0, setup, test_discard_all, teardown); g_test_add (DOMAIN"/DiscardAllAppendNotify", FilterFixture, 0, setup, test_discard_all_append_notify, teardown); g_test_add (DOMAIN"/ChangeBackend", FilterFixture, 0, setup, test_change_backend, teardown); g_test_add (DOMAIN"/CollatorAscending", FilterFixture, 0, setup, test_collator_asc, teardown); g_test_add (DOMAIN"/CollatorDescending", FilterFixture, 0, setup, test_collator_desc, teardown); g_test_add (DOMAIN"/Key", FilterFixture, 0, setup, test_key, teardown); g_test_add (DOMAIN"/Any", FilterFixture, 0, setup, test_any, teardown); g_test_add (DOMAIN"/Regex", FilterFixture, 0, setup, test_regex, teardown); g_test_add (DOMAIN"/Changesets", FilterFixture, 0, setup_empty, test_changesets, teardown); } static void setup_empty (FilterFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "i", "s", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); } static void add_3rows(DeeModel *model) { dee_model_append (model, 0, "Zero"); dee_model_append (model, 1, "One"); dee_model_append (model, 2, "Two"); } static void setup (FilterFixture *fix, gconstpointer data) { setup_empty (fix, data); add_3rows (fix->model); } static void teardown (FilterFixture *fix, gconstpointer data) { g_object_unref (fix->model); fix->model = NULL; } static void discard_all_model_map (DeeModel *orig_model, DeeFilterModel *mapped_model, gpointer user_data) { /* Don't do anything! */ } static void append_all_model_map (DeeModel *orig_model, DeeFilterModel *mapped_model, gpointer user_data) { DeeModelIter *iter; iter = dee_model_get_first_iter (orig_model); while (!dee_model_is_last (orig_model, iter)) { dee_filter_model_append_iter (mapped_model, iter); iter = dee_model_next (orig_model, iter); } } static gboolean discard_all_model_notify (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *mapped_model, gpointer user_data) { /* Do nothing */ return FALSE; } static gboolean append_all_model_notify (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *mapped_model, gpointer user_data) { /* Always say "Thank you, I am delighted", * and append the new row to @mapped_model */ dee_filter_model_append_iter (mapped_model, orig_iter); return TRUE; } static void signal_counter (DeeModel *model, DeeModelIter *iter, guint *count) { *count = *count + 1; } /* Test behaviouor when orig_model is empty */ static void test_empty_orig (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new (append_all_model_map, append_all_model_notify, fix, NULL, &filter); dee_model_clear (fix->model); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); DeeModel *m = dee_filter_model_new (fix->model, &filter); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); DeeModelIter *iter = dee_model_get_first_iter (m); g_assert (dee_model_is_first (m, iter)); g_assert (dee_model_is_last (m, iter)); g_assert_cmpuint (0, ==, dee_serializable_model_get_seqnum (m)); } /* A filter model that is a complete copy of the orig */ static void test_append_all (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new (append_all_model_map, append_all_model_notify, fix, NULL, &filter); DeeModel *m = dee_filter_model_new (fix->model, &filter); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); DeeModelIter *iter = dee_model_get_first_iter (m); g_assert (dee_model_is_first (m, iter)); g_assert (!dee_model_is_last (m, iter)); g_assert_cmpint (0, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, iter, 1)); iter = dee_model_next (m, iter); g_assert (!dee_model_is_first (m, iter)); g_assert (!dee_model_is_last (m, iter)); g_assert_cmpint (1, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("One", ==, dee_model_get_string (m, iter, 1)); g_assert (dee_model_get_first_iter (m) == dee_model_prev (m, iter)); iter = dee_model_next (m, iter); g_assert (!dee_model_is_first (m, iter)); g_assert (!dee_model_is_last (m, iter)); g_assert_cmpint (2, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("Two", ==, dee_model_get_string (m, iter, 1)); g_assert (iter == dee_model_prev (m, dee_model_get_last_iter (m))); iter = dee_model_next (m, iter); g_assert (!dee_model_is_first (m, iter)); g_assert (dee_model_is_last (m, iter)); g_assert (dee_model_is_last (m, iter)); g_assert_cmpuint (dee_serializable_model_get_seqnum (fix->model), ==, dee_serializable_model_get_seqnum (m)); } /* Test a filter that blocks everything */ static void test_discard_all (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new (discard_all_model_map, discard_all_model_notify, fix, NULL, &filter); DeeModel *m = dee_filter_model_new (fix->model, &filter); /* Check expected sizes */ g_return_if_fail (DEE_IS_FILTER_MODEL (m)); g_assert_cmpint (0, ==, dee_model_get_n_rows (m)); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); /* Check that the begin iter is indeed also the end iter */ DeeModelIter *iter = dee_model_get_first_iter (m); g_assert (dee_model_is_first (m, iter)); g_assert (dee_model_is_last (m, iter)); /* Check that seqnum is still zero */ g_assert_cmpuint (0, ==, dee_serializable_model_get_seqnum (m)); guint filter_add_count = 0; guint orig_add_count = 0; g_signal_connect (m, "row-added", G_CALLBACK (signal_counter), &filter_add_count); g_signal_connect (fix->model, "row-added", G_CALLBACK (signal_counter), &orig_add_count); dee_model_append (fix->model, 3, "Three"); dee_model_append (fix->model, 4, "Four"); /* Updates to the orig model should be ignored by this filter */ g_assert_cmpint (0, ==, filter_add_count); g_assert_cmpint (2, ==, orig_add_count); g_assert_cmpint (0, ==, dee_model_get_n_rows (m)); g_assert_cmpint (5, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpuint (0, ==, dee_serializable_model_get_seqnum (m)); /* Now add stuff to the filtered model directly. This should work, * and should be written through to the orig model as well */ dee_model_append (m, 27, "TwentySeven"); g_assert_cmpint (1, ==, filter_add_count); g_assert_cmpint (3, ==, orig_add_count); g_assert_cmpint (1, ==, dee_model_get_n_rows (m)); g_assert_cmpint (6, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpuint (1, ==, dee_serializable_model_get_seqnum (m)); /* The first (and only) row of 'm' should be at offset 5 in fix->model */ iter = dee_model_get_first_iter (m); g_assert (iter == dee_model_get_iter_at_row (fix->model, 5)); g_assert_cmpint (27, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpint (27, ==, dee_model_get_int32 (fix->model, iter, 0)); g_assert_cmpstr ("TwentySeven", ==, dee_model_get_string (m, iter, 1)); g_assert_cmpstr ("TwentySeven", ==, dee_model_get_string (fix->model, iter, 1)); /* And append two more rows to the filtered model, to ensure the order */ dee_model_prepend (m, -1, "MinusOne"); dee_model_append (m, 39, "ThirtyNine"); g_assert_cmpint (3, ==, filter_add_count); g_assert_cmpint (5, ==, orig_add_count); g_assert_cmpint (3, ==, dee_model_get_n_rows (m)); g_assert_cmpint (8, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpuint (3, ==, dee_serializable_model_get_seqnum (m)); iter = dee_model_prev (m, dee_model_get_last_iter (m)); g_assert_cmpint (39, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("ThirtyNine", ==, dee_model_get_string (m, iter, 1)); iter = dee_model_prev (m, iter); g_assert_cmpint (27, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("TwentySeven", ==, dee_model_get_string (m, iter, 1)); iter = dee_model_prev (m, iter); g_assert_cmpint (-1, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("MinusOne", ==, dee_model_get_string (m, iter, 1)); iter = dee_model_prev (fix->model, dee_model_get_last_iter (fix->model)); g_assert_cmpint (39, ==, dee_model_get_int32 (fix->model, iter, 0)); g_assert_cmpstr ("ThirtyNine", ==, dee_model_get_string (fix->model, iter, 1)); g_object_unref (m); } /* Test a filter that blocks everything */ static void test_discard_all_append_notify (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new (discard_all_model_map, append_all_model_notify, fix, NULL, &filter); DeeModel *m = dee_filter_model_new (fix->model, &filter); /* Nothing was added to the filter model, seqnum should be zero */ g_assert_cmpuint (0, ==, dee_serializable_model_get_seqnum (m)); guint filter_add_count = 0; guint orig_add_count = 0; g_signal_connect (m, "row-added", G_CALLBACK (signal_counter), &filter_add_count); g_signal_connect (fix->model, "row-added", G_CALLBACK (signal_counter), &orig_add_count); dee_model_append (fix->model, 3, "Three"); dee_model_append (fix->model, 4, "Four"); /* Updates to the orig model should be detected by this filter */ g_assert_cmpint (2, ==, filter_add_count); g_assert_cmpint (2, ==, orig_add_count); g_assert_cmpint (2, ==, dee_model_get_n_rows (m)); g_assert_cmpint (5, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpuint (2, ==, dee_serializable_model_get_seqnum (m)); /* The first row of 'm' should be at offset 3 in fix->model */ DeeModelIter *iter = dee_model_get_first_iter (m); g_assert (iter == dee_model_get_iter_at_row (fix->model, 3)); g_assert_cmpint (3, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpint (3, ==, dee_model_get_int32 (fix->model, iter, 0)); g_assert_cmpstr ("Three", ==, dee_model_get_string (m, iter, 1)); g_assert_cmpstr ("Three", ==, dee_model_get_string (fix->model, iter, 1)); /* The second row of 'm' should be at offset 4 in fix->model */ iter = dee_model_next (m, iter); g_assert (iter == dee_model_get_iter_at_row (fix->model, 4)); g_assert_cmpint (4, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpint (4, ==, dee_model_get_int32 (fix->model, iter, 0)); g_assert_cmpstr ("Four", ==, dee_model_get_string (m, iter, 1)); g_assert_cmpstr ("Four", ==, dee_model_get_string (fix->model, iter, 1)); /* Assert that the next iter in 'm is the end iter */ iter = dee_model_next (m, iter); g_assert (dee_model_is_last (m, iter)); g_object_unref (m); } static void test_change_backend (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new (append_all_model_map, append_all_model_notify, fix, NULL, &filter); DeeModel *m = dee_filter_model_new (fix->model, &filter); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); DeeModelIter *iter = dee_model_get_first_iter (m); g_assert (dee_model_is_first (m, iter)); g_assert (!dee_model_is_last (m, iter)); g_assert_cmpint (0, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, iter, 1)); g_assert_cmpuint (0, ==, dee_model_get_position (m, iter)); iter = dee_model_get_first_iter (fix->model); dee_model_remove (fix->model, iter); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpint (2, ==, dee_model_get_n_rows (m)); iter = dee_model_get_first_iter (fix->model); dee_model_remove (fix->model, iter); g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpint (1, ==, dee_model_get_n_rows (m)); iter = dee_model_get_first_iter (m); g_assert (dee_model_is_first (m, iter)); g_assert (!dee_model_is_last (m, iter)); g_assert_cmpint (2, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("Two", ==, dee_model_get_string (m, iter, 1)); iter = dee_model_get_first_iter (fix->model); dee_model_set (fix->model, iter, -1, "Minus one"); iter = dee_model_get_first_iter (m); g_assert (dee_model_is_first (m, iter)); g_assert (!dee_model_is_last (m, iter)); g_assert_cmpint (-1, ==, dee_model_get_int32 (m, iter, 0)); g_assert_cmpstr ("Minus one", ==, dee_model_get_string (m, iter, 1)); g_assert_cmpuint (0, ==, dee_model_get_position (m, iter)); /* and finally remove the last row via the filter model */ dee_model_remove (m, iter); g_assert_cmpint (0, ==, dee_model_get_n_rows (m)); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); } /* Test dee_filter_new_collator() ascending */ static void test_collator_asc (FilterFixture *fix, gconstpointer data) { DeeModelIter *r0, *r1, *r2, *r3, *r4, *r5; DeeFilter collator; DeeModel *m; dee_filter_new_collator (1, &collator); m = dee_filter_model_new (fix->model, &collator); /* Test alphabetic sorting after initial construction */ r0 = dee_model_get_iter_at_row (m, 0); r1 = dee_model_get_iter_at_row (m, 1); r2 = dee_model_get_iter_at_row (m, 2); g_assert_cmpstr ("One", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpstr ("Two", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r2, 1)); /* Test alphabetic sorting after updates */ dee_model_append (fix->model, 3, "Three"); dee_model_append (fix->model, 4, "Four"); r0 = dee_model_get_iter_at_row (m, 0); r1 = dee_model_get_iter_at_row (m, 1); r2 = dee_model_get_iter_at_row (m, 2); r3 = dee_model_get_iter_at_row (m, 3); r4 = dee_model_get_iter_at_row (m, 4); g_assert_cmpstr ("Four", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpstr ("One", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpstr ("Three", ==, dee_model_get_string (m, r2, 1)); g_assert_cmpstr ("Two", ==, dee_model_get_string (m, r3, 1)); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r4, 1)); /* Appending to the end of the sorted model is a special case in the code, * so double check on that... */ dee_model_append (fix->model, 5, "Zzzz"); r0 = dee_model_get_iter_at_row (m, 0); r5 = dee_model_get_iter_at_row (m, 5); g_assert_cmpstr ("Four", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpstr ("Zzzz", ==, dee_model_get_string (m, r5, 1)); g_object_unref (m); } /* Test dee_filter_new_collator_desc() descending*/ static void test_collator_desc (FilterFixture *fix, gconstpointer data) { DeeModelIter *r0, *r1, *r2, *r3, *r4, *r5; DeeFilter collator; DeeModel *m; dee_filter_new_collator_desc (1, &collator); m = dee_filter_model_new (fix->model, &collator); /* Test alphabetic sorting after initial construction */ r0 = dee_model_get_iter_at_row (m, 0); r1 = dee_model_get_iter_at_row (m, 1); r2 = dee_model_get_iter_at_row (m, 2); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpstr ("Two", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpstr ("One", ==, dee_model_get_string (m, r2, 1)); /* Test alphabetic sorting after updates */ dee_model_append (fix->model, 3, "Three"); dee_model_append (fix->model, 4, "Four"); r0 = dee_model_get_iter_at_row (m, 0); r1 = dee_model_get_iter_at_row (m, 1); r2 = dee_model_get_iter_at_row (m, 2); r3 = dee_model_get_iter_at_row (m, 3); r4 = dee_model_get_iter_at_row (m, 4); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpstr ("Two", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpstr ("Three", ==, dee_model_get_string (m, r2, 1)); g_assert_cmpstr ("One", ==, dee_model_get_string (m, r3, 1)); g_assert_cmpstr ("Four", ==, dee_model_get_string (m, r4, 1)); /* Appending to the end of the sorted model is a special case in the code, * so double check on that... */ dee_model_append (fix->model, 5, "Zzzz"); r0 = dee_model_get_iter_at_row (m, 0); r5 = dee_model_get_iter_at_row (m, 5); g_assert_cmpstr ("Four", ==, dee_model_get_string (m, r5, 1)); g_assert_cmpstr ("Zzzz", ==, dee_model_get_string (m, r0, 1)); g_object_unref (m); } static void _test_orig_ordering (FilterFixture *fix, DeeFilter *filter) { DeeModelIter *r0, *r1, *r2, *r3, *r4;//, *r5; DeeModel *m = dee_filter_model_new (fix->model, filter); /* Assert that the initial filtering is good: * { [ 0, "Zero" ] } { [ 0, "Zero" ], * [ 1, "One" ], * [ 2, "Two" ]} * */ g_assert_cmpint (1, ==, dee_model_get_n_rows (m)); r0 = dee_model_get_first_iter (m); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpint (0, ==, dee_model_get_int32 (m, r0, 0)); g_assert (dee_model_next (m, r0) == dee_model_get_last_iter (m)); /* Assert that we can append rows: * { [ 0, "Zero" ], { [ 0, "Zero" ], * [ 12, "Zero" ], [ 1, "One" ], * [ 13, "Zero" ] } [ 2, "Two" ], * [ 1, "One" ], * [ 12, "Zero" ], * [ 13, "Zero" ] } * */ dee_model_append (fix->model, 11, "One"); // Discard dee_model_append (fix->model, 12, "Zero"); // Include dee_model_append (fix->model, 13, "Zero"); // Include g_assert_cmpint (3, ==, dee_model_get_n_rows (m)); r0 = dee_model_get_iter_at_row (m, 0); r1 = dee_model_get_iter_at_row (m, 1); r2 = dee_model_get_iter_at_row (m, 2); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpint (0, ==, dee_model_get_int32 (m, r0, 0)); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpint (12, ==, dee_model_get_int32 (m, r1, 0)); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r2, 1)); g_assert_cmpint (13, ==, dee_model_get_int32 (m, r2, 0)); /* Assert sorting is correct for prepends: * { [ -1, "Zero" ], { [ -1, "Zero" ], * [ 0, "Zero" ], [ 0, "Zero" ], * [ 12, "Zero" ], [ 1, "One" ], * [ 13, "Zero" ] } [ 2, "Two" ], * [ 1, "One" ], * [ 12, "Zero" ], * [ 13, "Zero" ] } * */ dee_model_prepend (fix->model, -1, "Zero"); g_assert_cmpint (4, ==, dee_model_get_n_rows (m)); r0 = dee_model_get_first_iter (m); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r0, 1)); g_assert_cmpint (-1, ==, dee_model_get_int32 (m, r0, 0)); /* Assert sorting is correct for inserts before a row that is already * in the filtered model: * { [ -1, "Zero" ], { [ -1, "Zero" ], * [ -2, "Zero" ], [ -2, "Zero" ], * [ 0, "Zero" ], [ 0, "Zero" ], * [ 12, "Zero" ], [ 1, "One" ], * [ 13, "Zero" ] } [ 2, "Two" ], * [ 1, "One" ], * [ 12, "Zero" ], * [ 13, "Zero" ] } * */ r1 = dee_model_get_iter_at_row (m, 1); // The (0, "Zero") row g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpint (0, ==, dee_model_get_int32 (m, r1, 0)); dee_model_insert_before (fix->model, r1, -2, "Zero"); r1 = dee_model_get_iter_at_row (m, 1); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r1, 1)); g_assert_cmpint (-2, ==, dee_model_get_int32 (m, r1, 0)); /* Assert sorting is correct for inserts before a row that is *not* included * in the filtered model: * { [ -1, "Zero" ], { [ -1, "Zero" ], * [ -2, "Zero" ], [ -2, "Zero" ], * [ 0, "Zero" ], [ 0, "Zero" ], * [ -3, "Zero" ], [ 1, "One" ], * [ 12, "Zero" ], [ -3, "Zero" ] * [ 2, "Two" ], * [ 13, "Zero" ] } [ 1, "One" ], * [ 12, "Zero" ], * [ 13, "Zero" ] } * */ r4 = dee_model_get_iter_at_row (fix->model, 4); g_assert_cmpstr ("Two", ==, dee_model_get_string (fix->model, r4, 1)); g_assert (!dee_filter_model_contains (DEE_FILTER_MODEL (m), r4)); dee_model_insert_before (fix->model, r4, -3, "Zero"); r3 = dee_model_get_iter_at_row (m, 3); g_assert_cmpstr ("Zero", ==, dee_model_get_string (m, r3, 1)); g_assert_cmpint (-3, ==, dee_model_get_int32 (m, r3, 0)); g_object_unref (m); } /* Test dee_filter_new_for_key_column() */ static void test_key (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new_for_key_column (1, "Zero", &filter); _test_orig_ordering (fix, &filter); } /* Test dee_filter_new_for_any_column() */ static void test_any (FilterFixture *fix, gconstpointer data) { DeeFilter filter; dee_filter_new_for_any_column (1, g_variant_new_string ("Zero"), &filter); _test_orig_ordering (fix, &filter); } /* Test dee_filter_new_regex() */ static void test_regex (FilterFixture *fix, gconstpointer data) { DeeFilter filter; GRegex *regex; regex = g_regex_new (".ero", 0, 0, NULL); dee_filter_new_regex (1, regex, &filter); _test_orig_ordering (fix, &filter); g_regex_unref (regex); } static void increment_first (TwoIntsTuple *tuple) { tuple->first++; // first always has to be incremented before second g_assert (tuple->first > tuple->second); } static void increment_second (TwoIntsTuple *tuple) { tuple->second++; // second needs to be incremented after first g_assert (tuple->second == tuple->first); } static void test_changesets (FilterFixture *fix, gconstpointer data) { GRegex *regex; DeeFilter filter; DeeModel *filter_m1; DeeModel *filter_m2; DeeModel *filter_m3; regex = g_regex_new ("^..[eo]", 0, 0, NULL); dee_filter_new_regex (1, regex, &filter); filter_m1 = dee_filter_model_new (fix->model, &filter); g_regex_unref (regex); regex = g_regex_new ("^Z", 0, 0, NULL); dee_filter_new_regex (1, regex, &filter); filter_m2 = dee_filter_model_new (fix->model, &filter); g_regex_unref (regex); regex = g_regex_new ("^X", 0, 0, NULL); dee_filter_new_regex (1, regex, &filter); filter_m3 = dee_filter_model_new (fix->model, &filter); g_regex_unref (regex); g_assert_cmpuint (0, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpuint (0, ==, dee_model_get_n_rows (filter_m1)); g_assert_cmpuint (0, ==, dee_model_get_n_rows (filter_m2)); g_assert_cmpuint (0, ==, dee_model_get_n_rows (filter_m3)); TwoIntsTuple tuple_m0 = { 0, 0 }; TwoIntsTuple tuple_m1 = { 0, 0 }; TwoIntsTuple tuple_m2 = { 0, 0 }; TwoIntsTuple tuple_m3 = { 0, 0 }; g_signal_connect_swapped (fix->model, "changeset-started", G_CALLBACK (increment_first), &tuple_m0); g_signal_connect_swapped (fix->model, "changeset-finished", G_CALLBACK (increment_second), &tuple_m0); g_signal_connect_swapped (filter_m1, "changeset-started", G_CALLBACK (increment_first), &tuple_m1); g_signal_connect_swapped (filter_m1, "changeset-finished", G_CALLBACK (increment_second), &tuple_m1); g_signal_connect_swapped (filter_m2, "changeset-started", G_CALLBACK (increment_first), &tuple_m2); g_signal_connect_swapped (filter_m2, "changeset-finished", G_CALLBACK (increment_second), &tuple_m2); g_signal_connect_swapped (filter_m3, "changeset-started", G_CALLBACK (increment_first), &tuple_m3); g_signal_connect_swapped (filter_m3, "changeset-finished", G_CALLBACK (increment_second), &tuple_m3); dee_model_begin_changeset (fix->model); add_3rows (fix->model); dee_model_end_changeset (fix->model); g_assert_cmpuint (3, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpuint (2, ==, dee_model_get_n_rows (filter_m1)); g_assert_cmpuint (1, ==, dee_model_get_n_rows (filter_m2)); g_assert_cmpuint (0, ==, dee_model_get_n_rows (filter_m3)); g_assert_cmpint (tuple_m0.first, ==, 1); g_assert_cmpint (tuple_m0.second, ==, 1); g_assert_cmpint (tuple_m1.first, ==, 1); g_assert_cmpint (tuple_m1.second, ==, 1); g_assert_cmpint (tuple_m2.first, ==, 1); g_assert_cmpint (tuple_m2.second, ==, 1); g_assert_cmpint (tuple_m3.first, ==, 1); g_assert_cmpint (tuple_m3.second, ==, 1); } dee-1.2.7+15.04.20150304/tests/test-model-rows.c0000644000015300001610000011770312475676210021203 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * Michal Hruby * */ #include #include #include #include typedef struct { DeeModel *model; } RowsFixture; static void seq_rows_setup (RowsFixture *fix, gconstpointer data); static void seq_rows_teardown (RowsFixture *fix, gconstpointer data); static void proxy_rows_setup (RowsFixture *fix, gconstpointer data); static void proxy_rows_teardown (RowsFixture *fix, gconstpointer data); static void txn_rows_setup (RowsFixture *fix, gconstpointer data); static void txn_rows_teardown (RowsFixture *fix, gconstpointer data); static void seq_rows_asv_setup (RowsFixture *fix, gconstpointer data); static void proxy_rows_asv_setup (RowsFixture *fix, gconstpointer data); static void txn_rows_asv_setup (RowsFixture *fix, gconstpointer data); static void test_rows_allocation (RowsFixture *fix, gconstpointer data); static void test_rows_clear (RowsFixture *fix, gconstpointer data); static void test_insert_at_pos (RowsFixture *fix, gconstpointer data); static void test_insert_at_iter (RowsFixture *fix, gconstpointer data); static void test_prepend (RowsFixture *fix, gconstpointer data); static void test_append (RowsFixture *fix, gconstpointer data); static void test_get_value (RowsFixture *fix, gconstpointer data); static void test_no_transfer (RowsFixture *fix, gconstpointer data); static void test_iter_backwards (RowsFixture *fix, gconstpointer data); static void test_illegal_access (RowsFixture *fix, gconstpointer data); static void test_sorted (RowsFixture *fix, gconstpointer data); static void test_sort_stable (RowsFixture *fix, gconstpointer data); static void test_sorted_with_sizes (RowsFixture *fix, gconstpointer data); static void test_named_cols_append (RowsFixture *fix, gconstpointer data); static void test_named_cols_fields (RowsFixture *fix, gconstpointer data); static void test_named_cols_duplicated_fields (RowsFixture *fix, gconstpointer data); static void test_named_cols_error (RowsFixture *fix, gconstpointer data); static void test_model_iter_copy (RowsFixture *fix, gconstpointer data); static void test_model_iter_free (RowsFixture *fix, gconstpointer data); void test_model_rows_create_suite (void) { #define ITER_DOMAIN "/ModelIter/Boxing" #define SEQ_DOMAIN "/Model/Sequence/Rows" #define PROXY_DOMAIN "/Model/Proxy/Rows" #define TXN_DOMAIN "/Model/Transaction/Rows" g_test_add (ITER_DOMAIN"/Copy", RowsFixture, 0, seq_rows_setup, test_model_iter_copy, seq_rows_teardown); g_test_add (ITER_DOMAIN"/Free", RowsFixture, 0, seq_rows_setup, test_model_iter_free, seq_rows_teardown); g_test_add (SEQ_DOMAIN"/Allocation", RowsFixture, 0, seq_rows_setup, test_rows_allocation, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/Allocation", RowsFixture, 0, proxy_rows_setup, test_rows_allocation, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/Allocation", RowsFixture, 0, txn_rows_setup, test_rows_allocation, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/Clear", RowsFixture, 0, seq_rows_setup, test_rows_clear, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/Clear", RowsFixture, 0, proxy_rows_setup, test_rows_clear, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/Clear", RowsFixture, 0, txn_rows_setup, test_rows_clear, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/InsertAtPos", RowsFixture, 0, seq_rows_setup, test_insert_at_pos, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/InsertAtPos", RowsFixture, 0, proxy_rows_setup, test_insert_at_pos, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/InsertAtPos", RowsFixture, 0, txn_rows_setup, test_insert_at_pos, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/InsertAtIter", RowsFixture, 0, seq_rows_setup, test_insert_at_iter, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/InsertAtIter", RowsFixture, 0, proxy_rows_setup, test_insert_at_iter, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/InsertAtIter", RowsFixture, 0, txn_rows_setup, test_insert_at_iter, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/Prepend", RowsFixture, 0, seq_rows_setup, test_prepend, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/Prepend", RowsFixture, 0, proxy_rows_setup, test_prepend, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/Prepend", RowsFixture, 0, txn_rows_setup, test_prepend, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/Append", RowsFixture, 0, seq_rows_setup, test_append, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/Append", RowsFixture, 0, proxy_rows_setup, test_append, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/Append", RowsFixture, 0, txn_rows_setup, test_append, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/GetValue", RowsFixture, 0, seq_rows_setup, test_get_value, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/GetValue", RowsFixture, 0, proxy_rows_setup, test_get_value, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/GetValue", RowsFixture, 0, txn_rows_setup, test_get_value, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/NoTransfer", RowsFixture, 0, seq_rows_setup, test_no_transfer, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/NoTransfer", RowsFixture, 0, proxy_rows_setup, test_no_transfer, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/NoTransfer", RowsFixture, 0, txn_rows_setup, test_no_transfer, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/IterBackwards", RowsFixture, 0, seq_rows_setup, test_iter_backwards, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/IterBackwards", RowsFixture, 0, proxy_rows_setup, test_iter_backwards, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/IterBackwards", RowsFixture, 0, txn_rows_setup, test_iter_backwards, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/IllegalAccess", RowsFixture, 0, seq_rows_setup, test_illegal_access, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/IllegalAccess", RowsFixture, 0, proxy_rows_setup, test_illegal_access, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/IllegalAccess", RowsFixture, 0, txn_rows_setup, test_illegal_access, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/Sorted", RowsFixture, 0, seq_rows_setup, test_sorted, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/Sorted", RowsFixture, 0, proxy_rows_setup, test_sorted, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/Sorted", RowsFixture, 0, txn_rows_setup, test_sorted, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/Sorted/WithSizes", RowsFixture, 0, seq_rows_setup, test_sorted_with_sizes, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/Sorted/WithSizes", RowsFixture, 0, proxy_rows_setup, test_sorted_with_sizes, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/Sorted/WithSizes", RowsFixture, 0, txn_rows_setup, test_sorted_with_sizes, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/StableSorted", RowsFixture, 0, seq_rows_setup, test_sort_stable, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/StableSorted", RowsFixture, 0, proxy_rows_setup, test_sort_stable, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/StableSorted", RowsFixture, 0, txn_rows_setup, test_sort_stable, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/NamedColumns/Append", RowsFixture, 0, seq_rows_setup, test_named_cols_append, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/NamedColumns/Append", RowsFixture, 0, proxy_rows_setup, test_named_cols_append, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/NamedColumns/Append", RowsFixture, 0, txn_rows_setup, test_named_cols_append, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/NamedColumns/Fields", RowsFixture, 0, seq_rows_asv_setup, test_named_cols_fields, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/NamedColumns/Fields", RowsFixture, 0, proxy_rows_asv_setup, test_named_cols_fields, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/NamedColumns/Fields", RowsFixture, 0, txn_rows_asv_setup, test_named_cols_fields, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/NamedColumns/DuplicatedFields", RowsFixture, 0, seq_rows_asv_setup, test_named_cols_duplicated_fields, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/NamedColumns/DuplicatedFields", RowsFixture, 0, proxy_rows_asv_setup, test_named_cols_duplicated_fields, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/NamedColumns/DuplicatedFields", RowsFixture, 0, txn_rows_asv_setup, test_named_cols_duplicated_fields, txn_rows_teardown); g_test_add (SEQ_DOMAIN"/NamedColumns/Invalid", RowsFixture, 0, seq_rows_setup, test_named_cols_error, seq_rows_teardown); g_test_add (PROXY_DOMAIN"/NamedColumns/Invalid", RowsFixture, 0, proxy_rows_setup, test_named_cols_error, proxy_rows_teardown); g_test_add (TXN_DOMAIN"/NamedColumns/Invalid", RowsFixture, 0, txn_rows_setup, test_named_cols_error, txn_rows_teardown); } /* setup & teardown functions */ static void seq_rows_setup (RowsFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "i", "s", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); } static void seq_rows_teardown (RowsFixture *fix, gconstpointer data) { g_object_unref (fix->model); fix->model = NULL; } static void proxy_rows_setup (RowsFixture *fix, gconstpointer data) { seq_rows_setup (fix, data); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", fix->model, NULL); g_assert (DEE_IS_PROXY_MODEL (fix->model)); } static void proxy_rows_teardown (RowsFixture *fix, gconstpointer data) { g_object_unref (fix->model); fix->model = NULL; } static void txn_rows_setup (RowsFixture *fix, gconstpointer data) { seq_rows_setup (fix, data); fix->model = dee_transaction_new (fix->model); g_assert (DEE_IS_TRANSACTION (fix->model)); } static void txn_rows_teardown (RowsFixture *fix, gconstpointer data) { g_object_unref (fix->model); fix->model = NULL; } static void seq_rows_asv_setup (RowsFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "i", "s", "a{sv}", "a{sv}", NULL); dee_model_set_column_names (fix->model, "count", "name", "hints", "hints2", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); } static void proxy_rows_asv_setup (RowsFixture *fix, gconstpointer data) { seq_rows_asv_setup (fix, data); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", fix->model, NULL); g_assert (DEE_IS_PROXY_MODEL (fix->model)); } static void txn_rows_asv_setup (RowsFixture *fix, gconstpointer data) { seq_rows_asv_setup (fix, data); fix->model = dee_transaction_new (fix->model); g_assert (DEE_IS_TRANSACTION (fix->model)); } /* test cases */ static void test_model_iter_copy (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter, *copied; iter = dee_model_append (fix->model, 10, "Rooney"); copied = g_boxed_copy (DEE_TYPE_MODEL_ITER, iter); g_assert (iter == copied); } static void test_model_iter_free (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; iter = dee_model_append (fix->model, 10, "Rooney"); g_boxed_free (DEE_TYPE_MODEL_ITER, iter); /* And since it's supposed to be a no-op we can do this */ for (i = 0; i < 100; i++) g_boxed_free (DEE_TYPE_MODEL_ITER, iter); /* Didn't crash? Good! */ } static void test_rows_allocation (RowsFixture *fix, gconstpointer data) { g_assert (DEE_IS_MODEL (fix->model)); } static gint n_clear_sigs = 0; static void on_clear_row_removed (DeeModel *model, DeeModelIter *iter) { n_clear_sigs++; } static void test_rows_clear (RowsFixture *fix, gconstpointer data) { gint i; for (i = 0; i < 1000; i++) { dee_model_append (fix->model, 10, "Rooney"); } g_assert_cmpint (1000, ==, dee_model_get_n_rows (fix->model)); g_signal_connect (fix->model, "row-removed", G_CALLBACK (on_clear_row_removed), NULL); n_clear_sigs = 0; dee_model_clear (fix->model); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); g_assert_cmpint (1000, ==, n_clear_sigs); } static void test_insert_at_pos (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; dee_model_append (fix->model, 10, "Rooney"); dee_model_append (fix->model, 10, "Rooney"); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); dee_model_insert (fix->model, 1, 27, "Not Rooney"); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); g_assert (dee_model_is_first (fix->model, iter)); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Rooney"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (27, ==, i); g_assert_cmpstr (str, ==, "Not Rooney"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Rooney"); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static void test_insert_at_iter (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; dee_model_append (fix->model, 10, "Rooney"); dee_model_append (fix->model, 10, "Rooney"); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); iter = dee_model_next (fix->model, iter); dee_model_insert_before (fix->model, iter, 27, "Not Rooney"); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Rooney"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (27, ==, i); g_assert_cmpstr (str, ==, "Not Rooney"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Rooney"); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static void test_prepend (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; dee_model_prepend (fix->model, 11, "Mid"); dee_model_append (fix->model, 12, "Last"); dee_model_prepend (fix->model, 10, "First"); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "First"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (11, ==, i); g_assert_cmpstr (str, ==, "Mid"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (12, ==, i); g_assert_cmpstr (str, ==, "Last"); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static void test_append (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; dee_model_append (fix->model, 11, "First"); dee_model_append (fix->model, 12, "Mid"); dee_model_append (fix->model, 10, "Last"); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (11, ==, i); g_assert_cmpstr (str, ==, "First"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (12, ==, i); g_assert_cmpstr (str, ==, "Mid"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Last"); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static void test_get_value (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; GVariant *variant; dee_model_set_column_names (fix->model, "count", "name", NULL); dee_model_append (fix->model, 11, "First"); dee_model_append (fix->model, 12, "Mid"); dee_model_append (fix->model, 10, "Last"); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); variant = dee_model_get_value (fix->model, iter, 0); g_assert_cmpint (g_variant_get_int32 (variant), ==, 11); g_variant_unref (variant); iter = dee_model_next (fix->model, iter); variant = dee_model_get_value (fix->model, iter, 1); g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "Mid"); g_variant_unref (variant); iter = dee_model_next (fix->model, iter); variant = dee_model_get_value_by_name (fix->model, iter, "count"); g_assert_cmpint (g_variant_get_int32 (variant), ==, 10); g_variant_unref (variant); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static void test_iter_backwards (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; dee_model_append (fix->model, 11, "First"); dee_model_append (fix->model, 12, "Mid"); dee_model_append (fix->model, 10, "Last"); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_last_iter (fix->model); g_assert (dee_model_is_last (fix->model, iter)); iter = dee_model_prev (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Last"); iter = dee_model_prev (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (12, ==, i); g_assert_cmpstr (str, ==, "Mid"); iter = dee_model_prev (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (11, ==, i); g_assert_cmpstr (str, ==, "First"); g_assert (dee_model_is_first (fix->model, iter)); } /* Make sure the the memory allocated for strings is not reffed inside * the model. Also checks that strings are returned as const pointers */ static void test_no_transfer (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gchar *orig, *str1, *str2; orig = g_strdup ("test"); dee_model_append (fix->model, 1, orig); g_assert_cmpint (1, ==, dee_model_get_n_rows (fix->model)); /* This should work, of course */ iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, NULL, &str1); g_assert_cmpstr (str1, ==, "test"); /* Assert that we get a const pointer to the string back. * Strings should behave like that */ dee_model_get (fix->model, iter, NULL, &str2); g_assert (str1 == str2); /* Modify orig in place and assert it doesn't affect the model */ orig[0] = 'P'; dee_model_get (fix->model, iter, NULL, &str1, -1); g_assert_cmpstr (str1, ==, "test"); /* Now free orig and make sure we can still read from the model */ g_free (orig); dee_model_get (fix->model, iter, NULL, &str1, -1); g_assert_cmpstr (str1, ==, "test"); } /* Try to get and set values from a removed row */ static void test_illegal_access (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; dee_model_append (fix->model, 1, "Hello"); iter = dee_model_append (fix->model, 1, "Mary"); dee_model_append (fix->model, 1, "Lou"); dee_model_remove (fix->model, iter); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "Mary"); exit (0); /* successful test run */ } g_test_trap_assert_failed (); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { dee_model_set (fix->model, iter, 27, "Marie"); exit (0); /* successful test run */ } g_test_trap_assert_failed (); } static gint cmp_constant (GVariant **row1, GVariant **row2, gpointer user_data) { g_assert_cmpstr (user_data, ==, "test-user-data"); return 0; } static gint cmp_col_0 (GVariant **row1, GVariant **row2, gpointer user_data) { g_assert_cmpstr (user_data, ==, "test-user-data"); //g_debug ("CMP %i %i", g_variant_get_int32 (row1[0]), g_variant_get_int32 (row2[0])); return g_variant_get_int32 (row2[0]) - g_variant_get_int32 (row1[0]); } static void test_sorted (RowsFixture *fix, gconstpointer data) { DeeModelIter *hter, *iter, *jter, *kter; gboolean was_found; /* FINAL MODEL: [(28,s), (27,s), (26,s), (25,s)] * ~= [hter, iter, jter, kter] */ /* Test find() with an empty model. With NULL was_found arg */ iter = dee_model_find_sorted (fix->model, cmp_col_0, "test-user-data", NULL, 0, ""); g_assert (iter == dee_model_get_last_iter (fix->model)); /* Test find() with an empty model. With non-NULL was_found arg */ was_found = TRUE; iter = dee_model_find_sorted (fix->model, cmp_col_0, "test-user-data", &was_found, 0, ""); g_assert (!was_found); g_assert (iter == dee_model_get_last_iter (fix->model)); /* Insert the first row */ iter = dee_model_insert_sorted (fix->model, cmp_col_0, "test-user-data", 27, "Sorta sorted"); g_assert (iter != dee_model_get_last_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); /* Test append */ kter = dee_model_insert_sorted (fix->model, cmp_col_0, "test-user-data", 25, "Sorta sorted"); g_assert (kter != dee_model_get_last_iter (fix->model)); g_assert (kter != dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (kter != iter); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); g_assert (kter == dee_model_next (fix->model, iter)); /* Test insert in between rows */ jter = dee_model_insert_sorted (fix->model, cmp_col_0, "test-user-data", 26, "Sorta sorted"); g_assert (jter != dee_model_get_last_iter (fix->model)); g_assert (jter != dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (jter != iter); g_assert (jter != kter); g_assert (jter == dee_model_next (fix->model, iter)); g_assert (kter == dee_model_next (fix->model, jter)); g_assert (dee_model_get_last_iter (fix->model) == dee_model_next (fix->model, kter)); /* Test prepend */ hter = dee_model_insert_sorted (fix->model, cmp_col_0, "test-user-data", 28, "Sorta sorted"); g_assert (hter == dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_next (fix->model, hter)); g_assert_cmpint (4, ==, dee_model_get_n_rows (fix->model)); /* Test find() again now that we have data in the model */ DeeModelIter *result; result = dee_model_find_sorted (fix->model, cmp_col_0, "test-user-data", NULL, 24, ""); g_assert (result == dee_model_get_last_iter (fix->model)); result = dee_model_find_sorted (fix->model, cmp_col_0, "test-user-data", NULL, 28, ""); g_assert (result == hter); /* Test find(). With non-NULL was_found arg */ was_found = TRUE; result = dee_model_find_sorted (fix->model, cmp_col_0, "test-user-data", &was_found, 24, ""); g_assert (result == dee_model_get_last_iter (fix->model)); result = dee_model_find_sorted (fix->model, cmp_col_0, "test-user-data", &was_found, 28, ""); g_assert (was_found); g_assert (result == hter); } static void test_sort_stable (RowsFixture *fix, gconstpointer data) { DeeModelIter *hter, *iter, *jter, *kter; /* FINAL MODEL: [(25,s), (26,s), (27,s), (28,s)] * ~= [hter, iter, jter, kter] */ /* Insert the first row */ iter = dee_model_insert_sorted (fix->model, cmp_constant, "test-user-data", 25, "Stable"); g_assert (iter != dee_model_get_last_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); /* Second row */ kter = dee_model_insert_sorted (fix->model, cmp_constant, "test-user-data", 26, "Stable"); g_assert (kter != dee_model_get_last_iter (fix->model)); g_assert (kter != dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (kter != iter); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); g_assert (kter == dee_model_next (fix->model, iter)); /* Third row */ jter = dee_model_insert_sorted (fix->model, cmp_constant, "test-user-data", 27, "Stable"); g_assert (jter != dee_model_get_last_iter (fix->model)); g_assert (jter != dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (jter != iter); g_assert (jter != kter); g_assert (jter == dee_model_next (fix->model, kter)); g_assert (dee_model_get_last_iter (fix->model) == dee_model_next (fix->model, jter)); /* Fourth row */ hter = dee_model_insert_sorted (fix->model, cmp_constant, "test-user-data", 28, "Stable"); g_assert (hter == dee_model_next (fix->model, jter)); g_assert_cmpint (4, ==, dee_model_get_n_rows (fix->model)); } static gint sized_cmp_col_0 (GVariant **row1, guint row1_length, GVariant **row2, guint row2_length, gpointer user_data) { g_assert_cmpstr (user_data, ==, "test-user-data"); g_assert_cmpuint (row1_length, ==, 2); g_assert_cmpuint (row2_length, ==, 2); g_assert_cmpuint (row1_length, ==, row2_length); return g_variant_get_int32 (row2[0]) - g_variant_get_int32 (row1[0]); } static void test_sorted_with_sizes (RowsFixture *fix, gconstpointer data) { DeeModelIter *hter, *iter, *jter, *kter; gboolean was_found; GVariant *row_spec[2]; /* FINAL MODEL: [(28,s), (27,s), (26,s), (25,s)] * ~= [hter, iter, jter, kter] */ row_spec[0] = g_variant_new_int32 (0); row_spec[1] = g_variant_new_string (""); /* Test find() with an empty model. With NULL was_found arg */ iter = dee_model_find_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data", NULL); g_assert (iter == dee_model_get_last_iter (fix->model)); /* Test find() with an empty model. With non-NULL was_found arg */ was_found = TRUE; iter = dee_model_find_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data", &was_found); g_assert (!was_found); g_assert (iter == dee_model_get_last_iter (fix->model)); /* Insert the first row */ row_spec[0] = g_variant_new_int32 (27); row_spec[1] = g_variant_new_string ("Sorta sorted"); iter = dee_model_insert_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data"); g_assert (iter != dee_model_get_last_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); /* Test append */ row_spec[0] = g_variant_new_int32 (25); row_spec[1] = g_variant_new_string ("Sorta sorted"); kter = dee_model_insert_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data"); g_assert (kter != dee_model_get_last_iter (fix->model)); g_assert (kter != dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (kter != iter); g_assert_cmpint (2, ==, dee_model_get_n_rows (fix->model)); g_assert (kter == dee_model_next (fix->model, iter)); /* Test insert in between rows */ row_spec[0] = g_variant_new_int32 (26); row_spec[1] = g_variant_new_string ("Sorta sorted"); jter = dee_model_insert_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data"); g_assert (jter != dee_model_get_last_iter (fix->model)); g_assert (jter != dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_get_first_iter (fix->model)); g_assert (jter != iter); g_assert (jter != kter); g_assert (jter == dee_model_next (fix->model, iter)); g_assert (kter == dee_model_next (fix->model, jter)); g_assert (dee_model_get_last_iter (fix->model) == dee_model_next (fix->model, kter)); /* Test prepend */ row_spec[0] = g_variant_new_int32 (28); row_spec[1] = g_variant_new_string ("Sorta sorted"); hter = dee_model_insert_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data"); g_assert (hter == dee_model_get_first_iter (fix->model)); g_assert (iter == dee_model_next (fix->model, hter)); g_assert_cmpint (4, ==, dee_model_get_n_rows (fix->model)); /* Test find() again now that we have data in the model */ DeeModelIter *result; row_spec[0] = g_variant_new_int32 (24); row_spec[1] = g_variant_new_string (""); result = dee_model_find_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data", NULL); g_assert (result == dee_model_get_last_iter (fix->model)); row_spec[0] = g_variant_new_int32 (28); row_spec[1] = g_variant_new_string (""); result = dee_model_find_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data", NULL); g_assert (result == hter); /* Test find(). With non-NULL was_found arg */ was_found = FALSE; row_spec[0] = g_variant_new_int32 (24); row_spec[1] = g_variant_new_string (""); result = dee_model_find_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data", &was_found); g_assert (result == dee_model_get_last_iter (fix->model)); row_spec[0] = g_variant_new_int32 (28); row_spec[1] = g_variant_new_string (""); result = dee_model_find_row_sorted_with_sizes (fix->model, row_spec, sized_cmp_col_0, "test-user-data", &was_found); g_assert (was_found); g_assert (result == hter); } static void test_named_cols_append (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; GVariant *row_members[2]; dee_model_set_column_names (fix->model, "count", "name", NULL); dee_model_build_named_row (fix->model, row_members, "count", 11, "name", "First", NULL); dee_model_append_row (fix->model, row_members); dee_model_build_named_row (fix->model, row_members, "name", "Mid", "count", 12, NULL); dee_model_append_row (fix->model, row_members); dee_model_build_named_row (fix->model, row_members, "count", 10, "name", "Last", NULL); dee_model_append_row (fix->model, row_members); g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (11, ==, i); g_assert_cmpstr (str, ==, "First"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (12, ==, i); g_assert_cmpstr (str, ==, "Mid"); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Last"); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static void test_named_cols_fields (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; gchar *str; GVariant *row_members[4]; GVariant *dict, *dummy; GHashTable *fields_schemas; fields_schemas = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (fields_schemas, "object-path", "o"); g_hash_table_insert (fields_schemas, "id", "i"); dee_model_register_vardict_schema (fix->model, 2, fields_schemas); g_hash_table_unref (fields_schemas); dee_model_build_named_row (fix->model, row_members, "count", 11, "name", "First", NULL); dee_model_append_row (fix->model, row_members); dee_model_build_named_row (fix->model, row_members, "name", "Mid", "count", 12, "object-path", "/org/example", "hints::id", 8123, NULL); dee_model_append_row (fix->model, row_members); dee_model_build_named_row (fix->model, row_members, "count", 10, "name", "Last", "id", 90, NULL); dee_model_append_row (fix->model, row_members); /* Commence checks */ g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &i, &str, &dict, &dummy); g_assert_cmpint (11, ==, i); g_assert_cmpstr (str, ==, "First"); g_assert_cmpuint (0, ==, g_variant_n_children (dict)); g_assert (dee_model_get_value_by_name (fix->model, iter, "object-path") == NULL); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str, &dict, &dummy); g_assert_cmpint (12, ==, i); g_assert_cmpstr (str, ==, "Mid"); g_assert_cmpuint (2, ==, g_variant_n_children (dict)); g_assert_cmpstr ("/org/example", ==, g_variant_get_string (g_variant_lookup_value (dict, "object-path", G_VARIANT_TYPE_OBJECT_PATH), NULL)); g_assert_cmpint (8123, ==, g_variant_get_int32 (dee_model_get_value_by_name (fix->model, iter, "id"))); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str, &dict, &dummy); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Last"); g_assert_cmpuint (1, ==, g_variant_n_children (dict)); g_assert_cmpint (90, ==, g_variant_get_int32 (dee_model_get_value_by_name (fix->model, iter, "id"))); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); } static gboolean expected_error_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data) { return FALSE; } static void ignore_error_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *msg, gpointer user_data) { } static void test_named_cols_duplicated_fields (RowsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; guint handler_id; gchar *str; GVariant *row_members[4]; GVariant *dict1, *dict2; GHashTable *fields_schemas; g_test_log_set_fatal_handler (expected_error_handler, NULL); handler_id = g_log_set_handler ("dee", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL, ignore_error_handler, NULL); fields_schemas = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (fields_schemas, "id", "i"); dee_model_register_vardict_schema (fix->model, 2, fields_schemas); g_hash_table_insert (fields_schemas, "id", "s"); g_hash_table_insert (fields_schemas, "extra-id", "i"); dee_model_register_vardict_schema (fix->model, 3, fields_schemas); g_hash_table_unref (fields_schemas); dee_model_build_named_row (fix->model, row_members, "count", 11, "name", "First", NULL); dee_model_append_row (fix->model, row_members); dee_model_build_named_row (fix->model, row_members, "name", "Mid", "count", 12, "hints::id", 8123, "hints2::id", "8123", NULL); dee_model_append_row (fix->model, row_members); dee_model_build_named_row (fix->model, row_members, "count", 10, "name", "Last", "hints2::id", "foo", NULL); dee_model_append_row (fix->model, row_members); /* Commence checks */ g_assert_cmpint (3, ==, dee_model_get_n_rows (fix->model)); iter = dee_model_get_first_iter (fix->model); dee_model_get (fix->model, iter, &i, &str, &dict1, &dict2); g_assert_cmpint (11, ==, i); g_assert_cmpstr (str, ==, "First"); g_assert_cmpuint (0, ==, g_variant_n_children (dict1)); g_assert_cmpuint (0, ==, g_variant_n_children (dict2)); g_assert (dee_model_get_value_by_name (fix->model, iter, "hints::id") == NULL); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str, &dict1, &dict2); g_assert_cmpint (12, ==, i); g_assert_cmpstr (str, ==, "Mid"); g_assert_cmpuint (1, ==, g_variant_n_children (dict1)); g_assert_cmpuint (1, ==, g_variant_n_children (dict2)); g_assert_cmpint (8123, ==, g_variant_get_int32 (dee_model_get_value_by_name (fix->model, iter, "hints::id"))); g_assert_cmpstr ("8123", ==, g_variant_get_string (dee_model_get_value_by_name (fix->model, iter, "hints2::id"), NULL)); iter = dee_model_next (fix->model, iter); dee_model_get (fix->model, iter, &i, &str, &dict1, &dict2); g_assert_cmpint (10, ==, i); g_assert_cmpstr (str, ==, "Last"); g_assert_cmpuint (0, ==, g_variant_n_children (dict1)); g_assert_cmpuint (1, ==, g_variant_n_children (dict2)); g_assert_cmpstr ("foo", ==, g_variant_get_string (dee_model_get_value_by_name (fix->model, iter, "hints2::id"), NULL)); iter = dee_model_next (fix->model, iter); g_assert (dee_model_is_last (fix->model, iter)); g_log_remove_handler ("dee", handler_id); } static void test_named_cols_error (RowsFixture *fix, gconstpointer data) { GVariant *row_members[2]; GVariant **result; guint handler_id; dee_model_set_column_names (fix->model, "count", "name", NULL); g_test_log_set_fatal_handler (expected_error_handler, NULL); handler_id = g_log_set_handler ("dee", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL, ignore_error_handler, NULL); /* Only first col set */ result = dee_model_build_named_row (fix->model, row_members, "name", "First", NULL); g_assert (result == NULL); /* Only second col set */ result = dee_model_build_named_row (fix->model, row_members, "count", 12, NULL); g_assert (result == NULL); /* Unregistered cols set */ result = dee_model_build_named_row (fix->model, row_members, "nm", "Foo", "cnt", 12, NULL); g_assert (result == NULL); g_log_remove_handler ("dee", handler_id); } dee-1.2.7+15.04.20150304/tests/test-model-tags.c0000644000015300001610000002671012475676210021144 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include #include #include typedef struct { DeeModel *m; } Fixture; static void sequence_model_setup (Fixture *fix, gconstpointer data); static void sequence_model_teardown (Fixture *fix, gconstpointer data); static void shared_model_setup (Fixture *fix, gconstpointer data); static void shared_model_teardown (Fixture *fix, gconstpointer data); static void test_no_tags (Fixture *fix, gconstpointer data); static void test_one_tag (Fixture *fix, gconstpointer data); static void test_two_tags (Fixture *fix, gconstpointer data); static void test_late_tag (Fixture *fix, gconstpointer data); static void test_destroy_tag (Fixture *fix, gconstpointer data); static void test_tag_access_in_row_removed_handler (Fixture *fix, gconstpointer data); void test_model_tags_create_suite (void) { #define SEQUENCE_MODEL_DOMAIN "/ModelTags/SequenceModel" #define SHARED_MODEL_DOMAIN "/ModelTags/SharedModel" g_test_add (SEQUENCE_MODEL_DOMAIN"/NoTags", Fixture, 0, sequence_model_setup, test_no_tags, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/NoTags", Fixture, 0, shared_model_setup, test_no_tags, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/OneTag", Fixture, 0, sequence_model_setup, test_one_tag, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/OneTag", Fixture, 0, shared_model_setup, test_one_tag, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/TwoTags", Fixture, 0, sequence_model_setup, test_two_tags, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/TwoTags", Fixture, 0, shared_model_setup, test_two_tags, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/LateTag", Fixture, 0, sequence_model_setup, test_late_tag, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/LateTag", Fixture, 0, shared_model_setup, test_late_tag, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/DestroyFunc", Fixture, 0, sequence_model_setup, test_destroy_tag, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/DestroyFunc", Fixture, 0, shared_model_setup, test_destroy_tag, shared_model_teardown); g_test_add (SEQUENCE_MODEL_DOMAIN"/TagAccessInRowRemovedHandler", Fixture, 0, sequence_model_setup, test_tag_access_in_row_removed_handler, sequence_model_teardown); g_test_add (SHARED_MODEL_DOMAIN"/TagAccessInRowRemovedHandler", Fixture, 0, shared_model_setup, test_tag_access_in_row_removed_handler, shared_model_teardown); } static void sequence_model_setup (Fixture *fix, gconstpointer data) { fix->m = dee_sequence_model_new (); dee_model_set_schema (fix->m, "i", "s", NULL); } static void sequence_model_teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->m); fix->m = NULL; } static void shared_model_setup (Fixture *fix, gconstpointer data) { fix->m = dee_shared_model_new ("org.example.ThisIsNotATest"); dee_model_set_schema (fix->m, "i", "s", NULL); } static void shared_model_teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->m); fix->m = NULL; } static void test_no_tags (Fixture *fix, gconstpointer data) { DeeModelIter *iter; gpointer tag; iter = dee_model_append (fix->m, 27, "Hello"); /* Check that getting an undefined tag fails gracefully */ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { tag = NULL; tag = dee_model_get_tag (fix->m, iter, GUINT_TO_POINTER (123)); g_assert (tag == NULL); exit (0); /* successful test run */ } g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Unable to look up tag. No tags registered on DeeSequenceModel*"); /* Ditto for setting undefined tags */ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { dee_model_set_tag (fix->m, iter, GUINT_TO_POINTER (123), GUINT_TO_POINTER (321)); exit (0); /* successful test run */ } g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Unable to look up tag. No tags registered on DeeSequenceModel*"); } static void test_one_tag (Fixture *fix, gconstpointer data) { DeeModelTag *tag; DeeModelIter *iter1, *iter2; tag = dee_model_register_tag (fix->m, NULL); iter1 = dee_model_append (fix->m, 27, "Hello"); iter2 = dee_model_append (fix->m, 68, "world"); /* Set+Get */ dee_model_set_tag (fix->m, iter1, tag, "tag1"); dee_model_set_tag (fix->m, iter2, tag, "tag2"); g_assert_cmpstr ("tag1", ==, dee_model_get_tag (fix->m, iter1, tag)); g_assert_cmpstr ("tag2", ==, dee_model_get_tag (fix->m, iter2, tag)); /* Clear a tag */ dee_model_clear_tag (fix->m, iter1, tag); g_assert (dee_model_get_tag (fix->m, iter1, tag) == NULL); g_assert_cmpstr ("tag2", ==, dee_model_get_tag (fix->m, iter2, tag)); /* Override exiting */ dee_model_set_tag (fix->m, iter2, tag, "tag3"); g_assert (dee_model_get_tag (fix->m, iter1, tag) == NULL); g_assert_cmpstr ("tag3", ==, dee_model_get_tag (fix->m, iter2, tag)); /* Check that getting an undefined tag fails gracefully */ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { tag = NULL; tag = dee_model_get_tag (fix->m, iter1, GUINT_TO_POINTER (123)); g_assert (tag == NULL); exit (0); /* successful test run */ } g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Unable to find tag 123*"); /* Ditto for setting undefined tags */ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { dee_model_set_tag (fix->m, iter1, GUINT_TO_POINTER (123), GUINT_TO_POINTER (321)); exit (0); /* successful test run */ } g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Unable to find tag 123*"); } static void test_two_tags (Fixture *fix, gconstpointer data) { DeeModelTag *tag1, *tag2; DeeModelIter *iter1, *iter2; tag1 = dee_model_register_tag (fix->m, NULL); tag2 = dee_model_register_tag (fix->m, (GDestroyNotify) g_free); iter1 = dee_model_append (fix->m, 27, "Hello"); iter2 = dee_model_append (fix->m, 68, "world"); dee_model_set_tag (fix->m, iter1, tag1, "tag1-value1"); dee_model_set_tag (fix->m, iter2, tag1, "tag1-value2"); dee_model_set_tag (fix->m, iter1, tag2, g_strdup ("tag2-value1")); dee_model_set_tag (fix->m, iter2, tag2, g_strdup ("tag2-value2")); g_assert_cmpstr ("tag1-value1", ==, dee_model_get_tag (fix->m, iter1, tag1)); g_assert_cmpstr ("tag1-value2", ==, dee_model_get_tag (fix->m, iter2, tag1)); g_assert_cmpstr ("tag2-value1", ==, dee_model_get_tag (fix->m, iter1, tag2)); g_assert_cmpstr ("tag2-value2", ==, dee_model_get_tag (fix->m, iter2, tag2)); /* Clear one tag. The rest should be unchanged */ dee_model_clear_tag (fix->m, iter1, tag1); g_assert (dee_model_get_tag (fix->m, iter1, tag1) == NULL); g_assert_cmpstr ("tag1-value2", ==, dee_model_get_tag (fix->m, iter2, tag1)); g_assert_cmpstr ("tag2-value1", ==, dee_model_get_tag (fix->m, iter1, tag2)); g_assert_cmpstr ("tag2-value2", ==, dee_model_get_tag (fix->m, iter2, tag2)); } static void test_late_tag (Fixture *fix, gconstpointer data) { /* The point here is to try and register a tag *after* we begun populating * the model */ DeeModelTag *tag1, *tag2; DeeModelIter *iter1, *iter2; tag1 = dee_model_register_tag (fix->m, NULL); iter1 = dee_model_append (fix->m, 27, "Hello"); iter2 = dee_model_append (fix->m, 68, "world"); /* Start working randomly with the model */ dee_model_set_tag (fix->m, iter1, tag1, "tag1-value1"); g_assert_cmpstr ("tag1-value1", ==, dee_model_get_tag (fix->m, iter1, tag1)); g_assert (dee_model_get_tag (fix->m, iter2, tag1) == NULL); /* With some rows and tags in the model, now register another tag. * Assert that all rows have this tag in unset state */ tag2 = dee_model_register_tag (fix->m, (GDestroyNotify) g_free); g_assert (dee_model_get_tag (fix->m, iter1, tag2) == NULL); g_assert (dee_model_get_tag (fix->m, iter2, tag2) == NULL); /* Set the new tag and verify the complete state */ dee_model_set_tag (fix->m, iter1, tag2, g_strdup ("tag2-value1")); dee_model_set_tag (fix->m, iter2, tag2, g_strdup ("tag2-value2")); g_assert_cmpstr ("tag1-value1", ==, dee_model_get_tag (fix->m, iter1, tag1)); g_assert (dee_model_get_tag (fix->m, iter2, tag1) == NULL); g_assert_cmpstr ("tag2-value1", ==, dee_model_get_tag (fix->m, iter1, tag2)); g_assert_cmpstr ("tag2-value2", ==, dee_model_get_tag (fix->m, iter2, tag2)); } static void destroy_tag (gpointer tag_value) { /* A GDestroyNotify that twiddles with a string */ ((gchar *) tag_value)[0] = '*'; } static void test_destroy_tag (Fixture *fix, gconstpointer data) { /* Assert that the destroy function is indeed called */ gchar *tag_value = g_strdup ("#"); DeeModelTag *tag; DeeModelIter *iter1; tag = dee_model_register_tag (fix->m, destroy_tag); iter1 = dee_model_append (fix->m, 27, "Hello"); /* Set+Get */ dee_model_set_tag (fix->m, iter1, tag, tag_value); g_assert_cmpstr ("#", ==, dee_model_get_tag (fix->m, iter1, tag)); /* Assert destroy func triggered. Changing "#" to "*" */ dee_model_clear_tag (fix->m, iter1, tag); g_assert (dee_model_get_tag (fix->m, iter1, tag) == NULL); g_assert_cmpstr ("*", ==, tag_value); /* Change the annotation back to "#" and make sure that destroy is also * called when we remove the row */ tag_value[0] = '#'; dee_model_set_tag (fix->m, iter1, tag, tag_value); g_assert_cmpstr ("#", ==, dee_model_get_tag (fix->m, iter1, tag)); dee_model_remove (fix->m, iter1); g_assert_cmpstr ("*", ==, tag_value); g_free (tag_value); } static int row_removed_handler_called = 0; static void row_removed_handler (DeeModel *model, DeeModelIter *iter, DeeModelTag *tag) { /* Assert that we can read the tag before it's freed. We use a dummy * free that sets the tag to "*" to detect this */ g_assert_cmpstr (dee_model_get_tag (model, iter, tag), ==, "#"); row_removed_handler_called = 1; } static void test_tag_access_in_row_removed_handler (Fixture *fix, gconstpointer data) { /* Check that we can access a tag in the row-removed handler, * before the tag is destroyed */ gchar *tag_value = g_strdup ("#"); DeeModelTag *tag; DeeModelIter *iter1; tag = dee_model_register_tag (fix->m, destroy_tag); iter1 = dee_model_append (fix->m, 27, "Hello"); dee_model_set_tag (fix->m, iter1, tag, tag_value); g_signal_connect (fix->m, "row-removed", G_CALLBACK (row_removed_handler), tag); dee_model_clear (fix->m); g_assert_cmpint (row_removed_handler_called, ==, 1); g_assert_cmpstr ("*", ==, tag_value); g_free (tag_value); } dee-1.2.7+15.04.20150304/tests/model-helper-add3rows.c0000644000015300001610000000716612475676210022240 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include guint64 before_begin_seqnum, before_end_seqnum, after_begin_seqnum, after_end_seqnum; static void _begin_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { before_begin_seqnum = begin_seqnum; before_end_seqnum = end_seqnum; } static void _end_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { after_begin_seqnum = begin_seqnum; after_end_seqnum = end_seqnum; } static void _row_added (DeeModel *model, DeeModelIter *iter, GSList **rows_so_far) { /* Yes, I _know_ that append() is slow, but this is a test! */ *rows_so_far = g_slist_append (*rows_so_far, iter); } /* Expects and empty clone and three rows-added signals */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; GSList *rows_added; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); g_signal_connect (model, "begin-transaction", G_CALLBACK (_begin_txn), NULL); g_signal_connect (model, "end-transaction", G_CALLBACK (_end_txn), NULL); /* Wait until we find the leader */ if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for model to synchronize"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 0); /* Listen for changes */ rows_added = NULL; g_signal_connect (model, "row-added", G_CALLBACK (_row_added), &rows_added); /* Wait for some RowsAdded signals */ gtx_yield_main_loop (1000); /* Check that we got what we expected */ g_assert_cmpint (g_slist_length (rows_added), == , 3); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); iter = (DeeModelIter*) g_slist_nth (rows_added, 0)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "zero"); iter = (DeeModelIter*) g_slist_nth (rows_added, 1)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "one"); iter = (DeeModelIter*) g_slist_nth (rows_added, 2)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 2); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "two"); gtx_assert_last_unref (model); g_slist_free (rows_added); g_assert (before_begin_seqnum == after_begin_seqnum); g_assert (before_end_seqnum == after_end_seqnum); g_assert_cmpint (0, ==, (guint) before_begin_seqnum); g_assert_cmpint (3, ==, (guint) before_end_seqnum); return 0; } dee-1.2.7+15.04.20150304/tests/test-resource-manager.c0000644000015300001610000001451612475676210022350 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include #include typedef struct { DeeModel *orig; DeeModel *copy; DeeResourceManager *rm; } Fixture; static void sequence_model_setup (Fixture *fix, gconstpointer data); static void sequence_model_teardown (Fixture *fix, gconstpointer data); static void shared_model_setup (Fixture *fix, gconstpointer data); static void shared_model_teardown (Fixture *fix, gconstpointer data); static void test_model_persistence (Fixture *fix, gconstpointer data); static void test_resource_manager_default (Fixture *fix, gconstpointer data); void test_resource_manager_create_suite (void) { #define DOMAIN "/ResourceManager" g_test_add (DOMAIN"/Default", Fixture, 0, NULL, test_resource_manager_default, NULL); g_test_add (DOMAIN"/SequenceModel", Fixture, 0, sequence_model_setup, test_model_persistence, sequence_model_teardown); g_test_add (DOMAIN"/SharedModel", Fixture, 0, shared_model_setup, test_model_persistence, shared_model_teardown); } static void sequence_model_setup (Fixture *fix, gconstpointer data) { fix->orig = dee_sequence_model_new (); dee_model_set_schema (fix->orig, "i", "s", NULL); fix->copy = NULL; fix->rm = dee_file_resource_manager_new ("dee-test-resource-manager/nested/resources"); g_assert (DEE_IS_SEQUENCE_MODEL (fix->orig)); } static void sequence_model_teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->orig); fix->orig = NULL; if (fix->copy) g_object_unref (fix->copy); fix->copy = NULL; g_object_unref (fix->rm); fix->rm = NULL; } static void shared_model_setup (Fixture *fix, gconstpointer data) { fix->orig = dee_shared_model_new ("org.example.ThisIsNotATest"); dee_model_set_schema (fix->orig, "i", "s", NULL); fix->copy = NULL; fix->rm = dee_file_resource_manager_new ("dee-test-resource-manager/nested/resources"); g_assert (DEE_IS_SHARED_MODEL (fix->orig)); } static void shared_model_teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->orig); fix->orig = NULL; if (fix->copy) g_object_unref (fix->copy); fix->copy = NULL; g_object_unref (fix->rm); fix->rm = NULL; } static void dee_assert_cmpmodel (DeeModel *m1, DeeModel *m2) { guint i, j, n_cols, n_rows; DeeModelIter *row1, *row2; GVariant *v1, *v2; g_assert (m1 != NULL); g_assert (m2 != NULL); g_assert (DEE_IS_MODEL (m1)); g_assert (DEE_IS_MODEL (m2)); g_assert_cmpint (dee_model_get_n_columns (m1), ==, dee_model_get_n_columns (m2)); g_assert_cmpint (dee_model_get_n_rows (m1), ==, dee_model_get_n_rows (m2)); n_cols = dee_model_get_n_columns (m1); for (i = 0; i < n_cols; i++) { g_assert_cmpstr (dee_model_get_column_schema (m1, i), ==, dee_model_get_column_schema (m1, i)); } n_rows = dee_model_get_n_rows (m1); for (i = 0; i < n_rows; i++) { row1 = dee_model_get_iter_at_row (m1, i); row2 = dee_model_get_iter_at_row (m2, i); for (j = 0; j < n_cols; j++) { v1 = dee_model_get_value (m1, row1, j); v2 = dee_model_get_value (m2, row2, j); g_assert (g_variant_equal (v1, v2)); g_variant_unref (v1); g_variant_unref (v2); } } if (DEE_IS_SERIALIZABLE_MODEL (m1) && DEE_IS_SERIALIZABLE_MODEL (m2)) { g_assert_cmpuint(dee_serializable_model_get_seqnum (m1), ==, dee_serializable_model_get_seqnum (m2)); } if (DEE_IS_SHARED_MODEL (m1) && DEE_IS_SHARED_MODEL (m2)) { g_assert_cmpstr (dee_shared_model_get_swarm_name (DEE_SHARED_MODEL (m1)), ==, dee_shared_model_get_swarm_name (DEE_SHARED_MODEL (m2))); } } static void test_model_persistence (Fixture *fix, gconstpointer data) { GError *error = NULL; const gchar *resource_name = "com.canonical.Dee.TestResource"; dee_model_append (fix->orig, 27, "Hello world"); dee_model_append (fix->orig, 68, "Hola Mars"); dee_resource_manager_store (fix->rm, DEE_SERIALIZABLE (fix->orig), resource_name, &error); if (error) { g_critical ("Failed to write serializable model to resource %s: %s 111111 %s %i", resource_name, error->message, g_quark_to_string(error->domain), error->code); g_error_free (error); return; } fix->copy = DEE_MODEL (dee_resource_manager_load(fix->rm, resource_name, &error)); if (error) { g_critical ("Failed to read serializable model from resource %s: %s", resource_name, error->message); g_error_free (error); return; } if (fix->copy == NULL) { g_critical ("No parser registered for serialized model"); } dee_assert_cmpmodel (fix->orig, fix->copy); } static void test_resource_manager_default (Fixture *fix, gconstpointer data) { DeeResourceManager *manager; GError *error; GObject *result; gchar *primary_path; error = NULL; manager = dee_resource_manager_get_default (); g_object_get (manager, "primary-path", &primary_path, NULL); g_assert (g_str_has_suffix (primary_path, "resources")); g_free (primary_path); result = dee_resource_manager_load (manager, "com.this.hopefully.doesnt.exist.com", &error); g_assert (result == NULL); /* loading non-existing resource just returns NULL, doesn't throw error */ g_assert_no_error (error); } dee-1.2.7+15.04.20150304/tests/test-icu.c0000644000015300001610000000673012475676210017670 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #include #include typedef struct { DeeICUTermFilter *filter; } Fixture; static void setup (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup (Fixture *fix, gconstpointer data) { fix->filter = NULL; } static void teardown (Fixture *fix, gconstpointer data) { if (fix->filter) dee_icu_term_filter_destroy (fix->filter); } static void test_ascii_folder (Fixture *fix, gconstpointer data) { gchar *result; fix->filter = dee_icu_term_filter_new_ascii_folder(); result = dee_icu_term_filter_apply (fix->filter, "Hello world"); g_assert_cmpstr (result, ==, "Hello world"); g_free (result); result = dee_icu_term_filter_apply (fix->filter, "øöô"); g_assert_cmpstr (result, ==, "ooo"); g_free (result); result = dee_icu_term_filter_apply (fix->filter, "Θεοδωράτου, Ελένη"); g_assert_cmpstr (result, ==, "Theodoratou, Elene"); g_free (result); result = dee_icu_term_filter_apply (fix->filter, "Догилева, Татьяна"); g_assert_cmpstr (result, ==, "Dogileva, Tatʹana"); g_free (result); result = dee_icu_term_filter_apply (fix->filter, "김, 국삼"); g_assert_cmpstr (result, ==, "gim, gugsam"); g_free (result); result = dee_icu_term_filter_apply (fix->filter, "たけだ, まさゆき"); g_assert_cmpstr (result, ==, "takeda, masayuki"); g_free (result); /* One last time to honor the French ;-) */ result = dee_icu_term_filter_apply (fix->filter, "Est-ce que tes enfants sont de bons élèves? Ça va pour eux?"); g_assert_cmpstr (result, ==, "Est-ce que tes enfants sont de bons eleves? Ca va pour eux?"); g_free (result); } static void test_bad_id (Fixture *fix, gconstpointer data) { GError *error = NULL; fix->filter = dee_icu_term_filter_new ("*-sad ???", NULL, &error); g_assert (fix->filter == NULL); g_assert (error != NULL); g_assert_cmpint (error->domain, ==, DEE_ICU_ERROR); g_assert_cmpint (error->code, ==, DEE_ICU_ERROR_BAD_ID); g_error_free (error); } static void test_bad_rule (Fixture *fix, gconstpointer data) { GError *error = NULL; fix->filter = dee_icu_term_filter_new (NULL, "*-sad ???", &error); g_assert (fix->filter == NULL); g_assert (error != NULL); g_assert_cmpint (error->domain, ==, DEE_ICU_ERROR); g_assert_cmpint (error->code, ==, DEE_ICU_ERROR_BAD_RULE); g_error_free (error); } void test_icu_create_suite (void) { #define DOMAIN "/ICU/TermFilter" g_test_add (DOMAIN"/Default/AsciiFolder", Fixture, 0, setup, test_ascii_folder, teardown); g_test_add (DOMAIN"/Default/BadId", Fixture, 0, setup, test_bad_id, teardown); g_test_add (DOMAIN"/Default/BadRule", Fixture, 0, setup, test_bad_rule, teardown); } dee-1.2.7+15.04.20150304/tests/test-glist-result-set.c0000644000015300001610000000770512475676210022342 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include DeeResultSet* dee_glist_result_set_new (GList *rows, DeeModel *model, GObject *row_owner); typedef struct { DeeResultSet *results; DeeModel *model; GList *list; } Fixture; static void setup (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup (Fixture *fix, gconstpointer data) { DeeModelIter *iter; fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", NULL); fix->list = NULL; iter = dee_model_append (fix->model, "Hello world", 27); fix->list = g_list_append (fix->list, iter); iter = dee_model_append (fix->model, "how are you", 28); fix->list = g_list_append (fix->list, iter); iter = dee_model_append (fix->model, "today", 28); fix->list = g_list_append (fix->list, iter); fix->results = dee_glist_result_set_new (fix->list, fix->model, NULL); } static void teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->results); g_object_unref (fix->model); g_list_free (fix->list); fix->results = NULL; fix->model = NULL; fix->list = NULL; } static void test_three (Fixture *fix, gconstpointer data) { DeeModelIter *iter; g_assert (DEE_IS_RESULT_SET (fix->results)); g_assert_cmpint (dee_result_set_get_n_rows (fix->results), ==, 3); g_assert_cmpint (dee_result_set_tell(fix->results), ==, 0); g_assert (dee_result_set_has_next (fix->results)); iter = dee_result_set_next (fix->results); g_assert (iter != NULL); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 0), ==, "Hello world"); g_assert_cmpint (dee_result_set_get_n_rows (fix->results), ==, 3); g_assert_cmpint (dee_result_set_tell(fix->results), ==, 1); g_assert (dee_result_set_has_next (fix->results)); iter = dee_result_set_next (fix->results); g_assert (iter != NULL); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 0), ==, "how are you"); g_assert_cmpint (dee_result_set_get_n_rows (fix->results), ==, 3); g_assert_cmpint (dee_result_set_tell(fix->results), ==, 2); g_assert (dee_result_set_has_next (fix->results)); iter = dee_result_set_next (fix->results); g_assert (iter != NULL); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 0), ==, "today"); g_assert_cmpint (dee_result_set_get_n_rows (fix->results), ==, 3); g_assert_cmpint (dee_result_set_tell(fix->results), ==, 3); g_assert (!dee_result_set_has_next (fix->results)); g_assert (dee_result_set_get_model (fix->results) == fix->model); } static void test_empty (Fixture *fix, gconstpointer data) { DeeResultSet *results; results = dee_glist_result_set_new (NULL, fix->model, NULL); g_assert (DEE_IS_RESULT_SET (results)); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 0); g_assert_cmpint (dee_result_set_tell(results), ==, 0); g_assert (!dee_result_set_has_next (results)); g_object_unref (results); } void test_glist_result_set_create_suite (void) { #define DOMAIN "/Index/ResultSet/GList" g_test_add (DOMAIN"/Test3", Fixture, 0, setup, test_three, teardown); g_test_add (DOMAIN"/Test0", Fixture, 0, setup, test_empty, teardown); } dee-1.2.7+15.04.20150304/tests/model-helper-resync3rows.c0000644000015300001610000000741012475676210023003 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by: * Michal Hruby * */ #include "config.h" #include #include #include #include static void row_added (DeeModel *model, DeeModelIter *iter, gpointer data) { gint *num_added = (gint*) data; (*num_added)++; } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; gint num_added; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif g_set_prgname ("model-helper"); if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); num_added = 0; g_signal_connect (model, "row-added", G_CALLBACK (row_added), &num_added); if (gtx_wait_for_signal (G_OBJECT (model), 10000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); iter = dee_model_get_iter_at_row (model, 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "zero"); iter = dee_model_get_iter_at_row (model, 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "one"); iter = dee_model_get_iter_at_row (model, 2); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "two"); /* The swarm leader goes away and later reappears, * swarm-leader prop should toggle */ if (gtx_wait_for_signal (G_OBJECT (dee_shared_model_get_peer (DEE_SHARED_MODEL (model))), 10000, "notify::swarm-leader", NULL)) g_error ("Helper model timed out waiting for 'swarm-leader' notification"); if (!dee_shared_model_is_leader (DEE_SHARED_MODEL (model))) g_error ("Helper didn't become leader"); if (gtx_wait_for_signal (G_OBJECT (dee_shared_model_get_peer (DEE_SHARED_MODEL (model))), 10000, "notify::swarm-leader", NULL)) g_error ("Helper model timed out waiting for second 'swarm-leader' notification"); if (dee_shared_model_is_leader (DEE_SHARED_MODEL (model))) g_error ("Helper didn't loose swarm leadership"); /* And let's wait for synchronization again */ if (gtx_wait_for_signal (G_OBJECT (model), 10000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); iter = dee_model_get_iter_at_row (model, 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "zero"); iter = dee_model_get_iter_at_row (model, 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "one"); iter = dee_model_get_iter_at_row (model, 2); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "two"); gtx_assert_last_unref (model); g_assert_cmpint (num_added, >, 3); return 0; } dee-1.2.7+15.04.20150304/tests/test-model-interactions.c0000644000015300001610000007254612475676210022720 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by Mikkel Kamstrup Erlandsen * */ #include #include #include #include #define TIMEOUT 500 #define MODEL_NAME "com.canonical.DeeModel.Tests.Interactions" /* A command line that launches the appropriaye model-helper-* executable, * giving $name as first argument */ #define MODEL_HELPER(helper,name) \ (gchar *[]) { "./model-helper-"#helper, name, NULL } typedef struct { DeeModel *model; } Fixture; static void model_setup (Fixture *fix, gconstpointer data); static void model_teardown (Fixture *fix, gconstpointer data); static void model_setup_null (Fixture *fix, gconstpointer data); static void model_teardown_null (Fixture *fix, gconstpointer data); static void model_setup_with_meta (Fixture *fix, gconstpointer data); static void test_ready (Fixture *fix, gconstpointer data); static void test_clone (Fixture *fix, gconstpointer data); static void test_clone_meta (Fixture *fix, gconstpointer data); static void test_row_added (Fixture *fix, gconstpointer data); static void test_row_changed (Fixture *fix, gconstpointer data); static void test_row_removed (Fixture *fix, gconstpointer data); static void test_model_clear (Fixture *fix, gconstpointer data); static void test_clear_add (Fixture *fix, gconstpointer data); static void test_clear_add_txn (Fixture *fix, gconstpointer data); static void test_clear_then_add (Fixture *fix, gconstpointer data); static void test_add_clear (Fixture *fix, gconstpointer data); static void test_row_inserted (Fixture *fix, gconstpointer data); static void test_schemaless_leader (Fixture *fix, gconstpointer data); static void test_introspect (Fixture *fix, gconstpointer data); static void test_ownership_stealing (Fixture *fix, gconstpointer data); static void test_remote_append (Fixture *fix, gconstpointer data); static void test_disabled_writes (Fixture *fix, gconstpointer data); static void test_commit_before_clone (Fixture *fix, gconstpointer data); static void test_force_resync (Fixture *fix, gconstpointer data); static void test_manual_flush (Fixture *fix, gconstpointer data); void test_model_interactions_create_suite (void) { #define DOMAIN "/Model/Interactions" g_test_add (DOMAIN"/Ready", Fixture, 0, model_setup, test_ready, model_teardown); g_test_add (DOMAIN"/Clone", Fixture, 0, model_setup, test_clone, model_teardown); g_test_add (DOMAIN"/Clone/WithMeta", Fixture, 0, model_setup_with_meta, test_clone_meta, model_teardown); g_test_add (DOMAIN"/RowAdded", Fixture, 0, model_setup, test_row_added, model_teardown); g_test_add (DOMAIN"/RowChanged", Fixture, 0, model_setup, test_row_changed, model_teardown); g_test_add (DOMAIN"/RowRemoved", Fixture, 0, model_setup, test_row_removed, model_teardown); g_test_add (DOMAIN"/Clear", Fixture, 0, model_setup, test_model_clear, model_teardown); g_test_add (DOMAIN"/ClearAndAdd", Fixture, 0, model_setup, test_clear_add, model_teardown); g_test_add (DOMAIN"/TxnClearAndAdd", Fixture, 0, model_setup, test_clear_add_txn, model_teardown); g_test_add (DOMAIN"/ClearThenAdd", Fixture, 0, model_setup, test_clear_then_add, model_teardown); g_test_add (DOMAIN"/AddAndClear", Fixture, 0, model_setup, test_add_clear, model_teardown); g_test_add (DOMAIN"/RowInserted", Fixture, 0, model_setup, test_row_inserted, model_teardown); g_test_add (DOMAIN"/SchemalessLeader", Fixture, 0, model_setup_null, test_schemaless_leader, model_teardown_null); g_test_add (DOMAIN"/Introspect", Fixture, 0, model_setup, test_introspect, model_teardown); g_test_add (DOMAIN"/OwnershipStealing", Fixture, 0, model_setup, test_ownership_stealing, model_teardown); g_test_add (DOMAIN"/RemoteAppend", Fixture, 0, model_setup, test_remote_append, model_teardown); g_test_add (DOMAIN"/DisabledWrites", Fixture, 0, model_setup_null, test_disabled_writes, model_teardown_null); g_test_add (DOMAIN"/CommitBeforeClone", Fixture, 0, model_setup, test_commit_before_clone, model_teardown); g_test_add (DOMAIN"/ForceResync", Fixture, 0, model_setup, test_force_resync, model_teardown); g_test_add (DOMAIN"/ManualFlush", Fixture, 0, model_setup, test_manual_flush, model_teardown); } static void model_setup (Fixture *fix, gconstpointer data) { fix->model = dee_shared_model_new (MODEL_NAME); dee_model_set_schema (fix->model, "i", "s", NULL); g_assert (DEE_IS_MODEL (fix->model)); } static void model_teardown (Fixture *fix, gconstpointer data) { gtx_assert_last_unref (fix->model); } static void model_setup_null (Fixture *fix, gconstpointer data) { fix->model = NULL; } static void model_teardown_null (Fixture *fix, gconstpointer data) { g_assert (fix->model == NULL); } static void model_setup_with_meta (Fixture *fix, gconstpointer data) { GHashTable *vardict_schema; fix->model = dee_shared_model_new (MODEL_NAME); dee_model_set_schema (fix->model, "i", "s", "a{sv}", "a{sv}", NULL); dee_model_set_column_names (fix->model, "count", "title", "hints", "extra-hints", NULL); vardict_schema = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (vardict_schema, "uri", "s"); dee_model_register_vardict_schema (fix->model, 2, vardict_schema); g_assert (DEE_IS_MODEL (fix->model)); g_hash_table_unref (vardict_schema); } static void test_ready (Fixture *fix, gconstpointer data) { GParamSpec *pspec; gboolean synchronized; /* Test the GObject property reports FALSE */ g_object_get (fix->model, "synchronized", &synchronized, NULL); g_assert_cmpint (0, == , synchronized); /* Check that convenience getter now reports FALSE */ g_assert_cmpint (0, ==, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model))); if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec)) g_critical ("Model never synchronized"); else g_param_spec_unref (pspec); /* Test the GObject property reports TRUE */ g_object_get (fix->model, "synchronized", &synchronized, NULL); g_assert_cmpint (0, != , synchronized); /* Check that convenience getter now reports TRUE */ g_assert_cmpint (0, !=, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model))); if (!gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) { g_critical ("Model changed synchronization state twice"); g_param_spec_unref (pspec); } } static gboolean _add3rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_append (model, 0, "zero"); dee_model_append (model, 1, "one"); dee_model_append (model, 2, "two"); return FALSE; } static gboolean _add5rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_append (model, 0, "zero"); dee_model_append (model, 1, "one"); dee_model_append (model, 2, "two"); dee_model_append (model, 3, "three"); dee_model_append (model, 4, "four"); return FALSE; } static gboolean _change3rows (DeeModel *model) { DeeModelIter *iter; g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); iter = dee_model_get_iter_at_row (model, 0); dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_zero")); iter = dee_model_get_iter_at_row (model, 1); dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_one")); iter = dee_model_get_iter_at_row (model, 2); dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_two")); return FALSE; } /* Assumes a model with 5 rows. Removes rows 0, 4, and 2 * in that order. Accounting for rows shifts this becomes * 0, 3, and 1. Leaving the original rows 1 and 3 now in * positions 0 and 1 */ static gboolean _remove3rows (DeeModel *model) { DeeModelIter *iter0, *iter4, *iter2; DeeModelIter *orig_iter1, *orig_iter3; g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); g_return_val_if_fail (dee_model_get_n_rows (model) == 5, FALSE); iter0 = dee_model_get_iter_at_row (model, 0); iter4 = dee_model_get_iter_at_row (model, 4); iter2 = dee_model_get_iter_at_row (model, 2); orig_iter1 = dee_model_get_iter_at_row (model, 1); orig_iter3 = dee_model_get_iter_at_row (model, 3); dee_model_remove (model, iter0); dee_model_remove (model, iter4); dee_model_remove (model, iter2); g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); g_assert (dee_model_get_iter_at_row (model, 0) == orig_iter1); g_assert (dee_model_get_iter_at_row (model, 1) == orig_iter3); return FALSE; } static gboolean _insert1row (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_insert (model, 1, 27, "twentyseven"); return FALSE; } static gboolean _clear_model (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_clear (model); return FALSE; } static gboolean _add_and_clear6rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); _add3rows (model); _clear_model (model); return FALSE; } static gboolean _clear_and_add5rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); _clear_model (model); _add5rows (model); return FALSE; } static gboolean _txn_clear_and_add5rows (DeeModel *model) { DeeTransaction *txn; GError *error; g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); txn = DEE_TRANSACTION (dee_transaction_new (model)); _clear_model (DEE_MODEL (txn)); _add5rows (DEE_MODEL (txn)); error = NULL; dee_transaction_commit (txn, &error); if (error) { g_critical ("Failed to commit clear-add5 transaction: %s", error->message); } g_object_unref (txn); return FALSE; } static void test_clone (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never synchronized"); _add3rows (fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clone3rows, MODEL_NAME), 1000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); /* We test that we can do this two times */ /*if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (3rows, MODEL_NAME), 1000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0);*/ } static void test_clone_meta (Fixture *fix, gconstpointer data) { GVariant *row_buf[3]; DeeModel *model; model = fix->model; if (gtx_wait_for_signal (G_OBJECT (model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never synchronized"); dee_model_append_row (model, dee_model_build_named_row (model, row_buf, "count", 0, "title", "zero", NULL)); dee_model_append_row (model, dee_model_build_named_row (model, row_buf, "count", 1, "title", "one", NULL)); dee_model_append_row (model, dee_model_build_named_row (model, row_buf, "count", 2, "title", "two", NULL)); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clone3rows-meta, MODEL_NAME), 1000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_added (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_changed (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); g_timeout_add (500, (GSourceFunc)_change3rows, fix->model); //const gchar *cmd[] = {"dbus-monitor", NULL}; //const gchar *cmd[] = {"sleep", "1",NULL}; if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (change3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_removed (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add5rows (fix->model); g_timeout_add (500, (GSourceFunc)_remove3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (remove3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_model_clear (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); g_timeout_add (500, (GSourceFunc)_clear_model, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_clear_add (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); g_timeout_add (500, (GSourceFunc)_clear_and_add5rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3add5, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_clear_add_txn (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); g_timeout_add (500, (GSourceFunc)_txn_clear_and_add5rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3add5, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); /* Test that the local model looks as expected */ DeeModelIter *iter = dee_model_get_first_iter (fix->model); g_assert_cmpint (dee_model_get_int32 (fix->model, iter, 0), ==, 0); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "zero"); iter = dee_model_next (fix->model, iter); g_assert_cmpint (dee_model_get_int32 (fix->model, iter, 0), ==, 1); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "one"); iter = dee_model_next (fix->model, iter); g_assert_cmpint (dee_model_get_int32 (fix->model, iter, 0), ==, 2); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "two"); iter = dee_model_next (fix->model, iter); g_assert_cmpint (dee_model_get_int32 (fix->model, iter, 0), ==, 3); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "three"); iter = dee_model_next (fix->model, iter); g_assert_cmpint (dee_model_get_int32 (fix->model, iter, 0), ==, 4); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "four"); gtx_assert_last_command_status (0); } static void test_clear_then_add (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _clear_model (fix->model); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); g_timeout_add (500, (GSourceFunc)_clear_model, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_add_clear (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); g_timeout_add (500, (GSourceFunc)_add_and_clear6rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear6rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_inserted (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); g_timeout_add (500, (GSourceFunc)_insert1row, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (insert1row, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void _ready (DeeModel *model, GParamSpec *pspec, GSList **rows_so_far) { /* We must not have any rows when 'ready' is emitted */ g_assert (*rows_so_far == NULL); } static void _collect_row (DeeModel *model, DeeModelIter *iter, GSList **rows_so_far) { /* Yes, I _know_ that append() is slow, but this is a test! */ *rows_so_far = g_slist_append (*rows_so_far, iter); } /* This case must run without a Fixture */ static void test_schemaless_leader (Fixture *fix, gconstpointer data) { DeeModel *model; DeeModelIter *iter; GSList *rows_added; g_assert (fix->model == NULL); /* Set up a clean model *without* a schema. We will pick up the schema * with the first transaction from the slave. Or at least, that's what we * want to assert ;-) */ model = dee_shared_model_new (MODEL_NAME); // no set_schema() on purpose! /* Listen for changes */ rows_added = NULL; g_signal_connect (model, "row-added", G_CALLBACK (_collect_row), &rows_added); g_signal_connect (model, "notify::synchronized", G_CALLBACK (_ready), &rows_added); /* Remote process defines column types and adds two rows */ if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (schemaless, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); /* Check that we got the right schema from the peer */ g_assert_cmpint (dee_model_get_n_columns (model), ==, 2); g_assert_cmpstr (dee_model_get_column_schema (model, 0), ==, "i"); g_assert_cmpstr (dee_model_get_column_schema (model, 1), ==, "s"); /* Check that we got what we expected */ g_assert_cmpint (g_slist_length (rows_added), == , 2); g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); g_assert_cmpint (dee_model_get_n_columns (model), ==, 2); iter = (DeeModelIter*) g_slist_nth (rows_added, 0)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 27); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "skunkworks"); iter = (DeeModelIter*) g_slist_nth (rows_added, 1)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 68); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "wumbo"); gtx_assert_last_unref (model); g_slist_free (rows_added); } static void test_introspect (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (introspect, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void stealing_notify (GObject *object, GParamSpec *pspec, Fixture *fix) { g_assert (DEE_IS_PEER (object)); if (!dee_peer_is_swarm_leader (DEE_PEER (object))) { g_object_set_data (G_OBJECT (fix->model), "stealing-success", GINT_TO_POINTER (1)); } } static void test_ownership_stealing (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); /* We should be leader before starting the helper process */ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model))); /* But loose it later on */ g_signal_connect (dee_shared_model_get_peer (DEE_SHARED_MODEL (fix->model)), "notify::swarm-leader", G_CALLBACK (stealing_notify), fix); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (replace, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); gtx_flush_sources (FALSE); g_assert (g_object_get_data (G_OBJECT (fix->model), "stealing-success")); g_assert_cmpuint (dee_model_get_n_rows (fix->model), >, 0); } static void changeset_signal (DeeModel *model, gboolean *value) { *value = TRUE; } static void test_remote_append (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); /* We should be leader before starting the helper process */ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model))); g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 0); gboolean got_changeset_start = FALSE; gboolean got_changeset_finish = FALSE; g_signal_connect (fix->model, "changeset-started", G_CALLBACK (changeset_signal), &got_changeset_start); g_signal_connect (fix->model, "changeset-finished", G_CALLBACK (changeset_signal), &got_changeset_finish); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (append1, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); /* There should be a new row in the model */ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1); g_assert (got_changeset_start); g_assert (got_changeset_finish); } static void test_disabled_writes (Fixture *fix, gconstpointer data) { DeePeer *peer; DeeModelIter *iter; peer = dee_peer_new (MODEL_NAME); fix->model = DEE_MODEL ( g_object_new (DEE_TYPE_SHARED_MODEL, "peer", peer, "back-end", dee_sequence_model_new (), "access-mode", DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE, NULL)); dee_model_set_schema (fix->model, "i", "s", NULL); g_object_unref (G_OBJECT (peer)); if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); /* We should be leader before starting the helper process */ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model))); dee_model_prepend (fix->model, 81, "eightyone"); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (append1, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); /* The peer tried to append a row, but we should have ignored that */ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1); iter = dee_model_get_first_iter (fix->model); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "eightyone"); gtx_assert_last_unref (fix->model); fix->model = NULL; } static gboolean quit_loop (GMainLoop *ml) { g_main_loop_quit (ml); return FALSE; } static void child_quit (GPid pid, gint status, gpointer user_data) { GMainLoop *ml; gpointer *data; data = user_data; ml = (GMainLoop*) data[0]; data[1] = GINT_TO_POINTER (status); g_main_loop_quit (ml); } static void test_commit_before_clone (Fixture *fix, gconstpointer data) { GPid pid; GMainLoop *ml; gpointer *wait_data; if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); /* need special handling of synchronization - commit must be emmitted after * the client is up (and possibly waiting for clone) */ g_spawn_async (TESTDIR, MODEL_HELPER (clone3rows, MODEL_NAME), NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL); ml = g_main_loop_new (NULL, FALSE); wait_data = g_new0 (gpointer, 2); wait_data[0] = ml; wait_data[1] = GINT_TO_POINTER (837); g_child_watch_add (pid, child_quit, wait_data); /* wait for the helper process to start up, then flush the commits */ g_usleep (500000); /* don't wait indefinitely for the child */ g_timeout_add (5000, (GSourceFunc) quit_loop, ml); g_main_loop_run (ml); if (GPOINTER_TO_INT (wait_data[1]) == 837) { g_critical ("Model helper timed out"); } else if (GPOINTER_TO_INT (wait_data[1]) != 0) { g_critical ("Model helper returned error"); } g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 3); } static void test_force_resync (Fixture *fix, gconstpointer data) { GPid pid; GMainLoop *ml; gpointer *wait_data; DeePeer *leader_peer; if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); /* need special handling of synchronization */ g_spawn_async (TESTDIR, MODEL_HELPER (resync3rows, MODEL_NAME), NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL); ml = g_main_loop_new (NULL, FALSE); wait_data = g_new0 (gpointer, 2); wait_data[0] = ml; wait_data[1] = GINT_TO_POINTER (837); g_child_watch_add (pid, child_quit, wait_data); /* We don't really have a better way atm. Peer discovery doesn't work * with latest changes done to DBus (no watching of method calls from other * processes) */ gtx_yield_main_loop (500); /* 500ms yield */ /* Peer should be synced now, let's throw away our model */ gtx_assert_last_unref (fix->model); gtx_yield_main_loop (50); /* 50ms yield */ /* And re-own it */ leader_peer = DEE_PEER (g_object_new (DEE_TYPE_PEER, "swarm-name", MODEL_NAME, "swarm-owner", TRUE, NULL)); fix->model = dee_shared_model_new_for_peer (leader_peer); dee_model_set_schema (fix->model, "i", "s", NULL); g_assert (DEE_IS_MODEL (fix->model)); if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); /* don't wait indefinitely for the child */ g_timeout_add (5000, (GSourceFunc) quit_loop, ml); g_main_loop_run (ml); if (GPOINTER_TO_INT (wait_data[1]) == 837) { g_critical ("Model helper timed out"); } else if (GPOINTER_TO_INT (wait_data[1]) != 0) { g_critical ("Model helper returned error"); } g_assert_cmpint (dee_model_get_n_rows (fix->model), ==, 3); } static void test_manual_flush (Fixture *fix, gconstpointer data) { DeeSharedModel *sm; sm = DEE_SHARED_MODEL (fix->model); dee_shared_model_set_flush_mode (sm, DEE_SHARED_MODEL_FLUSH_MODE_MANUAL); _add5rows (fix->model); /* A timeout would normally cause the model to flush, but with manual * flushing it shouldn't */ gtx_yield_main_loop (50); /* 50ms yield */ g_assert_cmpuint (dee_shared_model_flush_revision_queue_sync (sm), >, 0); } dee-1.2.7+15.04.20150304/tests/test-model-complex-column.c0000644000015300001610000001102312475676210023137 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * Neil Jagdish Patel * */ #include #include #include typedef struct { DeeModel *model; } ColumnFixture; static void column_setup (ColumnFixture *fix, gconstpointer data); static void column_teardown (ColumnFixture *fix, gconstpointer data); static void proxy_column_setup (ColumnFixture *fix, gconstpointer data); static void proxy_column_teardown (ColumnFixture *fix, gconstpointer data); static void test_column_allocation (ColumnFixture *fix, gconstpointer data); static void test_set_get (ColumnFixture *fix, gconstpointer data); void test_model_complex_column_create_suite (void) { #define SEQ_DOMAIN "/Model/Sequence/ComplexColumn" #define PROXY_DOMAIN "/Model/Proxy/ComplexColumn" g_test_add (SEQ_DOMAIN"/Allocation", ColumnFixture, 0, column_setup, test_column_allocation, column_teardown); g_test_add (PROXY_DOMAIN"/Allocation", ColumnFixture, 0, proxy_column_setup, test_column_allocation, proxy_column_teardown); g_test_add (SEQ_DOMAIN"/SetGet", ColumnFixture, 0, column_setup, test_set_get, column_teardown); g_test_add (PROXY_DOMAIN"/SetGet", ColumnFixture, 0, proxy_column_setup, test_set_get, proxy_column_teardown); } static void column_setup (ColumnFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "as", "(ii)", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); g_assert_cmpint (2, ==, dee_model_get_n_columns (fix->model)); g_assert_cmpstr ("as", ==, dee_model_get_column_schema (fix->model, 0)); g_assert_cmpstr ("(ii)", ==, dee_model_get_column_schema(fix->model, 1)); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); } static void column_teardown (ColumnFixture *fix, gconstpointer data) { g_object_unref (fix->model); } static void proxy_column_setup (ColumnFixture *fix, gconstpointer data) { column_setup (fix, data); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", fix->model, NULL); g_assert (DEE_IS_PROXY_MODEL (fix->model)); } static void proxy_column_teardown (ColumnFixture *fix, gconstpointer data) { g_assert (DEE_IS_PROXY_MODEL (fix->model)); g_object_unref (fix->model); } static void test_column_allocation (ColumnFixture *fix, gconstpointer data) { g_assert (DEE_IS_MODEL (fix->model)); } static void test_set_get (ColumnFixture *fix, gconstpointer data) { DeeModel *model = fix->model; DeeModelIter *iter; GVariantBuilder as; GVariant *value, *tmp, *col1, *col2; gint32 i1, i2; g_variant_builder_init (&as, G_VARIANT_TYPE ("as")); g_variant_builder_add (&as, "s", "Hello"); g_variant_builder_add (&as, "s", "World"); dee_model_append (fix->model, g_variant_builder_end (&as), g_variant_new ("(ii)", 27, 68)); g_assert (dee_model_get_n_rows (model) == 1); iter = dee_model_get_first_iter (model); dee_model_get (model, iter, &col1, &col2); value = dee_model_get_value (model, iter, 0); g_assert (value == col1); g_assert_cmpstr ("as", ==, g_variant_get_type_string (value)); g_assert_cmpint (2, ==, g_variant_n_children(value)); tmp = g_variant_get_child_value (value, 0); g_assert_cmpstr ("Hello", ==, g_variant_get_string (tmp, NULL)); g_variant_unref (tmp); tmp = g_variant_get_child_value (value, 1); g_assert_cmpstr ("World", ==, g_variant_get_string (tmp, NULL)); g_variant_unref (tmp); g_variant_unref (value); value = dee_model_get_value (model, iter, 1); g_assert (value == col2); g_variant_get (value, "(ii)", &i1, &i2); g_assert_cmpint (27, ==, i1); g_assert_cmpint (68, ==, i2); g_variant_unref (value); g_variant_unref (col1); g_variant_unref (col2); } dee-1.2.7+15.04.20150304/tests/test-index.c0000644000015300001610000005312112475676210020213 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010-2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include #include typedef struct { DeeModel *model; DeeIndex *index; } Fixture; static void setup_hash (Fixture *fix, gconstpointer data); static void setup_tree (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup_hash (Fixture *fix, gconstpointer data) { DeeAnalyzer *analyzer = dee_analyzer_new (); DeeModelReader reader; dee_model_reader_new_for_string_column (0, &reader); fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", NULL); fix->index = DEE_INDEX (dee_hash_index_new(fix->model, analyzer, &reader)); g_object_unref (analyzer); } static void setup_text_hash (Fixture *fix, gconstpointer data) { DeeAnalyzer *analyzer = DEE_ANALYZER (dee_text_analyzer_new ()); DeeModelReader reader; dee_model_reader_new_for_string_column (0, &reader); fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", NULL); fix->index = DEE_INDEX (dee_hash_index_new(fix->model, analyzer, &reader)); g_object_unref (analyzer); } static void setup_tree (Fixture *fix, gconstpointer data) { DeeAnalyzer *analyzer = dee_analyzer_new (); DeeModelReader reader; dee_model_reader_new_for_string_column (0, &reader); fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", NULL); fix->index = DEE_INDEX (dee_tree_index_new(fix->model, analyzer, &reader)); g_object_unref (analyzer); } static void setup_text_tree (Fixture *fix, gconstpointer data) { DeeAnalyzer *analyzer = DEE_ANALYZER (dee_text_analyzer_new ()); DeeModelReader reader; dee_model_reader_new_for_string_column (0, &reader); fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", NULL); fix->index = DEE_INDEX (dee_tree_index_new(fix->model, analyzer, &reader)); g_object_unref (analyzer); } static void teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->index); g_object_unref (fix->model); fix->index = NULL; fix->model = NULL; } static void test_empty (Fixture *fix, gconstpointer data) { g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); g_assert (NULL == dee_index_lookup_one (fix->index, "foobar")); } static void test_one (Fixture *fix, gconstpointer data) { DeeModelIter *orig_iter, *iter; DeeResultSet *results; orig_iter = dee_model_append (fix->model, "Hello world", 27); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 1); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 1); results = dee_index_lookup (fix->index, "Hello", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 0); g_object_unref (results); results = dee_index_lookup (fix->index, "Hello world", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); iter = dee_result_set_next (results); g_assert (iter == orig_iter); g_assert (NULL == dee_index_lookup_one (fix->index, "foobar")); g_assert (orig_iter == dee_index_lookup_one (fix->index, "Hello world")); dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); g_object_unref (results); } static void test_two (Fixture *fix, gconstpointer data) { DeeModelIter *orig_iter, *iter; DeeResultSet *results; orig_iter = dee_model_append (fix->model, "Hello world", 27); dee_model_append (fix->model, "Dee world", 68); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 2); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Dee world"), ==, 1); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 2); results = dee_index_lookup (fix->index, "Hello world", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows(results), ==, 1); iter = dee_result_set_next (results); g_assert (iter == orig_iter); g_object_unref (results); results = dee_index_lookup (fix->index, "Dee world", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows(results), ==, 1); g_object_unref (results); g_assert (NULL == dee_index_lookup_one (fix->index, "foobar")); g_assert (NULL == dee_index_lookup_one (fix->index, "Hello")); g_assert (NULL == dee_index_lookup_one (fix->index, "Dee")); dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Dee world"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); } /* Test that we can create an index on a model that already has some rows in it */ static void test_index_on_existing_rows (Fixture *fix, gconstpointer data) { DeeAnalyzer *analyzer; DeeModelReader reader; DeeIndex *extra_idx; DeeResultSet *results; dee_model_append (fix->model, "Hello world", 27); dee_model_append (fix->model, "Hello Dee", 68); analyzer = DEE_ANALYZER (dee_text_analyzer_new ()); dee_model_reader_new_for_string_column (0, &reader); /* Test DeeHashIndex */ extra_idx = DEE_INDEX (dee_hash_index_new(fix->model, analyzer, &reader)); results = dee_index_lookup (extra_idx, "hello", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows(results), ==, 2); g_object_unref (results); g_object_unref (extra_idx); /* Test DeeTreeIndex */ extra_idx = DEE_INDEX (dee_tree_index_new(fix->model, analyzer, &reader)); results = dee_index_lookup (extra_idx, "hello", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows(results), ==, 2); g_object_unref (results); g_object_unref (extra_idx); g_object_unref (analyzer); } static void test_change (Fixture *fix, gconstpointer data) { DeeModelIter *orig_iter, *iter; DeeResultSet *results; dee_model_append (fix->model, "Hello world", 27); dee_model_append (fix->model, "Hello world", 27); dee_model_append (fix->model, "Hello world", 27); orig_iter = dee_model_append (fix->model, "xyz", 27); dee_model_append (fix->model, "Hello world", 27); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 5); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 4); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 1); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 2); results = dee_index_lookup (fix->index, "xyz", DEE_TERM_MATCH_EXACT); g_assert_cmpint (dee_result_set_get_n_rows(results), ==, 1); iter = dee_result_set_next(results); g_assert (iter == orig_iter); /* Change the model and assert that the changes propagate to the index */ dee_model_set (fix->model, orig_iter, "Hello Yoda", 27); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 5); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 4); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello Yoda"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 2); dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello world"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "Hello Yoda"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); g_object_unref (results); } static void test_text (Fixture *fix, gconstpointer data) { DeeModelIter *iter1, *iter2; /* 1 row, 2 terms */ iter1 = dee_model_append (fix->model, "Hello world", 27); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "hello"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "world"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 2); g_assert (dee_index_lookup_one (fix->index, "hello") == iter1); g_assert (dee_index_lookup_one (fix->index, "world") == iter1); /* Verify that model.clear() works */ dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "hello"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "world"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); g_assert (dee_index_lookup_one (fix->index, "hello") == NULL); g_assert (dee_index_lookup_one (fix->index, "world") == NULL); /* 2 rows, 3 terms */ iter1 = dee_model_append (fix->model, "Hello world", 27); iter2 = dee_model_append (fix->model, "Hello... dee?", 68); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 2); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "hello"), ==, 2); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "world"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "dee"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 3); g_assert (dee_index_lookup_one (fix->index, "world") == iter1); g_assert (dee_index_lookup_one (fix->index, "dee") == iter2); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { /* lookup_one() should warn on more than 1 result */ g_message("This should not print!!! %p", dee_index_lookup_one (fix->index, "hello")); exit (0); } g_test_trap_assert_failed(); /* Verify that model.clear() works with 2 rows and 3 terms */ dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "hello"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "world"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "xyz"), ==, 0); g_assert_cmpint (dee_index_get_n_terms(fix->index), ==, 0); g_assert (dee_index_lookup_one (fix->index, "hello") == NULL); g_assert (dee_index_lookup_one (fix->index, "world") == NULL); } static void test_text_with_dupe_terms_per_row (Fixture *fix, gconstpointer data) { dee_model_append (fix->model, "Hello hello there!", 27); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "hello"), ==, 1); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "there"), ==, 1); dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "hello"), ==, 0); g_assert_cmpint (dee_index_get_n_rows_for_term(fix->index, "there"), ==, 0); } static void test_prefix_1_row (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *result_iter; DeeResultSet *results; g_assert (dee_index_get_supported_term_match_flags (fix->index) & DEE_TERM_MATCH_PREFIX); iter = dee_model_append (fix->model, "Hello world", 27); results = dee_index_lookup (fix->index, "hello", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); result_iter = dee_result_set_next (results); g_assert (iter == result_iter); g_object_unref (results); results = dee_index_lookup (fix->index, "hel", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); result_iter = dee_result_set_next (results); g_assert (iter == result_iter); g_object_unref (results); results = dee_index_lookup (fix->index, "h", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); result_iter = dee_result_set_next (results); g_assert (iter == result_iter); g_object_unref (results); results = dee_index_lookup (fix->index, "hellop", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 0); g_object_unref (results); } static void test_prefix_1_row_multiterm (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *result_iter; DeeResultSet *results; g_assert (dee_index_get_supported_term_match_flags (fix->index) & DEE_TERM_MATCH_PREFIX); iter = dee_model_append (fix->model, "To test this tremendous trouble", 27); results = dee_index_lookup (fix->index, "to", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); result_iter = dee_result_set_next (results); g_assert (iter == result_iter); g_object_unref (results); results = dee_index_lookup (fix->index, "t", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); result_iter = dee_result_set_next (results); g_assert (iter == result_iter); g_object_unref (results); } static void test_prefix_2_rows (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *i0, *i1; DeeResultSet *results; g_assert (dee_index_get_supported_term_match_flags (fix->index) & DEE_TERM_MATCH_PREFIX); i0 = dee_model_append (fix->model, "Caravan scrap yard", 0); i1 = dee_model_append (fix->model, "Scraper foo", 1); /* NOTE: We can NOT infer anything about the ordering of the matching * iters inside a single term */ results = dee_index_lookup (fix->index, "scrap", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 2); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); g_object_unref (results); results = dee_index_lookup (fix->index, "scra", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 2); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); g_object_unref (results); results = dee_index_lookup (fix->index, "scraper", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 1); iter = dee_result_set_next (results); g_assert (iter == i1); g_object_unref (results); results = dee_index_lookup (fix->index, "scraperer", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 0); g_object_unref (results); } static void test_prefix_3_rows (Fixture *fix, gconstpointer data) { DeeModelIter *iter, *i0, *i1; DeeResultSet *results; g_assert (dee_index_get_supported_term_match_flags (fix->index) & DEE_TERM_MATCH_PREFIX); i0 = dee_model_append (fix->model, "Hello world", 0); i1 = dee_model_append (fix->model, "Hello dee", 1); dee_model_append (fix->model, "Whopping heroes", 2); /* NOTE: We can NOT infer anything about the ordering of the matching * iters inside a single term */ results = dee_index_lookup (fix->index, "hello", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 2); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); g_object_unref (results); results = dee_index_lookup (fix->index, "hel", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 2); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); iter = dee_result_set_next (results); g_assert (iter == i0 || iter == i1); g_object_unref (results); results = dee_index_lookup (fix->index, "h", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 3); g_object_unref (results); /* NOTE: We CAN infer the ordering below because the test model is crafted * so that we have 2 hits each from distinct terms */ results = dee_index_lookup (fix->index, "w", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 2); iter = dee_result_set_next (results); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 0), ==, "Whopping heroes"); iter = dee_result_set_next (results); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 0), ==, "Hello world"); g_object_unref (results); results = dee_index_lookup (fix->index, "hellop", DEE_TERM_MATCH_PREFIX); g_assert_cmpint (dee_result_set_get_n_rows (results), ==, 0); g_object_unref (results); } static void test_prefix_4_rows_and_clear (Fixture *fix, gconstpointer data) { DeeModelIter *i0; DeeResultSet *rs; dee_model_append (fix->model, "file:///local_uri", 44); dee_model_append (fix->model, "http://foo.com", 23); dee_model_append (fix->model, "ftp://bar.org", 11); i0 = dee_model_append (fix->model, "file:///home/username/file@home", 07); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 4); rs = dee_index_lookup (fix->index, "user", DEE_TERM_MATCH_PREFIX); g_assert (i0 == dee_result_set_next (rs)); g_assert (!dee_result_set_has_next (rs)); g_object_unref (rs); dee_model_clear (fix->model); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); } static void test_prefix_search_empty (Fixture *fix, gconstpointer data) { DeeResultSet *rs; g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 0); rs = dee_index_lookup (fix->index, "a", DEE_TERM_MATCH_PREFIX); g_assert_cmpuint (0, ==, dee_result_set_get_n_rows (rs)); g_object_unref (rs); } static void test_prefix_search_near_beginning (Fixture *fix, gconstpointer data) { DeeResultSet *rs; dee_model_append (fix->model, "vl", 44); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 1); rs = dee_index_lookup (fix->index, "a", DEE_TERM_MATCH_PREFIX); g_assert_cmpuint (0, ==, dee_result_set_get_n_rows (rs)); g_object_unref (rs); } static void test_prefix_search_near_end (Fixture *fix, gconstpointer data) { DeeResultSet *rs; dee_model_append (fix->model, "a", 44); g_assert_cmpint (dee_index_get_n_rows (fix->index), ==, 1); rs = dee_index_lookup (fix->index, "vl", DEE_TERM_MATCH_PREFIX); g_assert_cmpuint (0, ==, dee_result_set_get_n_rows (rs)); g_object_unref (rs); } void test_hash_index_create_suite (void) { g_test_add ("/Index/Hash/Empty", Fixture, 0, setup_hash, test_empty, teardown); g_test_add ("/Index/Tree/Empty", Fixture, 0, setup_tree, test_empty, teardown); g_test_add ("/Index/Hash/One", Fixture, 0, setup_hash, test_one, teardown); g_test_add ("/Index/Tree/One", Fixture, 0, setup_tree, test_one, teardown); g_test_add ("/Index/Hash/Two", Fixture, 0, setup_hash, test_two, teardown); g_test_add ("/Index/Tree/Two", Fixture, 0, setup_tree, test_two, teardown); g_test_add ("/Index/WithExistingRows", Fixture, 0, setup_tree, test_index_on_existing_rows, teardown); g_test_add ("/Index/Hash/Change", Fixture, 0, setup_hash, test_change, teardown); g_test_add ("/Index/Tree/Change", Fixture, 0, setup_tree, test_change, teardown); g_test_add ("/Index/Hash/Text", Fixture, 0, setup_text_hash, test_text, teardown); g_test_add ("/Index/Tree/Text", Fixture, 0, setup_text_tree, test_text, teardown); g_test_add ("/Index/Hash/TextWithDupeTermsPerRow", Fixture, 0, setup_text_hash, test_text_with_dupe_terms_per_row, teardown); g_test_add ("/Index/Tree/TextWithDupeTermsPerRow", Fixture, 0, setup_text_tree, test_text_with_dupe_terms_per_row, teardown); g_test_add ("/Index/Tree/Prefix1Row", Fixture, 0, setup_text_tree, test_prefix_1_row, teardown); g_test_add ("/Index/Tree/Prefix1RowMultiterm", Fixture, 0, setup_text_tree, test_prefix_1_row_multiterm, teardown); g_test_add ("/Index/Tree/Prefix2Rows", Fixture, 0, setup_text_tree, test_prefix_2_rows, teardown); g_test_add ("/Index/Tree/Prefix3Rows", Fixture, 0, setup_text_tree, test_prefix_3_rows, teardown); g_test_add ("/Index/Tree/Prefix4RowsAndClear", Fixture, 0, setup_text_tree, test_prefix_4_rows_and_clear, teardown); g_test_add ("/Index/Tree/PrefixSearchEmpty", Fixture, 0, setup_text_tree, test_prefix_search_empty, teardown); g_test_add ("/Index/Tree/PrefixSearchNearBeginning", Fixture, 0, setup_text_tree, test_prefix_search_near_beginning, teardown); g_test_add ("/Index/Tree/PrefixSearchNearEnd", Fixture, 0, setup_text_tree, test_prefix_search_near_end, teardown); } dee-1.2.7+15.04.20150304/tests/test-model-readers.c0000644000015300001610000000630312475676210021627 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include typedef struct { DeeModel *model; } Fixture; static void setup (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup (Fixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "s", "i", "s", "u", NULL); dee_model_append (fix->model, "Hello world", 27, "Three Danish characters... æøå?", 68); } static void teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->model); fix->model = NULL; } static void test_string0 (Fixture *fix, gconstpointer data) { DeeModelReader reader; gchar *val; dee_model_reader_new_for_string_column (0, &reader); val = dee_model_reader_read (&reader, fix->model, dee_model_get_first_iter (fix->model)); g_assert_cmpstr ("Hello world", ==, val); g_free (val); } static void test_string2 (Fixture *fix, gconstpointer data) { DeeModelReader reader; gchar *val; dee_model_reader_new_for_string_column (2, &reader); val = dee_model_reader_read (&reader, fix->model, dee_model_get_first_iter (fix->model)); g_assert_cmpstr ("Three Danish characters... æøå?", ==, val); g_free (val); } static void test_int (Fixture *fix, gconstpointer data) { DeeModelReader reader; gchar *val; dee_model_reader_new_for_int32_column (1, &reader); val = dee_model_reader_read (&reader, fix->model, dee_model_get_first_iter (fix->model)); g_assert_cmpstr ("27", ==, val); g_free (val); } static void test_uint (Fixture *fix, gconstpointer data) { DeeModelReader reader; gchar *val; dee_model_reader_new_for_uint32_column (3, &reader); val = dee_model_reader_read (&reader, fix->model, dee_model_get_first_iter (fix->model)); g_assert_cmpstr ("68", ==, val); g_free (val); } void test_model_readers_create_suite (void) { #define DOMAIN "/Index/ModelReaders" g_test_add (DOMAIN"/String0", Fixture, 0, setup, test_string0, teardown); g_test_add (DOMAIN"/String2", Fixture, 0, setup, test_string2, teardown); g_test_add (DOMAIN"/Int", Fixture, 0, setup, test_int, teardown); g_test_add (DOMAIN"/UInt", Fixture, 0, setup, test_uint, teardown); } dee-1.2.7+15.04.20150304/tests/model-helper-clear3rows.c0000644000015300001610000000543312475676210022571 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include guint64 before_begin_seqnum, before_end_seqnum, after_begin_seqnum, after_end_seqnum; static void _begin_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { before_begin_seqnum = begin_seqnum; before_end_seqnum = end_seqnum; } static void _end_txn (DeeSharedModel *model, guint64 begin_seqnum, guint64 end_seqnum) { after_begin_seqnum = begin_seqnum; after_end_seqnum = end_seqnum; } static void _row_removed (DeeModel *model, DeeModelIter *iter, GSList **removed) { /* Yes, I _know_ that append() is slow, but this is a test! */ *removed = g_slist_append (*removed, iter); } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeeModel *model; GSList *removed; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); g_signal_connect (model, "begin-transaction", G_CALLBACK (_begin_txn), NULL); g_signal_connect (model, "end-transaction", G_CALLBACK (_end_txn), NULL); if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); /* Listen for removes */ removed = NULL; g_signal_connect (model, "row-removed", G_CALLBACK (_row_removed), &removed); /* Wait for some RowsChanged signals */ gtx_yield_main_loop (1000); g_assert_cmpint (g_slist_length (removed), ==, 3); g_assert_cmpint (dee_model_get_n_rows(model), ==, 0); g_assert_cmpint (6, ==, (guint) dee_serializable_model_get_seqnum (model)); g_assert (before_begin_seqnum == after_begin_seqnum); g_assert (before_end_seqnum == after_end_seqnum); g_assert_cmpint (3, ==, (guint) before_begin_seqnum); g_assert_cmpint (6, ==, (guint) before_end_seqnum); gtx_assert_last_unref (model); g_slist_free (removed); return 0; } dee-1.2.7+15.04.20150304/tests/model-helper-change3rows.c0000644000015300001610000000545212475676210022731 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include static void _row_changed (DeeModel *model, DeeModelIter *iter, GSList **changes_so_far) { /* Yes, I _know_ that append() is slow, but this is a test! */ *changes_so_far = g_slist_append (*changes_so_far, iter); } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; GSList *changes; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); /* Listen for changes */ changes = NULL; g_signal_connect (model, "row-changed", G_CALLBACK (_row_changed), &changes); /* Wait for some RowsChanged signals */ gtx_yield_main_loop (1000); /* Check that we got what we expected */ g_assert_cmpint (g_slist_length (changes), == , 3); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); iter = (DeeModelIter*) g_slist_nth (changes, 0)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "changed_zero"); iter = (DeeModelIter*) g_slist_nth (changes, 1)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "changed_one"); iter = (DeeModelIter*) g_slist_nth (changes, 2)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 2); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "changed_two"); gtx_assert_last_unref (model); g_slist_free (changes); return 0; } dee-1.2.7+15.04.20150304/tests/test-client-server.c0000644000015300001610000007246712475676210021704 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by Michal Hruby * */ #include #include #include #include #define TIMEOUT 100 #define PEER_NAME "com.canonical.Dee.Peer.Tests.Interactions" #define MODEL_NAME "com.canonical.Dee.Peer.Tests.Interactions" /* A command line that launches the appropriate *-helper-* executable, * giving $name as first argument */ #define SERVER_HELPER(helper,name,count) \ (gchar *[]) { "./server-helper-"#helper, name, #count, NULL } #define MODEL_HELPER(helper,name) \ (gchar *[]) { "./model-helper-"#helper, name, "DeeClient", NULL } typedef struct { DeeServer *server; } ServerFixture; typedef struct { DeeModel *model; } Fixture; static void server_setup (ServerFixture *fix, gconstpointer data); static void server_teardown (ServerFixture *fix, gconstpointer data); static void model_setup (Fixture *fix, gconstpointer data); static void model_teardown (Fixture *fix, gconstpointer data); static void model_setup_null (Fixture *fix, gconstpointer data); static void model_teardown_null (Fixture *fix, gconstpointer data); static void test_allocation (ServerFixture *fix, gconstpointer data); static void test_become_leader (ServerFixture *fix, gconstpointer data); static void test_valid_client_address (ServerFixture *fix, gconstpointer data); static void test_one_client (ServerFixture *fix, gconstpointer data); static void test_multiple_clients (ServerFixture *fix, gconstpointer data); static void test_shared_server (ServerFixture *fix, gconstpointer data); static void test_ready (Fixture *fix, gconstpointer data); static void test_clone (Fixture *fix, gconstpointer data); static void test_row_added (Fixture *fix, gconstpointer data); static void test_row_changed (Fixture *fix, gconstpointer data); static void test_row_removed (Fixture *fix, gconstpointer data); static void test_model_clear (Fixture *fix, gconstpointer data); static void test_clear_add (Fixture *fix, gconstpointer data); static void test_clear_then_add (Fixture *fix, gconstpointer data); static void test_row_inserted (Fixture *fix, gconstpointer data); static void test_schemaless_leader (Fixture *fix, gconstpointer data); static void test_client_commit (Fixture *fix, gconstpointer data); static void test_multiple_models (Fixture *fix, gconstpointer data); static void test_multiple_models2 (Fixture *fix, gconstpointer data); static void test_remote_append (Fixture *fix, gconstpointer data); static void test_disabled_writes (Fixture *fix, gconstpointer data); void test_client_server_interactions_create_suite (void) { #define DOMAIN "/ClientServer/Interactions" g_test_add (DOMAIN"/Allocation", ServerFixture, 0, server_setup, test_allocation, server_teardown); g_test_add (DOMAIN"/BecomeLeader", ServerFixture, 0, server_setup, test_become_leader, server_teardown); g_test_add (DOMAIN"/ValidClientAddress", ServerFixture, 0, server_setup, test_valid_client_address, server_teardown); g_test_add (DOMAIN"/OneClient", ServerFixture, 0, server_setup, test_one_client, server_teardown); g_test_add (DOMAIN"/MultipleClients", ServerFixture, 0, server_setup, test_multiple_clients, server_teardown); g_test_add (DOMAIN"/SharedServer", ServerFixture, 0, server_setup, test_shared_server, server_teardown); #undef DOMAIN #define DOMAIN "/ClientServer/Model/Interactions" g_test_add (DOMAIN"/Ready", Fixture, 0, model_setup, test_ready, model_teardown); g_test_add (DOMAIN"/Clone", Fixture, 0, model_setup, test_clone, model_teardown); g_test_add (DOMAIN"/RowAdded", Fixture, 0, model_setup, test_row_added, model_teardown); g_test_add (DOMAIN"/RowChanged", Fixture, 0, model_setup, test_row_changed, model_teardown); g_test_add (DOMAIN"/RowRemoved", Fixture, 0, model_setup, test_row_removed, model_teardown); g_test_add (DOMAIN"/Clear", Fixture, 0, model_setup, test_model_clear, model_teardown); g_test_add (DOMAIN"/ClearAndAdd", Fixture, 0, model_setup, test_clear_add, model_teardown); g_test_add (DOMAIN"/ClearThenAdd", Fixture, 0, model_setup, test_clear_then_add, model_teardown); g_test_add (DOMAIN"/RowInserted", Fixture, 0, model_setup, test_row_inserted, model_teardown); g_test_add (DOMAIN"/SchemalessLeader", Fixture, 0, model_setup_null, test_schemaless_leader, model_teardown_null); g_test_add (DOMAIN"/ClientCommit", Fixture, 0, model_setup, test_client_commit, model_teardown); g_test_add (DOMAIN"/MultipleModels", Fixture, 0, model_setup_null, test_multiple_models, model_teardown_null); g_test_add (DOMAIN"/MultipleModels2", Fixture, 0, model_setup_null, test_multiple_models2, model_teardown_null); g_test_add (DOMAIN"/RemoteAppend", Fixture, 0, model_setup, test_remote_append, model_teardown); g_test_add (DOMAIN"/DisabledWrites", Fixture, 0, model_setup_null, test_disabled_writes, model_teardown_null); } static void server_setup (ServerFixture *fix, gconstpointer data) { fix->server = dee_server_new (PEER_NAME); g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (DEE_PEER (fix->server))); g_assert (DEE_IS_SERVER (fix->server)); } static void server_teardown (ServerFixture *fix, gconstpointer data) { gtx_assert_last_unref (fix->server); /* Spin the mainloop a bit to check if we have any post-test * async effect crashing us */ gtx_yield_main_loop (200); } static void model_setup (Fixture *fix, gconstpointer data) { DeeServer *server = dee_server_new (MODEL_NAME); fix->model = (DeeModel*) dee_shared_model_new_for_peer (DEE_PEER (server)); dee_model_set_schema (fix->model, "i", "s", NULL); g_assert (DEE_IS_MODEL (fix->model)); } static void model_teardown (Fixture *fix, gconstpointer data) { gtx_assert_last_unref (fix->model); /* Spin the mainloop so the socket service gets into usable state again */ gtx_yield_main_loop (200); } static void model_setup_null (Fixture *fix, gconstpointer data) { } static void model_teardown_null (Fixture *fix, gconstpointer data) { } /******************** The actual test cases ********************/ static void test_allocation (ServerFixture *fix, gconstpointer data) { /* Do nothing, this test basically just asserts that * the fix->server is cleaned up after immediate construction */ } static void test_become_leader (ServerFixture *fix, gconstpointer data) { gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, "notify::swarm-leader", NULL); /* Assert that we have become swarm leaders. * No other peers should be running */ g_assert_cmpint (0, !=, dee_peer_is_swarm_leader (DEE_PEER (fix->server))); } static void test_valid_client_address (ServerFixture *fix, gconstpointer data) { gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, "notify::swarm-leader", NULL); g_assert (dee_server_get_client_address (fix->server) != NULL); } static void on_connection_acquired (DeeServer *server, GDBusConnection *connection, GSList **list) { /* Yes, I _know_ that append() is slow, but this is a test! */ *list = g_slist_append (*list, connection); } static void test_one_client (ServerFixture *fix, gconstpointer data) { GSList *list = NULL; g_signal_connect (fix->server, "connection-acquired", G_CALLBACK (on_connection_acquired), &list); gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, "notify::swarm-leader", NULL); /* We are leaders - launch the helper */ if (gtx_wait_for_command (TESTDIR, SERVER_HELPER (client, PEER_NAME, 1), 2000)) g_critical ("Peer helper timed out"); gtx_assert_last_command_status (0); g_assert_cmpuint (g_slist_length (list), ==, 1); g_slist_free (list); } static void test_multiple_clients (ServerFixture *fix, gconstpointer data) { GSList *list = NULL; g_signal_connect (fix->server, "connection-acquired", G_CALLBACK (on_connection_acquired), &list); gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, "notify::swarm-leader", NULL); /* We are leaders - launch the helper */ if (gtx_wait_for_command (TESTDIR, SERVER_HELPER (client, PEER_NAME, 4), 4000)) g_critical ("Peer helper timed out"); gtx_assert_last_command_status (0); g_assert_cmpuint (g_slist_length (list), ==, 4); g_slist_free (list); } static void test_shared_server (ServerFixture *fix, gconstpointer data) { DeeServer *another_server; gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, "notify::swarm-leader", NULL); /* Make sure we're able to use the same bus_address on multiple servers */ another_server = dee_server_new_for_address ( PEER_NAME ".T1", dee_server_get_client_address (fix->server)); gtx_wait_for_signal (G_OBJECT (another_server), TIMEOUT, "notify::swarm-leader", NULL); g_assert_cmpstr (dee_peer_get_swarm_leader (DEE_PEER (fix->server)), ==, dee_peer_get_swarm_leader (DEE_PEER (another_server))); g_assert_cmpstr (dee_server_get_client_address (fix->server), ==, dee_server_get_client_address (another_server)); g_object_unref (another_server); } static void test_ready (Fixture *fix, gconstpointer data) { GParamSpec *pspec; gboolean synchronized; /* Test the GObject property reports FALSE */ g_object_get (fix->model, "synchronized", &synchronized, NULL); g_assert_cmpint (0, == , synchronized); /* Check that convenience getter now reports FALSE */ g_assert_cmpint (0, ==, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model))); if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec)) g_critical ("Model never synchronized"); else g_param_spec_unref (pspec); /* Test the GObject property reports TRUE */ g_object_get (fix->model, "synchronized", &synchronized, NULL); g_assert_cmpint (0, != , synchronized); /* Check that convenience getter now reports TRUE */ g_assert_cmpint (0, !=, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model))); if (!gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec)) { g_critical ("Model changed synchronization state twice"); g_param_spec_unref (pspec); } } static gboolean _add3rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_append (model, 0, "zero"); dee_model_append (model, 1, "one"); dee_model_append (model, 2, "two"); return FALSE; } static gboolean _add5rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_append (model, 0, "zero"); dee_model_append (model, 1, "one"); dee_model_append (model, 2, "two"); dee_model_append (model, 3, "three"); dee_model_append (model, 4, "four"); return FALSE; } static gboolean _change3rows (DeeModel *model) { DeeModelIter *iter; g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); iter = dee_model_get_iter_at_row (model, 0); dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_zero")); iter = dee_model_get_iter_at_row (model, 1); dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_one")); iter = dee_model_get_iter_at_row (model, 2); dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_two")); return FALSE; } /* Assumes a model with 5 rows. Removes rows 0, 4, and 2 * in that order. Accounting for rows shifts this becomes * 0, 3, and 1. Leaving the original rows 1 and 3 now in * positions 0 and 1 */ static gboolean _remove3rows (DeeModel *model) { DeeModelIter *iter0, *iter4, *iter2; DeeModelIter *orig_iter1, *orig_iter3; g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); g_return_val_if_fail (dee_model_get_n_rows (model) == 5, FALSE); iter0 = dee_model_get_iter_at_row (model, 0); iter4 = dee_model_get_iter_at_row (model, 4); iter2 = dee_model_get_iter_at_row (model, 2); orig_iter1 = dee_model_get_iter_at_row (model, 1); orig_iter3 = dee_model_get_iter_at_row (model, 3); dee_model_remove (model, iter0); dee_model_remove (model, iter4); dee_model_remove (model, iter2); g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); g_assert (dee_model_get_iter_at_row (model, 0) == orig_iter1); g_assert (dee_model_get_iter_at_row (model, 1) == orig_iter3); return FALSE; } static gboolean _insert1row (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_insert (model, 1, 27, "twentyseven"); return FALSE; } static gboolean _clear_model (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); dee_model_clear (model); return FALSE; } static gboolean _clear_and_add5rows (DeeModel *model) { g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); _clear_model (model); _add5rows (model); return FALSE; } static void test_clone (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never synchronized"); _add3rows (fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clone3rows, MODEL_NAME), 1000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); /* We test that we can do this two times */ /*if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (3rows, MODEL_NAME), 1000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0);*/ } static void test_row_added (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_changed (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); g_timeout_add (500, (GSourceFunc)_change3rows, fix->model); //const gchar *cmd[] = {"dbus-monitor", NULL}; //const gchar *cmd[] = {"sleep", "1",NULL}; if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (change3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_removed (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add5rows (fix->model); g_timeout_add (500, (GSourceFunc)_remove3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (remove3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_model_clear (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); g_timeout_add (500, (GSourceFunc)_clear_model, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_clear_add (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); g_timeout_add (500, (GSourceFunc)_clear_and_add5rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3add5, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_clear_then_add (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _clear_model (fix->model); g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (add3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); g_timeout_add (500, (GSourceFunc)_clear_model, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (clear3rows, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void test_row_inserted (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); _add3rows (fix->model); g_timeout_add (500, (GSourceFunc)_insert1row, fix->model); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (insert1row, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); } static void _ready (DeeModel *model, GParamSpec *pspec, GSList **rows_so_far) { /* We must not have any rows when 'ready' is emitted */ g_assert (*rows_so_far == NULL); } static void _collect_row (DeeModel *model, DeeModelIter *iter, GSList **rows_so_far) { /* Yes, I _know_ that append() is slow, but this is a test! */ *rows_so_far = g_slist_append (*rows_so_far, iter); } /* This case must run without a Fixture */ static void test_schemaless_leader (Fixture *fix, gconstpointer data) { DeeModel *model; DeeModelIter *iter; GSList *rows_added; g_assert (fix->model == NULL); /* Set up a clean model *without* a schema. We will pick up the schema * with the first transaction from the slave. Or at least, that's what we * want to assert ;-) */ model = dee_shared_model_new_for_peer (DEE_PEER (dee_server_new (MODEL_NAME))); // no set_schema() on purpose! /* Listen for changes */ rows_added = NULL; g_signal_connect (model, "row-added", G_CALLBACK (_collect_row), &rows_added); g_signal_connect (model, "notify::synchronized", G_CALLBACK (_ready), &rows_added); /* Remote process defines column types and adds two rows */ if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (schemaless, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); /* Check that we got the right schema from the peer */ g_assert_cmpint (dee_model_get_n_columns (model), ==, 2); g_assert_cmpstr (dee_model_get_column_schema (model, 0), ==, "i"); g_assert_cmpstr (dee_model_get_column_schema (model, 1), ==, "s"); /* Check that we got what we expected */ g_assert_cmpint (g_slist_length (rows_added), == , 2); g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); g_assert_cmpint (dee_model_get_n_columns (model), ==, 2); iter = (DeeModelIter*) g_slist_nth (rows_added, 0)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 27); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "skunkworks"); iter = (DeeModelIter*) g_slist_nth (rows_added, 1)->data; g_assert_cmpint (dee_model_get_position (model, iter), == , 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 68); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "wumbo"); gtx_assert_last_unref (model); g_slist_free (rows_added); /* Spin the mainloop so the socket service gets into usable state again */ gtx_yield_main_loop (200); } static void test_client_commit (Fixture *fix, gconstpointer data) { gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL); DeeModel *client_model1 = dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new (MODEL_NAME))); DeeModel *client_model2 = dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new (MODEL_NAME))); gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT, "notify::synchronized", NULL); g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model1))); if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2))) { gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT, "notify::synchronized", NULL); } dee_model_append (client_model1, 38, "client_change"); gtx_yield_main_loop (500); g_assert_cmpuint (dee_model_get_n_rows (fix->model), >, 0); g_assert_cmpuint (dee_model_get_n_rows (client_model1), >, 0); g_assert_cmpuint (dee_model_get_n_rows (client_model2), >, 0); g_object_unref (client_model1); g_object_unref (client_model2); } static void test_multiple_models (Fixture *fix, gconstpointer data) { gchar *address = dee_server_bus_address_for_name (PEER_NAME, TRUE); DeeModel *model1 = dee_shared_model_new_for_peer ( DEE_PEER (dee_server_new_for_address (PEER_NAME ".T1", address)) ); DeeModel *model2 = dee_shared_model_new_for_peer ( DEE_PEER (dee_server_new_for_address (PEER_NAME ".T2", address)) ); dee_model_set_schema (model1, "i", "s", NULL); dee_model_set_schema (model2, "i", "s", NULL); gtx_wait_for_signal (G_OBJECT (model1), TIMEOUT, "notify::synchronized", NULL); if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2))) { gtx_wait_for_signal (G_OBJECT (model2), TIMEOUT, "notify::synchronized", NULL); } g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model1)) && dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2))); DeeModel *client_model1 = dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new_for_address (PEER_NAME ".T1", address))); DeeModel *client_model2 = dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new_for_address (PEER_NAME ".T2", address))); _add5rows (model1); _add3rows (model2); gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT, "notify::synchronized", NULL); g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5); g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5); if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2))) { gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT, "notify::synchronized", NULL); } g_assert_cmpuint (dee_model_get_n_rows (client_model2), ==, 3); g_assert_cmpuint (dee_model_get_n_rows (model2), ==, 3); /* Make sure the first model still has 5 rows */ g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5); g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5); g_object_unref (client_model1); g_object_unref (client_model2); g_object_unref (model1); g_object_unref (model2); g_free (address); /* Spin the mainloop so the socket service gets into usable state again */ gtx_yield_main_loop (200); } static void test_multiple_models2 (Fixture *fix, gconstpointer data) { gchar *address = dee_server_bus_address_for_name (PEER_NAME, TRUE); DeeModel *model1 = dee_shared_model_new_for_peer ( DEE_PEER (dee_server_new_for_address (PEER_NAME ".T1", address)) ); DeeModel *model2 = dee_shared_model_new_for_peer ( DEE_PEER (dee_server_new_for_address (PEER_NAME ".T2", address)) ); dee_model_set_schema (model1, "i", "s", NULL); dee_model_set_schema (model2, "i", "s", NULL); gtx_wait_for_signal (G_OBJECT (model1), TIMEOUT, "notify::synchronized", NULL); if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2))) { gtx_wait_for_signal (G_OBJECT (model2), TIMEOUT, "notify::synchronized", NULL); } g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model1)) && dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2))); DeeModel *client_model1 = dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new_for_address (PEER_NAME ".T1", address))); DeeModel *client_model2 = dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new_for_address (PEER_NAME ".T2", address))); _add5rows (model1); _add3rows (model2); gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT, "notify::synchronized", NULL); g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5); g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5); if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2))) { gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT, "notify::synchronized", NULL); } g_assert_cmpuint (dee_model_get_n_rows (client_model2), ==, 3); g_assert_cmpuint (dee_model_get_n_rows (model2), ==, 3); /* Make sure the first model still has 5 rows */ g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5); g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5); /* get rid of the first client and server */ g_object_unref (client_model1); g_object_unref (model1); /* and make sure the second ones still work fine */ dee_model_clear (model2); gtx_yield_main_loop (500); /* sync */ g_assert_cmpuint (dee_model_get_n_rows (client_model2), ==, 0); g_assert_cmpuint (dee_model_get_n_rows (model2), ==, 0); g_object_unref (client_model2); g_object_unref (model2); g_free (address); /* Spin the mainloop so the socket service gets into usable state again */ gtx_yield_main_loop (200); } static void changeset_signal (DeeModel *model, gboolean *value) { *value = TRUE; } static void test_remote_append (Fixture *fix, gconstpointer data) { if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); /* We should be leader before starting the helper process */ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model))); g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 0); gboolean got_changeset_start = FALSE; gboolean got_changeset_finish = FALSE; g_signal_connect (fix->model, "changeset-started", G_CALLBACK (changeset_signal), &got_changeset_start); g_signal_connect (fix->model, "changeset-finished", G_CALLBACK (changeset_signal), &got_changeset_finish); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (append1, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); /* There should be a new row in the model */ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1); g_assert (got_changeset_start); g_assert (got_changeset_finish); } static void test_disabled_writes (Fixture *fix, gconstpointer data) { DeePeer *peer; DeeModelIter *iter; peer = DEE_PEER (dee_server_new (MODEL_NAME)); fix->model = DEE_MODEL (g_object_new (DEE_TYPE_SHARED_MODEL, "peer", peer, "back-end", dee_sequence_model_new (), "access-mode", DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE, NULL)); dee_model_set_schema (fix->model, "i", "s", NULL); g_object_unref (G_OBJECT (peer)); if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) g_critical ("Model never emitted 'ready' signal"); /* We should be leader before starting the helper process */ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model))); dee_model_prepend (fix->model, 81, "eightyone"); if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (append1, MODEL_NAME), 2000)) g_critical ("Model helper timed out"); gtx_assert_last_command_status (0); /* The peer tried to append a row, but we should have ignored that */ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1); iter = dee_model_get_first_iter (fix->model); g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "eightyone"); } dee-1.2.7+15.04.20150304/tests/model-helper-clone3rows-meta.c0000644000015300001610000000613712475676210023531 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Michal Hruby * */ #include "config.h" #include #include #include #include static void row_added (DeeModel *model, DeeModelIter *iter, gpointer data) { gint *num_added = (gint*) data; (*num_added)++; } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeeModel *model; DeeModelIter *iter; const gchar **column_names; GVariant *column_namesv; GHashTable *vardict_schema; gint num_added; guint column; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif g_set_prgname ("model-helper"); if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); num_added = 0; g_signal_connect (model, "row-added", G_CALLBACK (row_added), &num_added); if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) g_error ("Helper model timed out waiting for 'ready' signal"); g_assert_cmpint (dee_model_get_n_rows (model), ==, 3); iter = dee_model_get_iter_at_row (model, 0); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 0); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "zero"); iter = dee_model_get_iter_at_row (model, 1); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 1); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "one"); iter = dee_model_get_iter_at_row (model, 2); g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 2); g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "two"); column_names = dee_model_get_column_names (model, NULL); g_assert (column_names != NULL); column_namesv = g_variant_new_strv (column_names, -1); g_assert_cmpstr (g_variant_print (column_namesv, FALSE), ==, "['count', 'title', 'hints', 'extra-hints']"); g_assert_cmpstr (dee_model_get_field_schema (model, "uri", &column), ==, "s"); g_assert_cmpuint (column, ==, 2); vardict_schema = dee_model_get_vardict_schema (model, 2); g_assert_cmpuint (g_hash_table_size (vardict_schema), >, 0); g_hash_table_unref (vardict_schema); vardict_schema = dee_model_get_vardict_schema (model, 3); g_assert (vardict_schema == NULL || g_hash_table_size (vardict_schema) == 0); if (vardict_schema) g_hash_table_unref (vardict_schema); gtx_assert_last_unref (model); g_assert_cmpint (num_added, ==, 3); return 0; } dee-1.2.7+15.04.20150304/tests/test-term-list.c0000644000015300001610000001146712475676210021033 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include #include #include typedef struct { DeeTermList *terms; } Fixture; static void setup (Fixture *fix, gconstpointer data); static void teardown (Fixture *fix, gconstpointer data); static void setup (Fixture *fix, gconstpointer data) { fix->terms = g_object_new (DEE_TYPE_TERM_LIST, NULL); g_assert (DEE_IS_TERM_LIST (fix->terms)); } static void teardown (Fixture *fix, gconstpointer data) { g_object_unref (fix->terms); fix->terms = NULL; } static void test_empty (Fixture *fix, gconstpointer data) { g_assert (DEE_IS_TERM_LIST (fix->terms)); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 0); dee_term_list_clear (fix->terms); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 0); } static void test_one (Fixture *fix, gconstpointer data) { dee_term_list_add_term (fix->terms, "one"); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "one"); dee_term_list_clear (fix->terms); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 0); } static void test_two (Fixture *fix, gconstpointer data) { dee_term_list_add_term (fix->terms, "one"); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "one"); dee_term_list_add_term (fix->terms, "two"); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 2); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 0), ==, "one"); g_assert_cmpstr (dee_term_list_get_term (fix->terms, 1), ==, "two"); dee_term_list_clear (fix->terms); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 0); } static void test_clone (Fixture *fix, gconstpointer data) { DeeTermList *clone; /* Cloning an empty list should work */ clone = dee_term_list_clone (fix->terms); g_assert_cmpint (dee_term_list_num_terms (clone), ==, 0); g_object_unref (clone); /* Clone with 1 item */ dee_term_list_add_term (fix->terms, "one"); clone = dee_term_list_clone (fix->terms); g_assert_cmpint (dee_term_list_num_terms (clone), ==, 1); g_assert_cmpstr (dee_term_list_get_term(clone, 0), ==, "one"); /* Terms from cloned lists should compare by pointers directly */ g_assert (dee_term_list_get_term (fix->terms, 0) == dee_term_list_get_term (clone, 0)); /* Clearing clone should not affect original */ dee_term_list_clear (clone); g_assert_cmpint (dee_term_list_num_terms (clone), ==, 0); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 1); g_object_unref (clone); /* Clone with 3 items */ dee_term_list_add_term (fix->terms, "two"); dee_term_list_add_term (fix->terms, "three"); clone = dee_term_list_clone (fix->terms); g_assert_cmpint (dee_term_list_num_terms (clone), ==, 3); g_assert_cmpstr (dee_term_list_get_term(clone, 0), ==, "one"); g_assert_cmpstr (dee_term_list_get_term(clone, 1), ==, "two"); g_assert_cmpstr (dee_term_list_get_term(clone, 2), ==, "three"); /* Terms from cloned lists should compare by pointers directly */ g_assert (dee_term_list_get_term (fix->terms, 0) == dee_term_list_get_term (clone, 0)); g_assert (dee_term_list_get_term (fix->terms, 1) == dee_term_list_get_term (clone, 1)); g_assert (dee_term_list_get_term (fix->terms, 2) == dee_term_list_get_term (clone, 2)); /* Clearing original should not affect clone*/ dee_term_list_clear (fix->terms); g_assert_cmpint (dee_term_list_num_terms (clone), ==, 3); g_assert_cmpint (dee_term_list_num_terms (fix->terms), ==, 0); g_object_unref (clone); } void test_term_list_create_suite (void) { #define DOMAIN "/Index/TermList" g_test_add (DOMAIN"/Empty", Fixture, 0, setup, test_empty, teardown); g_test_add (DOMAIN"/One", Fixture, 0, setup, test_one, teardown); g_test_add (DOMAIN"/Two", Fixture, 0, setup, test_two, teardown); g_test_add (DOMAIN"/Clone", Fixture, 0, setup, test_clone, teardown); } dee-1.2.7+15.04.20150304/tests/test-model-column.c0000644000015300001610000003405412475676210021503 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * Neil Jagdish Patel * */ #include #include #include #include typedef struct { DeeModel *model; } ColumnFixture; static void column_setup (ColumnFixture *fix, gconstpointer data); static void column_teardown (ColumnFixture *fix, gconstpointer data); static void proxy_column_setup (ColumnFixture *fix, gconstpointer data); static void proxy_column_teardown (ColumnFixture *fix, gconstpointer data); static void test_column_allocation (ColumnFixture *fix, gconstpointer data); static void test_unmodified_and_get_value (ColumnFixture *fix, gconstpointer data); static void test_modification_and_get_row (ColumnFixture *fix, gconstpointer data); static void test_get_schema (ColumnFixture *fix, gconstpointer data); static void test_no_schema (ColumnFixture *fix, gconstpointer data); static void test_bad_schemas (void); static void test_null_string (ColumnFixture *fix, gconstpointer data); void test_model_column_create_suite (void) { #define SEQ_DOMAIN "/Model/Sequence/Column" #define PROXY_DOMAIN "/Model/Proxy/Column" g_test_add (SEQ_DOMAIN"/Allocation", ColumnFixture, 0, column_setup, test_column_allocation, column_teardown); g_test_add (PROXY_DOMAIN"/Allocation", ColumnFixture, 0, proxy_column_setup, test_column_allocation, proxy_column_teardown); g_test_add (SEQ_DOMAIN"/UnmodifiedAndGetValue", ColumnFixture, 0, column_setup, test_unmodified_and_get_value, column_teardown); g_test_add (PROXY_DOMAIN"/UnmodifiedAndGetValue", ColumnFixture, 0, proxy_column_setup, test_unmodified_and_get_value, proxy_column_teardown); g_test_add (SEQ_DOMAIN"/ModificationAndGetRow", ColumnFixture, 0, column_setup, test_modification_and_get_row, column_teardown); g_test_add (PROXY_DOMAIN"/ModificationAndGetRow", ColumnFixture, 0, proxy_column_setup, test_modification_and_get_row, proxy_column_teardown); g_test_add (SEQ_DOMAIN"/Schemas", ColumnFixture, 0, column_setup, test_get_schema, column_teardown); g_test_add (PROXY_DOMAIN"/Schemas", ColumnFixture, 0, proxy_column_setup, test_get_schema, proxy_column_teardown); g_test_add (SEQ_DOMAIN"/NoSchemas", ColumnFixture, 0, column_setup, test_no_schema, column_teardown); g_test_add (PROXY_DOMAIN"/NoSchemas", ColumnFixture, 0, proxy_column_setup, test_no_schema, proxy_column_teardown); g_test_add_func ("/Model/Column/BadSchemas", test_bad_schemas); g_test_add (SEQ_DOMAIN"/NullString", ColumnFixture, 0, column_setup, test_null_string, column_teardown); g_test_add (PROXY_DOMAIN"/NullString", ColumnFixture, 0, proxy_column_setup, test_null_string, proxy_column_teardown); } static void column_setup (ColumnFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "b", "y", "i", "u", "x", "t", "d", "s", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); g_assert_cmpint (8, ==, dee_model_get_n_columns (fix->model)); g_assert_cmpstr ("b", ==, dee_model_get_column_schema (fix->model, 0)); g_assert_cmpstr ("y", ==, dee_model_get_column_schema(fix->model, 1)); g_assert_cmpstr ("i", ==, dee_model_get_column_schema (fix->model, 2)); g_assert_cmpstr ("u", ==, dee_model_get_column_schema (fix->model, 3)); g_assert_cmpstr ("x", ==, dee_model_get_column_schema (fix->model, 4)); g_assert_cmpstr ("t", ==, dee_model_get_column_schema (fix->model, 5)); g_assert_cmpstr ("d", ==, dee_model_get_column_schema (fix->model, 6)); g_assert_cmpstr ("s", ==, dee_model_get_column_schema (fix->model, 7)); g_assert_cmpint (0, ==, dee_model_get_n_rows (fix->model)); } static void column_teardown (ColumnFixture *fix, gconstpointer data) { g_object_unref (fix->model); } static void proxy_column_setup (ColumnFixture *fix, gconstpointer data) { column_setup (fix, data); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", fix->model, NULL); g_assert (DEE_IS_PROXY_MODEL (fix->model)); } static void proxy_column_teardown (ColumnFixture *fix, gconstpointer data) { g_assert (DEE_IS_PROXY_MODEL (fix->model)); g_object_unref (fix->model); } static void test_column_allocation (ColumnFixture *fix, gconstpointer data) { g_assert (DEE_IS_MODEL (fix->model)); } static void test_unmodified_and_get_value (ColumnFixture *fix, gconstpointer data) { DeeModel *model = fix->model; DeeModelIter *iter; GVariant *value; dee_model_append (fix->model, TRUE, '1', G_MININT32, G_MAXUINT32, G_MAXINT64, G_MAXUINT64, G_MAXDOUBLE, "Hello World"); g_assert (dee_model_get_n_rows (model) == 1); iter = dee_model_get_first_iter (model); value = dee_model_get_value (model, iter, 0); g_assert_cmpint (TRUE, ==, g_variant_get_boolean (value)); g_assert_cmpint (TRUE, ==, dee_model_get_bool (model, iter, 0)); g_variant_unref (value); value = dee_model_get_value (model, iter, 1); g_assert_cmpint ('1', ==, g_variant_get_byte (value)); g_assert_cmpint ('1', ==, dee_model_get_uchar (model, iter, 1)); g_variant_unref (value); value = dee_model_get_value (model, iter, 2); g_assert_cmpint (G_MININT32, ==, g_variant_get_int32 (value)); g_assert_cmpint (G_MININT32, ==, dee_model_get_int32 (model, iter, 2)); g_variant_unref (value); value = dee_model_get_value (model, iter, 3); g_assert (g_variant_get_uint32 (value) == G_MAXUINT32); g_assert_cmpuint (G_MAXUINT32, ==, dee_model_get_uint32 (model, iter, 3)); g_variant_unref (value); value = dee_model_get_value (model, iter, 4); g_assert (g_variant_get_int64 (value) == G_MAXINT64); g_assert_cmpint (G_MAXINT64, ==, dee_model_get_int64 (model, iter, 4)); g_variant_unref (value); value = dee_model_get_value (model, iter, 5); g_assert (g_variant_get_uint64 (value) == G_MAXUINT64); g_assert_cmpuint (G_MAXUINT64, ==, dee_model_get_uint64 (model, iter, 5)); g_variant_unref (value); value = dee_model_get_value (model, iter, 6); g_assert (g_variant_get_double (value) == G_MAXDOUBLE); g_assert_cmpfloat (G_MAXDOUBLE, ==, dee_model_get_double (model, iter, 6)); g_variant_unref (value); value = dee_model_get_value (model, iter, 7); g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "Hello World"); g_assert_cmpstr ("Hello World", ==, dee_model_get_string (model, iter, 7)); g_variant_unref (value); /* Assert that we don't mess up the string copying. * Ie that dee_model_get_string() returns a const string */ const gchar *cp1 = dee_model_get_string (model, iter, 7); const gchar *cp2 = dee_model_get_string (model, iter, 7); g_assert_cmpstr ("Hello World", ==, cp1); g_assert_cmpstr ("Hello World", ==, cp2); g_assert (cp1 == cp2); } static void test_modification_and_get_row (ColumnFixture *fix, gconstpointer data) { DeeModel *model = fix->model; DeeModelIter *iter; GVariant **heap, **stack, **_stack; gint i; dee_model_append (fix->model, TRUE, '1', G_MININT, G_MAXUINT, G_MAXINT64, G_MAXUINT64, G_MAXDOUBLE, "Hello World"); g_assert (dee_model_get_n_rows (model) == 1); iter = dee_model_get_first_iter (model); dee_model_set (model, iter, FALSE, '2', G_MININT+5, G_MAXUINT-5, G_MAXINT64-5, G_MAXUINT64-5, G_MAXDOUBLE-5.0, "World Hello"); /* Try filling a stack allocated array and assert * that we get the same pointer back*/ stack = g_alloca (sizeof(gpointer) * dee_model_get_n_columns (model)); _stack = dee_model_get_row (model, iter, stack); g_assert (stack == _stack); /* Also try allocating a new array on the heap by passing in NULL for * the destination */ heap = dee_model_get_row (model, iter, NULL); g_assert (heap != NULL); /* Now assert that both the stack- and heap allocated row * contains the right data */ g_assert (!g_variant_get_boolean (stack[0])); g_assert (!g_variant_get_boolean (heap[0])); g_assert_cmpint (g_variant_get_byte (stack[1]), ==, '2'); g_assert_cmpint (g_variant_get_byte (heap[1]), ==, '2'); g_assert_cmpint (g_variant_get_int32 (stack[2]), ==, G_MININT+5); g_assert_cmpint (g_variant_get_int32 (heap[2]), ==, G_MININT+5); g_assert_cmpint (g_variant_get_uint32 (stack[3]), ==, G_MAXUINT-5); g_assert_cmpint (g_variant_get_uint32 (heap[3]), ==, G_MAXUINT-5); g_assert (g_variant_get_int64 (stack[4]) == G_MAXINT64-5); g_assert (g_variant_get_int64 (heap[4]) == G_MAXINT64-5); g_assert (g_variant_get_uint64 (stack[5]) == G_MAXUINT64-5); g_assert (g_variant_get_uint64 (heap[5]) == G_MAXUINT64-5); g_assert (g_variant_get_double (stack[6]) == G_MAXDOUBLE-5.0); g_assert (g_variant_get_double (heap[6]) == G_MAXDOUBLE-5.0); g_assert_cmpstr (g_variant_get_string (stack[7], NULL), ==, "World Hello"); g_assert_cmpstr (g_variant_get_string (heap[7], NULL), ==, "World Hello"); /* Unref the variants and free the heap allocated array */ for (i = 0; i < dee_model_get_n_columns (model); i++) { g_variant_unref (stack[i]); g_variant_unref (heap[i]); } g_free (heap); } static void test_get_schema (ColumnFixture *fix, gconstpointer data) { const gchar* const *schema; guint n_cols; /* First check we don't crash when passing NULL for num_columns */ schema = dee_model_get_schema (fix->model, NULL); schema = dee_model_get_schema (fix->model, &n_cols); g_assert_cmpint (8, ==, n_cols); g_assert_cmpstr ("b", ==, schema[0]); g_assert_cmpstr ("b", ==, dee_model_get_column_schema (fix->model, 0)); g_assert_cmpstr ("y", ==, schema[1]); g_assert_cmpstr ("y", ==, dee_model_get_column_schema (fix->model, 1)); g_assert_cmpstr ("i", ==, schema[2]); g_assert_cmpstr ("i", ==, dee_model_get_column_schema (fix->model, 2)); g_assert_cmpstr ("u", ==, schema[3]); g_assert_cmpstr ("u", ==, dee_model_get_column_schema (fix->model, 3)); g_assert_cmpstr ("x", ==, schema[4]); g_assert_cmpstr ("x", ==, dee_model_get_column_schema (fix->model, 4)); g_assert_cmpstr ("t", ==, schema[5]); g_assert_cmpstr ("t", ==, dee_model_get_column_schema (fix->model, 5)); g_assert_cmpstr ("d", ==, schema[6]); g_assert_cmpstr ("d", ==, dee_model_get_column_schema (fix->model, 6)); g_assert_cmpstr ("s", ==, schema[7]); g_assert_cmpstr ("s", ==, dee_model_get_column_schema (fix->model, 7)); } static void test_no_schema (ColumnFixture *fix, gconstpointer data) { DeeModel *model; /* Create a model without a schema and try to manipulate it */ model = dee_sequence_model_new (); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { dee_model_append (model, "ignore this..."); exit (0); /* successful test run (which we shouldn't have) */ } g_test_trap_assert_failed(); g_test_trap_assert_stderr ("*doesn't have a schema*"); } static void test_bad_schemas (void) { DeeModel *model = NULL; if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { model = dee_sequence_model_new (); dee_model_set_schema (model, "", NULL); // empty signature } g_test_trap_assert_failed (); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { model = dee_sequence_model_new (); dee_model_set_schema (model, "as", "u", "(tt", NULL); // unclosed tuple } g_test_trap_assert_failed (); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) { model = dee_sequence_model_new (); dee_model_set_schema (model, "///", NULL); // illegal characters } g_test_trap_assert_failed (); } /* Inserting a NULL string should work */ static void test_null_string (ColumnFixture *fix, gconstpointer data) { DeeModel *model = fix->model; DeeModelIter *iter; /* Test that we can add a NULL string and that it'll be treated as "" */ dee_model_append (fix->model, TRUE, '1', (gint)0, (guint)0, G_GINT64_CONSTANT(0), G_GUINT64_CONSTANT (0), (gdouble)0.0, NULL); iter = dee_model_get_first_iter (model); g_assert_cmpstr ("", ==, dee_model_get_string (model, iter, 7)); dee_model_set_value (model, iter, 7, g_variant_new_string ("foo")); g_assert_cmpstr ("foo", ==, dee_model_get_string (model, iter, 7)); /* Test that we can modify a row in place and set a NULL string which * is treated as a "" */ dee_model_set (model, iter, TRUE, '1', (gint)0, (guint)0, G_GINT64_CONSTANT(0), G_GUINT64_CONSTANT (0), (gdouble)0.0, NULL); g_assert_cmpstr ("", ==, dee_model_get_string (model, iter, 7)); } dee-1.2.7+15.04.20150304/tests/peer-helper-1peer.c0000644000015300001610000000416112475676210021351 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Mikkel Kamstrup Erlandsen * */ #include "config.h" #include #include #include #include static gint n_peers = 0; static void on_peer_found (DeePeer *peer) { n_peers++; } static void on_peer_lost (DeePeer *peer) { n_peers--; } /* Expects a clone with 3 rows in it */ gint main (gint argc, gchar *argv[]) { DeePeer *peer; unsigned num_peers; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif peer = dee_peer_new (argv[1]); g_signal_connect (peer, "peer-found", G_CALLBACK (on_peer_found), NULL); g_signal_connect (peer, "peer-lost", G_CALLBACK (on_peer_lost), NULL); if (gtx_wait_for_signal (G_OBJECT (peer), 100000, "notify::swarm-leader", NULL)) g_error ("Peer helper timed out waiting for swarm leader"); /* The main process should be swarm leaders. Not us */ g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (peer)); /* At this point we shouldn't have emitted 'peer-found' yet */ g_assert_cmpint (0, ==, n_peers); if (gtx_wait_for_signal (G_OBJECT (peer), 100000, "peer-found", NULL)) g_error ("Peer helper timed out waiting for 'peer-found' signal"); g_assert_cmpint (1, ==, n_peers); /* Listing of peers includes also self */ /* Listing of peers is in flaky state atm (should == 2), see lp:1076971 */ num_peers = g_strv_length (dee_peer_list_peers (peer)); g_assert_cmpint (2, >=, num_peers); g_assert_cmpint (1, <=, num_peers); return 0; } dee-1.2.7+15.04.20150304/tests/test-model-signals.c0000644000015300001610000001214112475676210021637 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include typedef struct { DeeModel *model; } SignalsFixture; static void rows_setup (SignalsFixture *fix, gconstpointer data); static void rows_teardown (SignalsFixture *fix, gconstpointer data); static void proxy_rows_setup (SignalsFixture *fix, gconstpointer data); static void proxy_rows_teardown (SignalsFixture *fix, gconstpointer data); static void test_signal_add (SignalsFixture *fix, gconstpointer data); static void test_signal_remove (SignalsFixture *fix, gconstpointer data); static void test_signal_changed (SignalsFixture *fix, gconstpointer data); void test_model_signals_create_suite (void) { #define SEQ_DOMAIN "/Model/Sequence/Signals" #define PROXY_DOMAIN "/Model/Proxy/Signals" g_test_add (SEQ_DOMAIN"/Add", SignalsFixture, 0, rows_setup, test_signal_add, rows_teardown); g_test_add (PROXY_DOMAIN"/Add", SignalsFixture, 0, proxy_rows_setup, test_signal_add, proxy_rows_teardown); g_test_add (SEQ_DOMAIN"/Remove", SignalsFixture, 0, rows_setup, test_signal_remove, rows_teardown); g_test_add (PROXY_DOMAIN"/Remove", SignalsFixture, 0, proxy_rows_setup, test_signal_remove, proxy_rows_teardown); g_test_add (SEQ_DOMAIN"/Changed", SignalsFixture, 0, rows_setup, test_signal_changed, rows_teardown); g_test_add (PROXY_DOMAIN"/Changed", SignalsFixture, 0, proxy_rows_setup, test_signal_changed, proxy_rows_teardown); } static void rows_setup (SignalsFixture *fix, gconstpointer data) { fix->model = dee_sequence_model_new (); dee_model_set_schema (fix->model, "i", "s", NULL); g_assert (DEE_IS_SEQUENCE_MODEL (fix->model)); } static void rows_teardown (SignalsFixture *fix, gconstpointer data) { g_object_unref (fix->model); } static void proxy_rows_setup (SignalsFixture *fix, gconstpointer data) { rows_setup (fix, data); fix->model = g_object_new (DEE_TYPE_PROXY_MODEL, "back-end", fix->model, NULL); g_assert (DEE_IS_PROXY_MODEL (fix->model)); } static void proxy_rows_teardown (SignalsFixture *fix, gconstpointer data) { g_object_unref (fix->model); } static guint n_add_signals = 0; static void test_signal_add_callback (DeeModel *model, DeeModelIter *iter) { n_add_signals++; } static void test_signal_add (SignalsFixture *fix, gconstpointer data) { gint i; g_signal_connect (fix->model, "row-added", G_CALLBACK (test_signal_add_callback), NULL); n_add_signals = 0; for (i = 0; i < 10000; i++) { dee_model_append (fix->model, i, "Test"); } g_assert_cmpint (n_add_signals, ==, 10000); } static guint n_remove_signals = 0; static void test_signal_remove_callback (DeeModel *model, DeeModelIter *iter) { n_remove_signals++; } static void test_signal_remove (SignalsFixture *fix, gconstpointer data) { gint i; g_signal_connect (fix->model, "row-removed", G_CALLBACK (test_signal_remove_callback), NULL); for (i = 0; i < 10000; i++) { dee_model_append (fix->model, i, "Test"); } n_remove_signals = i = 0; dee_model_clear (fix->model); g_assert_cmpint (n_remove_signals, ==, 10000); } static guint n_changed_signals = 0; static void test_signal_changed_callback (DeeModel *model, DeeModelIter *iter) { n_changed_signals++; } static void test_signal_changed (SignalsFixture *fix, gconstpointer data) { DeeModelIter *iter; gint i; g_signal_connect (fix->model, "row-changed", G_CALLBACK (test_signal_changed_callback), NULL); for (i = 0; i < 10000; i++) { dee_model_append (fix->model, i, "Test"); } n_changed_signals = 0; iter = dee_model_get_first_iter (fix->model); while (iter != NULL && !dee_model_is_last (fix->model, iter)) { gint32 j = 0; gchar *k = "ing"; dee_model_set (fix->model, iter, j, k); iter = dee_model_next (fix->model, iter); } g_assert_cmpint (n_changed_signals, ==, 10000); n_changed_signals = 0; iter = dee_model_get_first_iter (fix->model); while (iter != NULL && !dee_model_is_last (fix->model, iter)) { dee_model_set_value (fix->model, iter, 0, g_variant_new_int32 (10)); iter = dee_model_next (fix->model, iter); } g_assert_cmpint (n_changed_signals, ==, 10000); } dee-1.2.7+15.04.20150304/tests/model-helper-append1.c0000644000015300001610000000263312475676210022034 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010-2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Michal Hruby * */ #include "config.h" #include #include #include #include /* Joins an existing model, and then tries to append a new row */ gint main (gint argc, gchar *argv[]) { DeeModel *model; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif if (argc == 2) model = dee_shared_model_new (argv[1]); else model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); if (gtx_wait_for_signal (G_OBJECT (model), 300, "notify::synchronized", NULL)) { g_critical ("Model never synchronized"); return 1; } dee_model_append (model, 68, "wumbo"); gtx_yield_main_loop (500); gtx_assert_last_unref (model); return 0; } dee-1.2.7+15.04.20150304/dee-icu-1.0.pc.in0000644000015300001610000000043012475676210017354 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: @PACKAGE_NAME@ Description: Dee ICU integration Version: @VERSION@ Libs: -L${libdir} -ldee-1.0 Cflags: -I${includedir}/dee-1.0 Requires: glib-2.0 gthread-2.0 gobject-2.0 gio-2.0 gio-unix-2.0 dee-1.2.7+15.04.20150304/README0000644000015300001610000000000212475676210015467 0ustar pbuserpbgroup00000000000000 dee-1.2.7+15.04.20150304/src/0000755000015300001610000000000012475676370015415 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/src/dee-filter-model.h0000644000015300001610000000771212475676210020704 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_FILTER_MODEL_H #define _HAVE_DEE_FILTER_MODEL_H #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_FILTER_MODEL (dee_filter_model_get_type ()) #define DEE_FILTER_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_FILTER_MODEL, DeeFilterModel)) #define DEE_FILTER_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_FILTER_MODEL, DeeFilterModelClass)) #define DEE_IS_FILTER_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_FILTER_MODEL)) #define DEE_IS_FILTER_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_FILTER_MODEL)) #define DEE_FILTER_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_FILTER_MODEL, DeeFilterModelClass)) typedef struct _DeeFilterModel DeeFilterModel; typedef struct _DeeFilterModelClass DeeFilterModelClass; typedef struct _DeeFilterModelPrivate DeeFilterModelPrivate; /* We need this one here to avoid circular refs */ typedef struct _DeeFilter DeeFilter; /** * DeeFilterModel: * * All fields in the DeeFilterModel structure are private and should never be * accessed directly */ struct _DeeFilterModel { /*< private >*/ DeeProxyModel parent; DeeFilterModelPrivate *priv; }; struct _DeeFilterModelClass { /*< private >*/ DeeProxyModelClass parent_class; /*< private >*/ void (*_dee_filter_model_1) (void); void (*_dee_filter_model_2) (void); void (*_dee_filter_model_3) (void); void (*_dee_filter_model_4) (void); }; /** * dee_filter_model_get_type: * * The GType of #DeeFilterModel * * Return value: the #GType of #DeeFilterModel **/ GType dee_filter_model_get_type (void); DeeModel* dee_filter_model_new (DeeModel *orig_model, DeeFilter *filter); gboolean dee_filter_model_contains (DeeFilterModel *self, DeeModelIter *iter); DeeModelIter* dee_filter_model_append_iter (DeeFilterModel *self, DeeModelIter *iter); DeeModelIter* dee_filter_model_prepend_iter (DeeFilterModel *self, DeeModelIter *iter); DeeModelIter* dee_filter_model_insert_iter (DeeFilterModel *self, DeeModelIter *iter, guint pos); DeeModelIter* dee_filter_model_insert_iter_before (DeeFilterModel *self, DeeModelIter *iter, DeeModelIter *pos); DeeModelIter* dee_filter_model_insert_iter_with_original_order (DeeFilterModel *self, DeeModelIter *iter); G_END_DECLS #endif /* _HAVE_DEE_FILTER_MODEL_H */ dee-1.2.7+15.04.20150304/src/dee-client.c0000644000015300001610000002656012475676210017574 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: Michal Hruby * */ /** * SECTION:dee-client * @short_description: Creates a client object you can use to connect * to a #DeeServer. * @include: dee.h * * #DeeClient is the endpoint for connecting to #DeeServer. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "dee-client.h" #include "dee-server.h" #include "dee-marshal.h" #include "trace-log.h" G_DEFINE_TYPE (DeeClient, dee_client, DEE_TYPE_PEER) #define GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEE_TYPE_CLIENT, DeeClientPrivate)) /** * DeeClientPrivate: * * Ignore this structure. **/ struct _DeeClientPrivate { GDBusConnection *connection; GCancellable *cancellable; gchar *bus_address; guint peer_found_timer_id; gulong closed_signal_handler_id; }; /* Globals */ enum { PROP_0, PROP_BUS_ADDRESS }; enum { LAST_SIGNAL }; //static guint32 _server_signals[LAST_SIGNAL] = { 0 }; /* Forwards */ static gboolean dee_client_is_swarm_leader (DeePeer *peer); static const gchar* dee_client_get_swarm_leader (DeePeer *peer); static GSList* dee_client_get_connections (DeePeer *peer); static gchar** dee_client_list_peers (DeePeer *peer); static void connecting_finished (GObject *object, GAsyncResult *res, gpointer user_data); static void connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, DeeClient *client); /* GObject methods */ static void dee_client_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { DeeClientPrivate *priv; priv = DEE_CLIENT (object)->priv; switch (property_id) { case PROP_BUS_ADDRESS: g_value_set_string (value, priv->bus_address); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void dee_client_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { DeeClientPrivate *priv; priv = DEE_CLIENT (object)->priv; switch (property_id) { case PROP_BUS_ADDRESS: if (priv->bus_address) g_free (priv->bus_address); priv->bus_address = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void dee_client_constructed (GObject *self) { DeeClientPrivate *priv; const gchar *swarm_name; GDBusConnectionFlags flags; priv = DEE_CLIENT (self)->priv; /* we should chain up the constructed method here, but peer does things we * don't want to, so not chaining up... */ swarm_name = dee_peer_get_swarm_name (DEE_PEER (self)); if (swarm_name == NULL) { g_critical ("DeeClient created without a swarm name. You must specify " "a non-NULL swarm name"); return; } if (!priv->bus_address) { priv->bus_address = dee_server_bus_address_for_name (swarm_name, TRUE); } flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT; priv->cancellable = g_cancellable_new (); g_dbus_connection_new_for_address (priv->bus_address, flags, NULL, // AuthObserver priv->cancellable, connecting_finished, self); } static void dee_client_finalize (GObject *object) { DeeClientPrivate *priv; priv = DEE_CLIENT (object)->priv; if (priv->cancellable) { g_cancellable_cancel (priv->cancellable); g_object_unref (priv->cancellable); } if (priv->closed_signal_handler_id) { g_signal_handler_disconnect (priv->connection, priv->closed_signal_handler_id); priv->closed_signal_handler_id = 0; } if (priv->connection) { g_object_unref (priv->connection); } if (priv->peer_found_timer_id) { g_source_remove (priv->peer_found_timer_id); priv->peer_found_timer_id = 0; } if (priv->bus_address) { g_free (priv->bus_address); } G_OBJECT_CLASS (dee_client_parent_class)->finalize (object); } static void dee_client_class_init (DeeClientClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); DeePeerClass *peer_class = DEE_PEER_CLASS (klass); g_type_class_add_private (klass, sizeof (DeeClientPrivate)); object_class->constructed = dee_client_constructed; object_class->get_property = dee_client_get_property; object_class->set_property = dee_client_set_property; object_class->finalize = dee_client_finalize; peer_class->is_swarm_leader = dee_client_is_swarm_leader; peer_class->get_swarm_leader = dee_client_get_swarm_leader; peer_class->get_connections = dee_client_get_connections; peer_class->list_peers = dee_client_list_peers; /** * DeeClient::bus-address: * * D-Bus address the client will connect to. If you do not specify this * property #DeeClient will use dee_server_bus_address_for_name() using * current swarm name to determine the value of this property. */ pspec = g_param_spec_string ("bus-address", "Bus address", "Bus address to use for the connection", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BUS_ADDRESS, pspec); } static void dee_client_init (DeeClient *self) { self->priv = GET_PRIVATE (self); } /** * dee_client_new: * @swarm_name: Name of swarm to join. * * Creates a new instance of #DeeClient and tries to connect to #DeeServer * created using dee_server_new(). The #DeePeer:swarm-leader property will * be set once the client connects. * * Return value: (transfer full): A newly constructed #DeeClient. */ DeeClient* dee_client_new (const gchar* swarm_name) { g_return_val_if_fail (swarm_name != NULL, NULL); return DEE_CLIENT (g_object_new (DEE_TYPE_CLIENT, "swarm-name", swarm_name, NULL)); } /** * dee_client_new_for_address: * @swarm_name: Name of swarm to join. * @bus_address: D-Bus address to use when connecting to the server. * * Creates a new instance of #DeeClient and tries to connect to @bus_address. * The #DeePeer:swarm-leader property will be set once the client connects. * * Return value: (transfer full): A newly constructed #DeeClient. */ DeeClient* dee_client_new_for_address (const gchar* swarm_name, const gchar* bus_address) { g_return_val_if_fail (swarm_name != NULL, NULL); return DEE_CLIENT (g_object_new (DEE_TYPE_CLIENT, "swarm-name", swarm_name, "bus-address", bus_address, NULL)); } /* Private Methods */ static gboolean dee_client_is_swarm_leader (DeePeer *peer) { return FALSE; } static const gchar* dee_client_get_swarm_leader (DeePeer *peer) { DeeClientPrivate *priv; priv = DEE_CLIENT (peer)->priv; return priv->connection ? g_dbus_connection_get_guid (priv->connection) : NULL; } static GSList* dee_client_get_connections (DeePeer *peer) { DeeClientPrivate *priv; GSList *list = NULL; priv = DEE_CLIENT (peer)->priv; if (priv->connection) { list = g_slist_append (list, priv->connection); } return list; } static gchar** dee_client_list_peers (DeePeer *peer) { DeeClientPrivate *priv; gchar **result; int i = 0; priv = DEE_CLIENT (peer)->priv; result = g_new (gchar*, priv->connection ? 2 : 1); if (priv->connection) { result[i++] = g_strdup (g_dbus_connection_get_guid (priv->connection)); } result[i] = NULL; return result; } static gboolean emit_peer_found (gpointer user_data) { g_return_val_if_fail (DEE_IS_CLIENT (user_data), FALSE); DeeClientPrivate *priv = DEE_CLIENT (user_data)->priv; g_signal_emit_by_name (user_data, "peer-found", g_dbus_connection_get_guid (priv->connection)); priv->peer_found_timer_id = 0; return FALSE; } static void connecting_finished (GObject *object, GAsyncResult *res, gpointer user_data) { GDBusConnection *connection; DeeClient *self; DeeClientPrivate *priv; GError *error = NULL; connection = g_dbus_connection_new_for_address_finish (res, &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning ("Unable to connect to server: %s", error->message); // swarm-leader will be set to NULL for unsuccessful connections g_object_notify (G_OBJECT (user_data), "swarm-leader"); } /* Don't touch the object in case we were cancelled, it's most likely * unreffed by now */ g_error_free (error); return; } self = DEE_CLIENT (user_data); priv = self->priv; priv->connection = connection; g_object_unref (priv->cancellable); priv->cancellable = NULL; priv->closed_signal_handler_id = g_signal_connect (connection, "closed", G_CALLBACK (connection_closed), self); g_object_notify (G_OBJECT (user_data), "swarm-leader"); g_signal_emit_by_name (user_data, "connection-acquired", connection); // FIXME: we might want to call some List method (same as DeePeer), so far // we'll just simulate an async method (tests expect this anyway) priv->peer_found_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT, emit_peer_found, user_data, NULL); } static void connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, DeeClient *client) { DeeClientPrivate *priv; g_return_if_fail (DEE_IS_CLIENT (client)); priv = client->priv; priv->connection = NULL; g_signal_handler_disconnect (connection, priv->closed_signal_handler_id); priv->closed_signal_handler_id = 0; trace_object (client, "Connection [%p] closed", connection); /* Let's do reverse order of connecting_finished */ g_signal_emit_by_name (client, "peer-lost", g_dbus_connection_get_guid (connection)); g_signal_emit_by_name (client, "connection-closed", connection); g_object_notify (G_OBJECT (client), "swarm-leader"); g_object_unref (connection); } dee-1.2.7+15.04.20150304/src/dee-analyzer.c0000644000015300001610000003610212475676210020134 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-analyzer * @short_description: Primary gateway for data indexing * @include: dee.h * * A #DeeAnalyzer takes a text stream, splits it into tokens, and runs the * tokens through a series of filtering steps. Optionally outputs collation * keys for the terms. * * One of the important use cases of analyzers in Dee is as vessel for the * indexing logic for creating a #DeeIndex from a #DeeModel. * * The recommended way to implement your own custom analyzers are by either * adding term filters to a #DeeAnalyzer or #DeeTextAnalyzer instance with * dee_analyzer_add_term_filter() and/or * derive your own subclass that overrides the dee_analyzer_tokenize() method. * Should you have very special requirements it is possible to reimplement * all aspects of the analyzer class though. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "dee-analyzer.h" G_DEFINE_TYPE (DeeAnalyzer, dee_analyzer, G_TYPE_OBJECT); #define DEE_ANALYZER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_ANALYZER, DeeAnalyzerPrivate)) typedef struct { DeeTermFilterFunc filter_func; gpointer data; GDestroyNotify destroy; } DeeTermFilter; /** * DeeAnalyzerPrivate: * * Ignore this structure. **/ struct _DeeAnalyzerPrivate { /* A list of DeeTermFilters */ GSList *term_filters; DeeTermList *term_pool; }; enum { PROP_0, }; /* * DeeAnalyzer forward declarations */ static void dee_analyzer_analyze_real (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out, DeeTermList *colkeys_out); static void dee_analyzer_tokenize_real (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out); static void dee_analyzer_add_term_filter_real (DeeAnalyzer *self, DeeTermFilterFunc filter_func, gpointer filter_data, GDestroyNotify filter_destroy); static gchar* dee_analyzer_collate_key_real (DeeAnalyzer *self, const gchar *data); static gint dee_analyzer_collate_cmp_real (DeeAnalyzer *self, const gchar *key1, const gchar *key2); /* Private forward declarations */ void _dee_analyzer_term_filter_free (DeeTermFilter *filter); DeeTermFilter* _dee_analyzer_term_filter_new (DeeTermFilterFunc filter_func, gpointer data, GDestroyNotify destroy); void _dee_analyzer_term_filter_free (DeeTermFilter *filter) { if (filter->destroy) filter->destroy (filter->data); g_slice_free (DeeTermFilter, filter); } DeeTermFilter* _dee_analyzer_term_filter_new (DeeTermFilterFunc filter_func, gpointer data, GDestroyNotify destroy) { DeeTermFilter *self; self = g_slice_new (DeeTermFilter); self->filter_func = filter_func; self->data = data; self->destroy = destroy; return self; } /* GObject stuff */ static void dee_analyzer_finalize (GObject *object) { DeeAnalyzerPrivate *priv = DEE_ANALYZER (object)->priv; g_slist_free_full (priv->term_filters, (GDestroyNotify) _dee_analyzer_term_filter_free); priv->term_filters = NULL; if (priv->term_pool) { g_object_unref (priv->term_pool); priv->term_pool = NULL; } G_OBJECT_CLASS (dee_analyzer_parent_class)->finalize (object); } static void dee_analyzer_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { //DeeAnalyzerPrivate *priv = DEE_ANALYZER (object)->priv; switch (id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_analyzer_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_analyzer_class_init (DeeAnalyzerClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_analyzer_finalize; obj_class->get_property = dee_analyzer_get_property; obj_class->set_property = dee_analyzer_set_property; klass->analyze = dee_analyzer_analyze_real; klass->tokenize = dee_analyzer_tokenize_real; klass->add_term_filter = dee_analyzer_add_term_filter_real; klass->collate_key = dee_analyzer_collate_key_real; klass->collate_cmp = dee_analyzer_collate_cmp_real; /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeAnalyzerPrivate)); } static void dee_analyzer_init (DeeAnalyzer *self) { DeeAnalyzerPrivate *priv; priv = self->priv = DEE_ANALYZER_GET_PRIVATE (self); priv->term_filters = NULL; priv->term_pool = (DeeTermList*) g_object_new (DEE_TYPE_TERM_LIST, NULL); } /* * Default implementations */ static void dee_analyzer_analyze_real (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out, DeeTermList *colkeys_out) { DeeAnalyzerPrivate *priv; GSList *iter; DeeTermList *in, *out, *tmp, *tmp_term_pool; gint i; gchar *colkey; const gchar *term; g_return_if_fail (DEE_IS_ANALYZER (self)); g_return_if_fail (data != NULL); priv = self->priv; dee_term_list_clear (priv->term_pool); tmp_term_pool = dee_term_list_clone (priv->term_pool); if (terms_out) dee_term_list_clear (terms_out); if (colkeys_out) dee_term_list_clear (colkeys_out); dee_analyzer_tokenize (self, data, priv->term_pool); /* Run terms through all filters. Result is that we'll have * the final terms in the 'in' term list */ in = priv->term_pool; out = tmp_term_pool; for (iter = priv->term_filters; iter; iter = iter->next) { DeeTermFilter *filter = (DeeTermFilter*) iter->data; filter->filter_func (in, out, filter->data); /* Clear and swap in/out buffers */ tmp = dee_term_list_clear (in); in = out; out = tmp; } /* Copy terms to output and generate colkeys if requested */ for (i = 0; i < dee_term_list_num_terms (in); i++) { term = dee_term_list_get_term (in, i); if (terms_out) dee_term_list_add_term (terms_out, term); if (colkeys_out) { colkey = dee_analyzer_collate_key (self, term); dee_term_list_add_term (colkeys_out, colkey); g_free (colkey); } } g_object_unref (tmp_term_pool); } /* Default tokenization is a no-op */ static void dee_analyzer_tokenize_real (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out) { g_return_if_fail (DEE_IS_ANALYZER (self)); g_return_if_fail (data != NULL); g_return_if_fail (DEE_IS_TERM_LIST (terms_out)); dee_term_list_add_term (terms_out, data); } static void dee_analyzer_add_term_filter_real (DeeAnalyzer *self, DeeTermFilterFunc filter_func, gpointer filter_data, GDestroyNotify filter_destroy) { DeeAnalyzerPrivate *priv; g_return_if_fail (DEE_IS_ANALYZER (self)); g_return_if_fail (filter_func != NULL); priv = self->priv; priv->term_filters = g_slist_append (priv->term_filters, _dee_analyzer_term_filter_new(filter_func, filter_data, filter_destroy)); } static gchar* dee_analyzer_collate_key_real (DeeAnalyzer *self, const gchar *data) { g_return_val_if_fail (DEE_IS_ANALYZER (self), NULL); g_return_val_if_fail (data != NULL, NULL); return g_strdup (data); } gint dee_analyzer_collate_cmp_real (DeeAnalyzer *self, const gchar *key1, const gchar *key2) { g_return_val_if_fail (DEE_IS_ANALYZER (self), 0); g_return_val_if_fail (key1 != NULL, 0); g_return_val_if_fail (key2 != NULL, 0); return strcmp (key1, key2); } /* * Public API */ /** * dee_analyzer_analyze: * @self: The analyzer to use * @data: The input data to analyze * @terms_out: (allow-none): A #DeeTermList to place the generated terms in. * If %NULL to terms are generated * @colkeys_out: (allow-none): A #DeeTermList to place generated collation keys in. * If %NULL no collation keys are generated * * Extract terms and or collation keys from some input data (which is normally, * but not necessarily, a UTF-8 string). * * The terms and corresponding collation keys will be written in order to the * provided #DeeTermLists. * * Implementation notes for subclasses: * The analysis process must call dee_analyzer_tokenize() and run the tokens * through all term filters added with dee_analyzer_add_term_filter(). * Collation keys must be generated with dee_analyzer_collate_key(). */ void dee_analyzer_analyze (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out, DeeTermList *colkeys_out) { DeeAnalyzerClass *klass; g_return_if_fail (DEE_IS_ANALYZER (self)); klass = DEE_ANALYZER_GET_CLASS (self); (* klass->analyze) (self, data, terms_out, colkeys_out); } /** * dee_analyzer_tokenize: * @self: The analyzer to use * @data: The input data to analyze * @terms_out: A #DeeTermList to place the generated tokens in. * * Tokenize some input data (which is normally, but not necessarily, * a UTF-8 string). * * Tokenization splits the input data into constituents (in most cases words), * but does not run it through any of the term filters set for the analyzer. * It is undefined if the tokenization process itself does any normalization. */ void dee_analyzer_tokenize (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out) { DeeAnalyzerClass *klass; g_return_if_fail (DEE_IS_ANALYZER (self)); klass = DEE_ANALYZER_GET_CLASS (self); (* klass->tokenize) (self, data, terms_out); } /** * dee_analyzer_add_term_filter: * @self: The analyzer to add a term filter to * @filter_func: (scope notified): Function to call * @filter_data: (closure): Data to pass to @filter_func when it is invoked * @filter_destroy: (allow-none): Called on @filter_data when the #DeeAnalyzer * owning the filter is destroyed * * Register a #DeeTermFilterFunc to be called whenever dee_analyzer_analyze() * is called. * * Term filters can be used to normalize, add, or remove terms from an input * data stream. */ void dee_analyzer_add_term_filter (DeeAnalyzer *self, DeeTermFilterFunc filter_func, gpointer filter_data, GDestroyNotify filter_destroy) { DeeAnalyzerClass *klass; g_return_if_fail (DEE_IS_ANALYZER (self)); klass = DEE_ANALYZER_GET_CLASS (self); (* klass->add_term_filter) (self, filter_func, filter_data, filter_destroy); } /** * dee_analyzer_collate_key: * @self: The analyzer to generate a collation key with * @data: The input data to generate a collation key for * * Generate a collation key for a set of input data (usually a UTF-8 string * passed through tokenization and term filters of the analyzer). * * The default implementation just calls g_strdup(). * * Returns: A newly allocated collation key. Use dee_analyzer_collate_cmp() or * dee_analyzer_collate_cmp_func() to compare collation keys. Free * with g_free(). */ gchar* dee_analyzer_collate_key (DeeAnalyzer *self, const gchar *data) { DeeAnalyzerClass *klass; g_return_val_if_fail (DEE_IS_ANALYZER (self), NULL); klass = DEE_ANALYZER_GET_CLASS (self); return (* klass->collate_key) (self, data); } /** * dee_analyzer_collate_cmp: * @self: The analyzer to use when comparing collation keys * @key1: The first collation key to compare * @key2: The second collation key to compare * * Compare collation keys generated by dee_analyzer_collate_key() with similar * semantics as strcmp(). See also dee_analyzer_collate_cmp_func() if you * need a version of this function that works as a #GCompareDataFunc. * * The default implementation in #DeeAnalyzer just uses strcmp(). * * Returns: -1, 0 or 1, if @key1 is <, == or > than @key2. */ gint dee_analyzer_collate_cmp (DeeAnalyzer *self, const gchar *key1, const gchar *key2) { DeeAnalyzerClass *klass; g_return_val_if_fail (DEE_IS_ANALYZER (self), 0); klass = DEE_ANALYZER_GET_CLASS (self); return (* klass->collate_cmp) (self, key1, key2); } /** * dee_analyzer_collate_cmp_func: * @key1: The first key to compare * @key2: The second key to compare * @analyzer: The #DeeAnalyzer to use for the comparison * * A #GCompareDataFunc using a #DeeAnalyzer to compare the keys. This is just * a convenience wrapper around dee_analyzer_collate_cmp(). * * Returns: -1, 0 or 1, if @key1 is <, == or > than @key2. */ gint dee_analyzer_collate_cmp_func (const gchar *key1, const gchar *key2, gpointer analyzer) { return dee_analyzer_collate_cmp ((DeeAnalyzer*)analyzer, key1, key2); } DeeAnalyzer* dee_analyzer_new (void) { return (DeeAnalyzer*) g_object_new (DEE_TYPE_ANALYZER, NULL); } dee-1.2.7+15.04.20150304/src/trace-log.c0000644000015300001610000000272512475676210017435 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #include #include "trace-log.h" void trace_object_va (void *obj, const gchar *format, va_list args) { GString *tmp; if (!G_IS_OBJECT(obj)) { g_critical ("Failed to log '%s' for object. Not an object.", format); return; } tmp = g_string_sized_new (512); g_string_printf (tmp, "(%s@%p): ", g_type_name(G_OBJECT_TYPE(obj)), obj); g_string_append (tmp, format); g_logv (TRACE_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, tmp->str, args); g_string_free (tmp, TRUE); } void trace_object_real (void *obj, const gchar *format, ...) { va_list args; va_start (args, format); trace_object_va (obj, format, args); va_end (args); } dee-1.2.7+15.04.20150304/src/dee-sequence-model.c0000644000015300001610000011260712475676210021222 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-sequence-model * @short_description: A #DeeModel implementation backed by a #GSequence * @include: dee.h * * #DeeSequenceModel is an implementation of the #DeeModel interface * backed by a #GSequence. It extends #DeeSerializableModel so that you may use * it as back end model for a #DeeSharedModel. * * The implementation is backed by a #GSequence giving a good tradeoff between * random access time versus random- insertion and deletion times. Notably the * dee_model_insert_sorted() and dee_model_find_sorted() methods use the * underlying tree structure to guarantee a O(log(N)) * profile. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "dee-model.h" #include "dee-serializable-model.h" #include "dee-sequence-model.h" #include "dee-marshal.h" #include "trace-log.h" static void dee_sequence_model_model_iface_init (DeeModelIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeSequenceModel, dee_sequence_model, DEE_TYPE_SERIALIZABLE_MODEL, G_IMPLEMENT_INTERFACE (DEE_TYPE_MODEL, dee_sequence_model_model_iface_init)); #define DEE_SEQUENCE_MODEL_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_SEQUENCE_MODEL, DeeSequenceModelPrivate)) /* Signal ids for emitting row update signals a just a smidgeon faster */ static guint sigid_row_added; static guint sigid_row_removed; static guint sigid_row_changed; /** * DeeSequenceModelPrivate: * * Ignore this structure. */ struct _DeeSequenceModelPrivate { /* Row data is is an array of gpointers. The last pointer in the array * points to the list of tags for the row. All items before are straight * old GVariants */ GSequence *sequence; /* The tag registry. The data members of the list simply contain the * GDestroyNotify for the tag. The tag handle is the offset into the * list + 1. We need the +1 to discern the first tag from a NULL pointer. * We can use offsets as we expect only very few tags per model */ GSList *tags; /* Flag marking if we are in a transaction */ gboolean setting_many; }; /* * DeeModel forward declarations */ static guint dee_sequence_model_get_n_rows (DeeModel *self); static DeeModelIter* dee_sequence_model_append_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_sequence_model_prepend_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_sequence_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static DeeModelIter* dee_sequence_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found); static void dee_sequence_model_remove (DeeModel *self, DeeModelIter *iter); static void dee_sequence_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static void dee_sequence_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value); static void dee_sequence_model_set_value_silently (DeeModel *self, DeeModelIter *iter, guint column, const gchar *col_schema, GVariant *value); static GVariant* dee_sequence_model_get_value (DeeModel *self, DeeModelIter *iter, guint column); static GVariant** dee_sequence_model_get_row (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members); static DeeModelIter* dee_sequence_model_get_first_iter (DeeModel *self); static DeeModelIter* dee_sequence_model_get_last_iter (DeeModel *self); static DeeModelIter* dee_sequence_model_get_iter_at_row (DeeModel *self, guint row); static gboolean dee_sequence_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column); static guchar dee_sequence_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column); static gint32 dee_sequence_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column); static guint32 dee_sequence_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column); static gint64 dee_sequence_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column); static guint64 dee_sequence_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column); static gdouble dee_sequence_model_get_double (DeeModel *self, DeeModelIter *iter, guint column); static const gchar* dee_sequence_model_get_string (DeeModel *self, DeeModelIter *iter, guint column); static DeeModelIter* dee_sequence_model_next (DeeModel *self, DeeModelIter *iter); static DeeModelIter* dee_sequence_model_prev (DeeModel *self, DeeModelIter *iter); static gboolean dee_sequence_model_is_first (DeeModel *self, DeeModelIter *iter); static gboolean dee_sequence_model_is_last (DeeModel *self, DeeModelIter *iter); static guint dee_sequence_model_get_position (DeeModel *self, DeeModelIter *iter); static DeeModelTag* dee_sequence_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy); static gpointer dee_sequence_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag); static void dee_sequence_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value); /* * Private forwards */ static gpointer * dee_sequence_model_create_empty_row (DeeModel *self); static void dee_sequence_model_free_row (DeeSequenceModel *self, GSequenceIter *iter); static void dee_sequence_model_find_tag (DeeSequenceModel *self, DeeModelIter *iter, DeeModelTag *tag, GSList **out_row_tag, GSList **out_tag); /* GObject Init */ static void dee_sequence_model_finalize (GObject *object) { DeeSequenceModel *self = DEE_SEQUENCE_MODEL (object); DeeSequenceModelPrivate *priv = self->priv; GSequenceIter *iter, *end; /* Free row data */ end = g_sequence_get_end_iter (priv->sequence); iter = g_sequence_get_begin_iter (priv->sequence); while (iter != end) { dee_sequence_model_free_row (self, iter); iter = g_sequence_iter_next (iter); } /* Free our GSequence */ g_sequence_free (priv->sequence); priv->sequence = NULL; /* Free the tag registry. The list members need no freeing, * they are just function pointers */ g_slist_free (priv->tags); priv->tags = NULL; G_OBJECT_CLASS (dee_sequence_model_parent_class)->finalize (object); } static void dee_sequence_model_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { switch (id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_sequence_model_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_sequence_model_class_init (DeeSequenceModelClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_sequence_model_finalize; obj_class->set_property = dee_sequence_model_set_property; obj_class->get_property = dee_sequence_model_get_property; /* Find signal ids for the model modification signals */ sigid_row_added = g_signal_lookup ("row-added", DEE_TYPE_MODEL); sigid_row_removed = g_signal_lookup ("row-removed", DEE_TYPE_MODEL); sigid_row_changed = g_signal_lookup ("row-changed", DEE_TYPE_MODEL); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeSequenceModelPrivate)); } static void dee_sequence_model_model_iface_init (DeeModelIface *iface) { iface->get_n_rows = dee_sequence_model_get_n_rows; iface->prepend_row = dee_sequence_model_prepend_row; iface->append_row = dee_sequence_model_append_row; iface->insert_row_before = dee_sequence_model_insert_row_before; iface->find_row_sorted = dee_sequence_model_find_row_sorted; iface->remove = dee_sequence_model_remove; iface->set_row = dee_sequence_model_set_row; iface->set_value = dee_sequence_model_set_value; iface->get_value = dee_sequence_model_get_value; iface->get_row = dee_sequence_model_get_row; iface->get_first_iter = dee_sequence_model_get_first_iter; iface->get_last_iter = dee_sequence_model_get_last_iter; iface->get_iter_at_row = dee_sequence_model_get_iter_at_row; iface->get_bool = dee_sequence_model_get_bool; iface->get_uchar = dee_sequence_model_get_uchar; iface->get_int32 = dee_sequence_model_get_int32; iface->get_uint32 = dee_sequence_model_get_uint32; iface->get_int64 = dee_sequence_model_get_int64; iface->get_uint64 = dee_sequence_model_get_uint64; iface->get_double = dee_sequence_model_get_double; iface->get_string = dee_sequence_model_get_string; iface->next = dee_sequence_model_next; iface->prev = dee_sequence_model_prev; iface->is_first = dee_sequence_model_is_first; iface->is_last = dee_sequence_model_is_last; iface->get_position = dee_sequence_model_get_position; iface->register_tag = dee_sequence_model_register_tag; iface->get_tag = dee_sequence_model_get_tag; iface->set_tag = dee_sequence_model_set_tag; } static void dee_sequence_model_init (DeeSequenceModel *model) { DeeSequenceModelPrivate *priv; priv = model->priv = DEE_SEQUENCE_MODEL_GET_PRIVATE (model); priv->sequence = g_sequence_new (NULL); priv->tags = NULL; priv->setting_many = FALSE; } /* Private Methods */ /* * DeeModel Interface Implementation */ static guint dee_sequence_model_get_n_rows (DeeModel *self) { DeeSequenceModelPrivate *priv; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), 0); priv = ((DeeSequenceModel *) self)->priv; return g_sequence_get_length (priv->sequence); } static DeeModelIter* dee_sequence_model_prepend_row (DeeModel *self, GVariant **row_members) { DeeSequenceModel *_self = (DeeSequenceModel *) self; DeeSequenceModelPrivate *priv; DeeModelIter *iter; gpointer *row; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (_self), NULL); g_return_val_if_fail (row_members != NULL, NULL); priv = _self->priv; row = dee_sequence_model_create_empty_row (self); iter = (DeeModelIter*) g_sequence_prepend (priv->sequence, row); priv->setting_many = TRUE; dee_model_set_row (self, iter, row_members); priv->setting_many = FALSE; dee_serializable_model_inc_seqnum (self); g_signal_emit (self, sigid_row_added, 0, iter); return iter; } static DeeModelIter* dee_sequence_model_append_row (DeeModel *self, GVariant **row_members) { DeeSequenceModel *_self = (DeeSequenceModel *) self; DeeSequenceModelPrivate *priv; DeeModelIter *iter; gpointer *row; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (_self), NULL); g_return_val_if_fail (row_members != NULL, NULL); priv = _self->priv; row = dee_sequence_model_create_empty_row (self); iter = (DeeModelIter*) g_sequence_append (priv->sequence, row); priv->setting_many = TRUE; dee_model_set_row (self, iter, row_members); priv->setting_many = FALSE; dee_serializable_model_inc_seqnum (self); g_signal_emit (self, sigid_row_added, 0, iter); return iter; } static DeeModelIter* dee_sequence_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeSequenceModelPrivate *priv; gpointer *row; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (row_members != NULL, NULL); priv = DEE_SEQUENCE_MODEL (self)->priv; row = dee_sequence_model_create_empty_row (self); iter = (DeeModelIter*) g_sequence_insert_before ((GSequenceIter *) iter, row); priv->setting_many = TRUE; dee_model_set_row (self, iter, row_members); priv->setting_many = FALSE; dee_serializable_model_inc_seqnum (self); g_signal_emit (self, sigid_row_added, 0, iter); return iter; } /* logN search using the tree structure of GSeq */ static DeeModelIter* dee_sequence_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found) { DeeSequenceModelPrivate *priv; GSequenceIter *iter; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); g_return_val_if_fail (row_spec != NULL, NULL); g_return_val_if_fail (cmp_func != NULL, NULL); priv = DEE_SEQUENCE_MODEL (self)->priv; iter = g_sequence_search (priv->sequence, row_spec, (GCompareDataFunc)cmp_func, user_data); /* Kinda awkward - if we did find the row then GSequence has placed just * after the row we wanted. If we did not find it, then we're in the right * place */ if (!g_sequence_iter_is_begin (iter)) { GSequenceIter *jter = g_sequence_iter_prev (iter); if (cmp_func (g_sequence_get (jter), row_spec, user_data) == 0) { if (out_was_found != NULL) *out_was_found = TRUE; return (DeeModelIter *) jter; } } if (out_was_found != NULL) *out_was_found = FALSE; return (DeeModelIter *) iter; } static void dee_sequence_model_remove (DeeModel *self, DeeModelIter *iter_) { DeeSequenceModel *_self = (DeeSequenceModel *)self; GSequenceIter *iter = (GSequenceIter *)iter_; g_return_if_fail (DEE_IS_SEQUENCE_MODEL (_self)); g_return_if_fail (iter != NULL); g_return_if_fail (!g_sequence_iter_is_end (iter)); if (iter) { /* Emit the removed signal while the iter is still valid, * but after we increased the seqnum */ dee_serializable_model_inc_seqnum (self); g_signal_emit (self, sigid_row_removed, 0, iter_); dee_sequence_model_free_row (_self, iter); g_sequence_remove (iter); } else { g_warning ("Unable to remove row '%p': does not exists", iter_); } } static void dee_sequence_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value) { DeeSequenceModel *_self = (DeeSequenceModel *)self; DeeSequenceModelPrivate *priv; g_return_if_fail (DEE_IS_SEQUENCE_MODEL (_self)); g_return_if_fail (iter != NULL); g_return_if_fail (value != NULL); g_return_if_fail (column < dee_model_get_n_columns (self)); priv = _self->priv; dee_sequence_model_set_value_silently (self, iter, column, dee_model_get_column_schema (self, column), value); if (priv->setting_many == FALSE) { dee_serializable_model_inc_seqnum (self); g_signal_emit (self, sigid_row_changed, 0, iter); } } static void dee_sequence_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeSequenceModel *_self = (DeeSequenceModel *)self; DeeSequenceModelPrivate *priv; guint i, n_cols; const gchar *const *schema; g_return_if_fail (DEE_IS_SEQUENCE_MODEL (_self)); g_return_if_fail (iter != NULL); g_return_if_fail (row_members != NULL); priv = _self->priv; schema = dee_model_get_schema (self, &n_cols); for (i = 0; i < n_cols; i++) { dee_sequence_model_set_value_silently (self, iter, i, schema[i], row_members[i]); } if (priv->setting_many == FALSE) { dee_serializable_model_inc_seqnum (self); g_signal_emit (self, sigid_row_changed, 0, iter); } } static void dee_sequence_model_set_value_silently (DeeModel *self, DeeModelIter *iter, guint column, const gchar *col_schema, GVariant *value) { gpointer *row; g_return_if_fail (g_variant_type_equal (g_variant_get_type (value), G_VARIANT_TYPE (col_schema))); row = g_sequence_get ((GSequenceIter *) iter); if (G_UNLIKELY (row == NULL)) { g_critical ("Unable to set value. NULL row data in DeeSequenceModel@%p " "at position %u. The row has probably been removed", self, dee_model_get_position (self, iter)); return; } if (row[column] != NULL) g_variant_unref (row[column]); row[column] = g_variant_ref_sink (value); } static GVariant* dee_sequence_model_peek_value (DeeModel *self, DeeModelIter *iter, guint column) { gpointer *row; row = g_sequence_get ((GSequenceIter *) iter); if (G_UNLIKELY (row == NULL)) { g_critical ("Unable to get value. NULL row data in DeeSequenceModel@%p " "at position %u. The row has probably been removed", self, dee_model_get_position (self, iter)); return NULL; } return row[column]; } static GVariant* dee_sequence_model_get_value (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (column < dee_model_get_n_columns (self), NULL); GVariant *val = dee_sequence_model_peek_value (self, iter, column); if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get value. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return NULL; } return g_variant_ref (val); } static GVariant** dee_sequence_model_get_row (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members) { guint col, n_cols; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); n_cols = dee_model_get_n_columns (self); if (out_row_members == NULL) out_row_members = g_new0 (GVariant*, n_cols + 1); /* We use peek_value() here because it saves us from some expensive checks * compared to get_value(), that we can guarantee from this call site anyway */ for (col = 0; col < n_cols; col++) out_row_members[col] = g_variant_ref ( dee_sequence_model_peek_value (self, iter, col)); return out_row_members; } static DeeModelIter* dee_sequence_model_get_first_iter (DeeModel *self) { DeeSequenceModel *_self = (DeeSequenceModel *)self; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (_self), NULL); return (DeeModelIter *) g_sequence_get_begin_iter (_self->priv->sequence); } static DeeModelIter* dee_sequence_model_get_last_iter (DeeModel *self) { DeeSequenceModel *_self = (DeeSequenceModel *)self; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (_self), NULL); return (DeeModelIter *) g_sequence_get_end_iter (_self->priv->sequence); } static DeeModelIter* dee_sequence_model_get_iter_at_row (DeeModel *self, guint row) { DeeSequenceModel *_self = (DeeSequenceModel *)self; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); return (DeeModelIter *) g_sequence_get_iter_at_pos (_self->priv->sequence, row); } static gboolean dee_sequence_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column); if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get boolean. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return FALSE; } return g_variant_get_boolean (val); } static guchar dee_sequence_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get byte. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return '\0'; } return g_variant_get_byte (val); } static gint32 dee_sequence_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get int32. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return 0; } return g_variant_get_int32 (val); } static guint32 dee_sequence_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get uint32. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return 0; } return g_variant_get_uint32 (val); } static gint64 dee_sequence_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get int64. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return G_GINT64_CONSTANT (0); } return g_variant_get_int64 (val); } static guint64 dee_sequence_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get uint64. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return G_GUINT64_CONSTANT (0); } return g_variant_get_uint64 (val); } static gdouble dee_sequence_model_get_double (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get double. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return 0; } return g_variant_get_double (val); } static const gchar* dee_sequence_model_get_string (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *val = dee_sequence_model_peek_value (self, iter, column);; if (G_UNLIKELY (val == NULL)) { g_critical ("Unable to get string. Column %i in DeeSequenceModel@%p" " holds a NULL value in row %u", column, self, dee_model_get_position (self, iter)); return NULL; } return g_variant_get_string (val, NULL); } static DeeModelIter* dee_sequence_model_next (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!g_sequence_iter_is_end ((GSequenceIter*) iter), NULL); return (DeeModelIter *) g_sequence_iter_next ((GSequenceIter *)iter); } static DeeModelIter* dee_sequence_model_prev (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!g_sequence_iter_is_begin ((GSequenceIter*) iter), NULL); return (DeeModelIter *) g_sequence_iter_prev ((GSequenceIter *)iter); } static gboolean dee_sequence_model_is_first (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), FALSE); g_return_val_if_fail (iter, FALSE); return g_sequence_iter_is_begin ((GSequenceIter *)iter); } static gboolean dee_sequence_model_is_last (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), FALSE); g_return_val_if_fail (iter, FALSE); return g_sequence_iter_is_end ((GSequenceIter *)iter); } static guint dee_sequence_model_get_position (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), FALSE); g_return_val_if_fail (iter, FALSE); return g_sequence_iter_get_position ((GSequenceIter *)iter); } static DeeModelTag* dee_sequence_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy) { DeeSequenceModelPrivate *priv; GSequenceIter *iter, *end; gpointer *row; guint tag_handle, n_cols; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); priv = DEE_SEQUENCE_MODEL (self)->priv; /* Register the tag. Multiple iterations of the tags list is not * a big deal here because we only expect very few tags per model */ priv->tags = g_slist_append (priv->tags, tag_destroy); tag_handle = g_slist_length (priv->tags); /* Update all existing rows to have this tag */ n_cols = dee_model_get_n_columns (self); end = g_sequence_get_end_iter (priv->sequence); iter = g_sequence_get_begin_iter (priv->sequence); while (iter != end) { row = g_sequence_get (iter); row[n_cols] = g_slist_append (row[n_cols], NULL); iter = g_sequence_iter_next (iter); } return (DeeModelTag *) GUINT_TO_POINTER (tag_handle); } static gpointer dee_sequence_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag) { DeeSequenceModel *_self; GSList *row_tag_l, *tag_l; g_return_val_if_fail (DEE_IS_SEQUENCE_MODEL (self), NULL); g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (tag != NULL, NULL); _self = DEE_SEQUENCE_MODEL (self); dee_sequence_model_find_tag (_self, iter, tag, &row_tag_l, &tag_l); if (row_tag_l == NULL || tag_l == NULL) { g_critical ("Failed to get tag %u on %s@%p", GPOINTER_TO_UINT (tag), G_OBJECT_TYPE_NAME (self), self); return NULL; } return row_tag_l->data; } void dee_sequence_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value) { DeeSequenceModel *_self; GSList *row_tag_l, *tag_l; GDestroyNotify destroy; gpointer old_value; g_return_if_fail (DEE_IS_SEQUENCE_MODEL (self)); g_return_if_fail (iter != NULL); g_return_if_fail (tag != NULL); _self = DEE_SEQUENCE_MODEL (self); dee_sequence_model_find_tag (_self, iter, tag, &row_tag_l, &tag_l); if (row_tag_l == NULL || tag_l == NULL) { g_critical ("Failed to set tag %u on %s@%p", GPOINTER_TO_UINT (tag), G_OBJECT_TYPE_NAME (self), self); return; } destroy = (GDestroyNotify) tag_l->data; old_value = row_tag_l->data; if (destroy && old_value) { destroy (old_value); } row_tag_l->data = value; } /* * Private methods */ /* Create an array with the right amount of elements, all set to NULL */ static gpointer* dee_sequence_model_create_empty_row (DeeModel *self) { DeeSequenceModelPrivate *priv; gpointer *row; guint n_columns; GSList *tag_iter; /* The row tags are stored as a GSList on the n_columns+1 index of the row. * Zeroing the memory below is important since it gives us an empty GSList * for the tags on the final position */ priv = ((DeeSequenceModel *)self)->priv; n_columns = dee_model_get_n_columns (self); row = g_slice_alloc0 (sizeof (gpointer) * (n_columns + 1)); /* Populate the row tag list to have the same length as our tag registry */ for (tag_iter = priv->tags; tag_iter; tag_iter = tag_iter->next) { row[n_columns] = g_slist_prepend (row[n_columns], NULL); } return row; } static void dee_sequence_model_free_row (DeeSequenceModel *self, GSequenceIter *iter) { DeeSequenceModelPrivate *priv; gpointer *row; guint n_cols, i; GSList *tag_iter, *row_tag_iter, *dum; GDestroyNotify destroy; priv = self->priv; row = g_sequence_get (iter); n_cols = dee_model_get_n_columns (DEE_MODEL (self)); /* Free the row data */ for (i = 0; i < n_cols; i++) g_variant_unref (row[i]); /* Free any row tags */ row_tag_iter = row[n_cols]; tag_iter = priv->tags; while (row_tag_iter && tag_iter) { destroy = (GDestroyNotify) tag_iter->data; if (destroy != NULL && row_tag_iter->data != NULL) destroy (row_tag_iter->data); /* Free the GSList element for the row tag while we're here anyway */ dum = row_tag_iter->next; g_slist_free_1 (row_tag_iter); row_tag_iter = dum; tag_iter = tag_iter->next; } if (row_tag_iter != NULL) { g_critical ("Internal error: Row tags leaked. " "More row tags for this row than there are registered tags."); } else if (tag_iter != NULL) { g_critical ("Internal error: Row tags leaked. " "More tags registered than there are tags for this row."); } /* Free the row itself */ g_slice_free1 (sizeof (gpointer) * (n_cols + 1), row); /* Set the row data to NULL to help debugging for consumers accessing * removed rows*/ g_sequence_set (iter, NULL); } static void dee_sequence_model_find_tag (DeeSequenceModel *self, DeeModelIter *iter, DeeModelTag *tag, GSList **out_row_tag, GSList **out_tag) { DeeSequenceModelPrivate *priv; gpointer *row; guint tag_offset, i, n_cols; GSList *row_tag_iter, *tag_iter; priv = self->priv; row = g_sequence_get ((GSequenceIter *) iter); n_cols = dee_model_get_n_columns (DEE_MODEL (self)); tag_offset = GPOINTER_TO_UINT (tag); if (G_UNLIKELY (priv->sequence == NULL)) { g_critical ("Access to freed DeeSequenceModel detected " "when looking up tag on DeeSequenceModel@%p", self); goto not_found; } if (G_UNLIKELY (priv->tags == NULL)) { g_critical ("Unable to look up tag. No tags registered on " "DeeSequenceModel@%p", self); goto not_found; } if (G_UNLIKELY (row == NULL)) { g_critical ("Unable to look up tag. No row data. " "The row has probably been removed "); goto not_found; } /* Find tag at right offset */ row_tag_iter = row[n_cols]; tag_iter = priv->tags; i = 1; // remember 1-based offset for tag handles while (row_tag_iter && tag_iter && i < tag_offset) { row_tag_iter = row_tag_iter->next; tag_iter = tag_iter->next; i++; } if (i != tag_offset) { g_critical ("Unable to find tag %u for %s@%p", tag_offset, G_OBJECT_TYPE_NAME (self), self); goto not_found; } *out_row_tag = row_tag_iter; *out_tag = tag_iter; return; not_found: *out_row_tag = NULL; *out_tag = NULL; } /* * Constructors */ /** * dee_sequence_model_new: * * Create a new #DeeSequenceModel. Before using it you must normally set a * schema on it by calling dee_model_set_schema(). * * Return value: (transfer full) (type DeeSequenceModel): A newly created * #DeeSequenceModel. Free with g_object_unref(). * */ DeeModel* dee_sequence_model_new () { DeeModel *self; self = DEE_MODEL (g_object_new (DEE_TYPE_SEQUENCE_MODEL, NULL)); return self; } dee-1.2.7+15.04.20150304/src/Makefile.am0000644000015300001610000000763412475676210017454 0ustar pbuserpbgroup00000000000000NULL = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = INTROSPECTION_GIRS = INTROSPECTION_SCANNER_ARGS = --warn-all -include $(INTROSPECTION_MAKEFILE) ## # DBus introspection XML ## # create a .h file containing the introspection data in a variable for gdbus %-xml.h : dbus/%.xml $(AM_V_GEN)name=`basename $< | sed -e 's/[-\.]/_/g' -e 's/.xml/_xml/g'`; \ echo "static const gchar $$name[] = " > $@; \ cat $< | tr \" \' | sed 's/^/\"/g' | sed 's/$$/\"/g' >> $@; \ echo ";" >> $@; BUILT_SOURCES += \ com.canonical.Dee.Model-xml.h \ com.canonical.Dee.Peer-xml.h EXTRA_DIST += \ dbus/com.canonical.Dee.Model.xml \ dbus/com.canonical.Dee.Peer.xml ## # Development headers ## devel_headersdir = $(includedir)/dee-1.0 devel_headers = \ dee.h \ dee-analyzer.h \ dee-file-resource-manager.h \ dee-filter-model.h \ dee-filter.h \ dee-hash-index.h \ dee-index.h \ dee-model.h \ dee-model-reader.h \ dee-peer.h \ dee-server.h \ dee-client.h \ dee-proxy-model.h \ dee-resource-manager.h \ dee-result-set.h \ dee-sequence-model.h \ dee-serializable.h \ dee-serializable-model.h \ dee-shared-model.h \ dee-term-list.h \ dee-text-analyzer.h \ dee-transaction.h \ dee-tree-index.h \ $(NULL) devel_headers_HEADERS = \ $(devel_headers) \ $(NULL) ## # Build libdee ## lib_LTLIBRARIES = libdee-1.0.la libdee_1_0_la_SOURCES = \ $(devel_headers) \ dee-analyzer.c \ dee-file-resource-manager.c \ dee-filter-model.c \ dee-filter.c \ dee-glist-result-set.h \ dee-glist-result-set.c \ dee-hash-index.c \ dee-index.c \ dee-model.c \ dee-model-reader.c \ dee-peer.c \ dee-server.c \ dee-client.c \ dee-proxy-model.c \ dee-resource-manager.c \ dee-result-set.c \ dee-sequence-model.c \ dee-serializable.c \ dee-serializable-model.c \ dee-shared-model.c \ dee-term-list.c \ dee-text-analyzer.c \ dee-transaction.c \ dee-tree-index.c \ trace-log.h \ $(BUILT_SOURCES) \ $(NULL) libdee_1_0_la_LIBADD = \ $(DEE_LIBS) libdee_1_0_la_LDFLAGS = \ $(DEE_LT_LDFLAGS) \ $(COVERAGE_LDFLAGS) libdee_1_0_la_CPPFLAGS = \ -I$(srcdir) \ -I$(top_srcdir) \ -I$(top_builddir) \ -DG_LOG_DOMAIN=\"dee\" \ -DPREFIX=\""$(prefix)"\" \ -DLIBDIR=\""$(libdir)"\" \ -DG_DISABLE_DEPRECATED \ -DDEE_COMPILATION \ $(GCC_FLAGS) \ $(DEE_CFLAGS) \ $(MAINTAINER_CFLAGS) \ $(COVERAGE_CFLAGS) if HAVE_ICU devel_headers += dee-icu.h libdee_1_0_la_SOURCES += dee-icu-term-filter.c libdee_1_0_la_LIBADD += $(ICU_LIBS) endif ## # If trace logging is not enabled # all the macros are removed by the preprocessor ## if ENABLE_TRACE_LOG libdee_1_0_la_SOURCES += trace-log.c endif ## # Signal Marshallers ## dee-marshal.h: $(srcdir)/dee-marshal.list $(AM_V_GEN)$(GLIB_GENMARSHAL) --header \ --prefix=_dee_marshal $(srcdir)/dee-marshal.list \ > dee-marshal.h dee-marshal.c: $(srcdir)/dee-marshal.list $(AM_V_GEN)$(GLIB_GENMARSHAL) --body \ --prefix=_dee_marshal $(srcdir)/dee-marshal.list \ > dee-marshal.c BUILT_SOURCES += \ dee-marshal.c \ dee-marshal.h EXTRA_DIST += dee-marshal.list CLEANFILES += \ $(BUILT_SOURCES) ## # GObject Introspection ## if HAVE_INTROSPECTION irscanner_sources = $(libdee_1_0_la_SOURCES) dee_gir = Dee-$(GIR_VERSION).gir Dee-1.0.gir: $(lib_LTLIBRARIES) Makefile Dee_1_0_gir_SCANNERFLAGS = --c-include="dee.h" Dee_1_0_gir_INCLUDES = GObject-2.0 GLib-2.0 Gio-2.0 Dee_1_0_gir_LIBS = $(lib_LTLIBRARIES) Dee_1_0_gir_FILES = $(irscanner_sources) Dee_1_0_gir_CFLAGS = \ -I$(srcdir) \ -I$(top_srcdir) \ -I$(top_builddir) \ -DDEE_COMPILATION \ $(NULL) Dee_1_0_gir_EXPORT_PACKAGES = dee-1.0 INTROSPECTION_GIRS += $(dee_gir) # INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to # install anything - we need to install inside our prefix. girdir = $(datadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(gir_DATA) $(typelibs_DATA) endif dee-1.2.7+15.04.20150304/src/dee-icu.h0000644000015300001610000000427712475676210017104 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #ifndef _HAVE_DEE_ICU_H #define _HAVE_DEE_ICU_H #include #include G_BEGIN_DECLS /** * DEE_ICU_ERROR: * * Error domain for the ICU extension to Dee. Error codes will be from the * #DeeICUError enumeration */ #define DEE_ICU_ERROR dee_icu_error_quark() /** * DeeICUError: * * Error codes for the ICU extension to Dee. These codes will be set when the * error domain is #DEE_ICU_ERROR. * * @DEE_ICU_ERROR_BAD_RULE: Error parsing a transliteration rule * @DEE_ICU_ERROR_BAD_ID: Error parsing a transliterator system id * @DEE_ICU_ERROR_UNKNOWN: The ICU subsystem returned an error that is not * handled in Dee */ typedef enum { DEE_ICU_ERROR_BAD_RULE, DEE_ICU_ERROR_BAD_ID, DEE_ICU_ERROR_UNKNOWN } DeeICUError; typedef struct _DeeICUTermFilter DeeICUTermFilter; DeeICUTermFilter* dee_icu_term_filter_new (const gchar* system_id, const gchar *rules, GError **error); DeeICUTermFilter* dee_icu_term_filter_new_ascii_folder (); gchar* dee_icu_term_filter_apply (DeeICUTermFilter *self, const gchar *text); void dee_icu_term_filter_destroy (DeeICUTermFilter *filter); GQuark dee_icu_error_quark (void); G_END_DECLS #endif /* _HAVE_DEE_ICU_H */ dee-1.2.7+15.04.20150304/src/dee-serializable-model.h0000644000015300001610000000652112475676210022062 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_SERIALIZABLE_MODEL_H #define _HAVE_DEE_SERIALIZABLE_MODEL_H #include #include #include G_BEGIN_DECLS #define DEE_TYPE_SERIALIZABLE_MODEL (dee_serializable_model_get_type ()) #define DEE_SERIALIZABLE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_SERIALIZABLE_MODEL, DeeSerializableModel)) #define DEE_SERIALIZABLE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_SERIALIZABLE_MODEL, DeeSerializableModelClass)) #define DEE_IS_SERIALIZABLE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_SERIALIZABLE_MODEL)) #define DEE_IS_SERIALIZABLE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_SERIALIZABLE_MODEL)) #define DEE_SERIALIZABLE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_ABSTRACT_MODEL, DeeSerializableModelClass)) typedef struct _DeeSerializableModel DeeSerializableModel; typedef struct _DeeSerializableModelClass DeeSerializableModelClass; typedef struct _DeeSerializableModelPrivate DeeSerializableModelPrivate; /** * DeeSerializableModel: * * All fields in the DeeSerializableModel structure are private and should never be * accessed directly */ struct _DeeSerializableModel { /*< private >*/ GObject parent; DeeSerializableModelPrivate *priv; }; struct _DeeSerializableModelClass { /*< private >*/ GObjectClass parent_class; /*< vatable >*/ guint64 (* get_seqnum) (DeeModel *self); void (* set_seqnum) (DeeModel *self, guint64 seqnum); guint64 (* inc_seqnum) (DeeModel *self); /*< private >*/ void (*_dee_serializable_model_1) (void); void (*_dee_serializable_model_2) (void); void (*_dee_serializable_model_3) (void); void (*_dee_serializable_model_4) (void); }; /** * dee_serializable_model_get_type: * * The GType of #DeeSerializableModel * * Return value: the #GType of #DeeSerializableModel **/ GType dee_serializable_model_get_type (void); guint64 dee_serializable_model_get_seqnum (DeeModel *self); void dee_serializable_model_set_seqnum (DeeModel *self, guint64 seqnum); guint64 dee_serializable_model_inc_seqnum (DeeModel *self); G_END_DECLS #endif /* _HAVE_DEE_SERIALIZABLE_MODEL_H */ dee-1.2.7+15.04.20150304/src/dee-result-set.h0000644000015300001610000000611212475676210020421 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _DEE_RESULT_SET_H_ #define _DEE_RESULT_SET_H_ #include #include #include G_BEGIN_DECLS #define DEE_TYPE_RESULT_SET (dee_result_set_get_type ()) #define DEE_RESULT_SET(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_RESULT_SET, DeeResultSet)) #define DEE_IS_RESULT_SET(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_RESULT_SET)) #define DEE_RESULT_SET_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE(obj, dee_result_set_get_type (), DeeResultSetIface)) typedef struct _DeeResultSetIface DeeResultSetIface; typedef struct _DeeResultSet DeeResultSet; struct _DeeResultSetIface { GTypeInterface g_iface; /*< public >*/ guint (*get_n_rows) (DeeResultSet *self); DeeModelIter* (*next) (DeeResultSet *self); gboolean (*has_next) (DeeResultSet *self); DeeModelIter* (*peek) (DeeResultSet *self); void (*seek) (DeeResultSet *self, guint pos); guint (*tell) (DeeResultSet *self); DeeModel* (*get_model) (DeeResultSet *self); /*< private >*/ void (*_dee_result_set_1) (void); void (*_dee_result_set_2) (void); void (*_dee_result_set_3) (void); void (*_dee_result_set_4) (void); void (*_dee_result_set_5) (void); }; GType dee_result_set_get_type (void); guint dee_result_set_get_n_rows (DeeResultSet *self); DeeModelIter* dee_result_set_next (DeeResultSet *self); gboolean dee_result_set_has_next (DeeResultSet *self); DeeModelIter* dee_result_set_peek (DeeResultSet *self); void dee_result_set_seek (DeeResultSet *self, guint pos); guint dee_result_set_tell (DeeResultSet *self); DeeModel* dee_result_set_get_model (DeeResultSet *self); #define _vala_dee_result_set_next_value(rs) (dee_result_set_has_next(rs) ? dee_result_set_next(rs) : NULL) #define _vala_dee_result_set_iterator(rs) ((DeeResultSet*)g_object_ref(rs)) G_END_DECLS #endif /* _HAVE_DEE_RESULT_SET_H */ dee-1.2.7+15.04.20150304/src/dee-serializable.h0000644000015300001610000000633712475676210020771 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _DEE_SERIALIZABLE_H_ #define _DEE_SERIALIZABLE_H_ #include #include #include G_BEGIN_DECLS #define DEE_TYPE_SERIALIZABLE (dee_serializable_get_type ()) #define DEE_SERIALIZABLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_SERIALIZABLE, DeeSerializable)) #define DEE_IS_SERIALIZABLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_SERIALIZABLE)) #define DEE_SERIALIZABLE_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE(obj, dee_serializable_get_type (), DeeSerializableIface)) typedef struct _DeeSerializableIface DeeSerializableIface; typedef struct _DeeSerializable DeeSerializable; /** * DeeSerializableParseFunc: * @data: A #GVariant with type signature as passed to * dee_serializable_register_parser() when the parser was registered. * The variant is not referenced. * * Return value: (transfer full): A newly constructed #GObject of the #GType * used when registering the parser. Note that since * the environment guarantees that the input data is valid * according to the registration information this function * can not fail. Thus %NULL is not a valid return value. */ typedef GObject* (*DeeSerializableParseFunc) (GVariant *data); struct _DeeSerializableIface { GTypeInterface g_iface; /*< public >*/ GVariant* (*serialize) (DeeSerializable *self); /*< private >*/ void (*_dee_serializable_1) (void); void (*_dee_serializable_2) (void); void (*_dee_serializable_3) (void); void (*_dee_serializable_4) (void); void (*_dee_serializable_5) (void); }; GType dee_serializable_get_type (void); void dee_serializable_register_parser (GType type, const GVariantType *vtype, DeeSerializableParseFunc parse_func); GObject* dee_serializable_parse (GVariant *data, GType type); GObject* dee_serializable_parse_external (GVariant *data); GVariant* dee_serializable_externalize (DeeSerializable *self); GVariant* dee_serializable_serialize (DeeSerializable *self); G_END_DECLS #endif /* _HAVE_DEE_SERIALIZABLE_H */ dee-1.2.7+15.04.20150304/src/dee-serializable.c0000644000015300001610000003446212475676210020764 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-serializable * @short_description: Interface for classes that can serialize to and from #GVariants * @include: dee.h * * Interface for classes that can serialize to and from #GVariants. * * There are two serialization concepts supported by this API: * serialization and externalization. * A serialized instance is created with dee_serializable_serialize() and can * be read back with dee_serializable_parse() provided you know the correct * #GType for the serialized data. The #GVariant representation of your * serialized data is guaranteed to be exactly as you implement yourself in the * @serialize vfunc of the #DeeSerializableIface. * * With externalized instances you don't have to know the correct GType to * recreate the instance. The #GType is encoded in the data itself. When you're * using dee_serializable_externalize() your data will be wrapped in a container * format with the required object metadata to read it back. For this reason * dee_serializable_parse_external() doesn't require you to pass in the #GType * you want to deserialize. * * * On Subclasses of DeeSerializable Types * * As a rule of thumb you need to re-implement the #DeeSerializable interface * and install parse functions with dee_serializable_register_parser() every * time you create a new class derived from a #DeeSerializable superclass. * * * In case a subclass does not provide it's own serialization interface * Dee will recurse upwards in the type hierarchy and use the serialization and * parser implementations of the first superclass with the required behaviour. * This means that the parsed instance will not be an * instance of the subclass but only of the serializable superclass. * Caveat emptor. * * */ #ifdef HAVE_CONFIG_H #include #endif #include "dee-serializable.h" #include "dee-serializable-model.h" #include "dee-sequence-model.h" #include "dee-shared-model.h" #include "trace-log.h" #define DEE_SERIALIZABLE_FORMAT_VERSION 1 typedef struct { GType type; GVariantType *vtype; DeeSerializableParseFunc parse; } Parser; GHashTable *parsers_by_gtype = NULL; typedef DeeSerializableIface DeeSerializableInterface; G_DEFINE_INTERFACE (DeeSerializable, dee_serializable, G_TYPE_OBJECT) static void dee_serializable_default_init (DeeSerializableInterface *klass) { } static void init_parsers () { gpointer *cls; parsers_by_gtype = g_hash_table_new (g_str_hash, g_str_equal); /* Call type initializers for built in DeeSerializables, * we need to ref the classes as the parsers are registered in the * class_init() functions */ cls = g_type_class_ref (dee_serializable_model_get_type ()); g_type_class_unref (cls); cls = g_type_class_ref (dee_sequence_model_get_type ()); g_type_class_unref (cls); cls = g_type_class_ref (dee_shared_model_get_type ()); g_type_class_unref (cls); } /** * dee_serializable_register_parser: * @type: The #GType of the object class to register a parser for * @vtype: Variants to be converted must have this signature * @parse_func: A function to convert #GVariant data into an instance of the * given @type. * * Register a parser that can convert #GVariant data into an instance of * a given #GType. Note that you can register more than one parser for the * same #GType provided that you give them different variant type signatures. * * If there is already a parser registered for the given @type and @vtype * it will be silently replaced. * * The recommended behaviour is that #GObject classes register their parsers in * their respective class init functions. */ void dee_serializable_register_parser (GType type, const GVariantType *vtype, DeeSerializableParseFunc parse_func) { GSList *parsers, *iter; Parser *parser; const gchar *gtype_name; g_return_if_fail (G_TYPE_IS_OBJECT (type)); g_return_if_fail (vtype != NULL); g_return_if_fail (parse_func != NULL); if (G_UNLIKELY (parsers_by_gtype == NULL)) { init_parsers (); } gtype_name = g_type_name (type); parsers = g_hash_table_lookup (parsers_by_gtype, gtype_name); trace ("Registering DeeSerializable parser for type %s with signature %s", gtype_name, g_variant_type_peek_string (vtype)); /* Update existing parser if we have one */ for (iter = parsers; iter != NULL; iter = iter->next) { parser = (Parser *) iter->data; if (g_variant_type_equal (parser->vtype, vtype)) { /* Parser for this type-vtype combo already registered. * Override the current parser as documented and return */ parser->parse = parse_func; return; } } /* Invariant: Beyond this point we don't have the parser registered */ parser = g_new0 (Parser, 1); parser->type = type; parser->vtype = g_variant_type_copy (vtype); parser->parse = parse_func; parsers = g_slist_prepend (parsers, parser); g_hash_table_insert (parsers_by_gtype, g_strdup (gtype_name), parsers); return; } static GObject* _parse_type (GVariant *data, GType type, gboolean *found_parser) { GObject *object = NULL; GSList *parsers, *iter; Parser *parser; const GVariantType *vtype; const gchar *gtype_name = NULL; g_return_val_if_fail (data != NULL, NULL); vtype = g_variant_get_type (data); gtype_name = g_type_name (type); if (found_parser) *found_parser = FALSE; trace ("Looking up parser for DeeSerializable of type %s with signature %s", gtype_name, g_variant_type_peek_string (vtype)); /* Find the right parser and apply it */ parsers = g_hash_table_lookup (parsers_by_gtype, gtype_name); for (iter = parsers; iter != NULL; iter = iter->next) { parser = (Parser *) iter->data; if (g_variant_type_equal (parser->vtype, vtype)) { if (found_parser) *found_parser = TRUE; object = parser->parse (data); if (G_UNLIKELY (object == NULL)) { g_critical ("Parser for GType %s signature %s returned NULL. This is not allowed by the contract for DeeSerializableParseFunc.", gtype_name, g_variant_type_peek_string (vtype)); } else if (G_UNLIKELY (!g_type_is_a (G_OBJECT_TYPE (object), parser->type))) { g_critical ("Parser for GType %s signature %s returned instance of type %s which is not a subtype of %s", gtype_name, g_variant_type_peek_string (vtype), G_OBJECT_TYPE_NAME (object), gtype_name); g_object_unref (object); object = NULL; } break; } } if (object == NULL) trace ("No parser registered for GType %s with signature %s", gtype_name, g_variant_type_peek_string (vtype)); return object; } /** * dee_serializable_parse_external: * @data: The #GVariant data to parse * * Reconstruct a #DeeSerializable from #GVariant data. For this function * to work you need to register a parser with * dee_serializable_register_parser(). Any native Dee class will do so * automatically. * * This method only works on data created with dee_serializable_externalize() * and not with data from dee_serializable_serialize(). * * Since a #DeeSerializableParseFunc is not allowed to fail - by contract - * it can be guaranteed that this function only returns %NULL in case there * is no known parser for the #GType or #GVariant signature of @data. * * Return value: (transfer full): A newly constructed #GObject build from @data * or %NULL in case no parser has been registered for the given * #GType or variant signature. Free with g_object_unref(). */ GObject* dee_serializable_parse_external (GVariant *data) { GObject *object = NULL; guint32 *version; GVariant *headers, *payload, *payloadv; gchar *gtype_name = NULL; GType gtype_id; g_return_val_if_fail (data != NULL, NULL); g_return_val_if_fail (g_variant_type_equal (g_variant_get_type (data), G_VARIANT_TYPE ("(ua{sv}v)")), NULL); if (G_UNLIKELY (parsers_by_gtype == NULL)) { init_parsers (); } g_variant_ref_sink (data); /* Unpack the serialized data */ g_variant_get_child (data, 0, "u", &version); headers = g_variant_get_child_value (data, 1); payloadv = g_variant_get_child_value (data, 2); payload = g_variant_get_variant (payloadv); if (!g_variant_lookup (headers, "GType", "s", >ype_name)) { g_critical ("Unable to parse DeeSerializable data: 'GType' header not present in serialized data"); goto out; } gtype_id = g_type_from_name (gtype_name); if (gtype_id == 0) { g_critical ("No known GType for type name %s. Perhaps it is not " "registered with serialization subsystem yet?", gtype_name); goto out; } object = dee_serializable_parse (payload, gtype_id); out: g_variant_unref (data); g_variant_unref (headers); g_variant_unref (payloadv); g_variant_unref (payload); g_free (gtype_name); return object; } /** * dee_serializable_parse: * @data: The #GVariant data to parse. If this is a floating reference it will * be consumed * @type: The #GType of the class to instantiate from @data * * Reconstruct a #DeeSerializable from #GVariant data. For this function * to work you need to register a parser with * dee_serializable_register_parser(). Any native Dee class will do so * automatically. * * This method only works on data created with dee_serializable_serialize() * and not with data from dee_serializable_externalize(). * * Since a #DeeSerializableParseFunc is not allowed to fail - by contract - * it can be guaranteed that this function only returns %NULL in case there * is no known parser for @type and #GVariant signature of @data. * * Return value: (transfer full): A newly constructed #GObject build from @data * or %NULL in case no parser has been registered for the given * #GType or variant signature. Free with g_object_unref(). */ GObject* dee_serializable_parse (GVariant *data, GType type) { GObject *object = NULL; GType orig_type; gboolean parser_found = FALSE; gboolean parsed = FALSE; g_return_val_if_fail (data != NULL, NULL); g_return_val_if_fail (g_type_is_a (type, DEE_TYPE_SERIALIZABLE), NULL); if (G_UNLIKELY (parsers_by_gtype == NULL)) { init_parsers (); } orig_type = type; g_variant_ref_sink (data); while (g_type_is_a (type, DEE_TYPE_SERIALIZABLE)) { object = _parse_type (data, type, &parser_found); parsed |= parser_found; if (object != NULL) break; type = g_type_parent (type); } if (!parsed) g_critical ("No parser registered for GType %s with signature %s", g_type_name (orig_type), g_variant_get_type_string (data)); g_variant_unref (data); return object; } /** * dee_serializable_externalize: * @self: The instance to externalize * * Build an externalized form of @self which can be used together with * dee_serializable_parse_external() to rebuild a copy of @self. * * It is important to note that the variant returned from this method does * not have the same type signature as returned from a call to * dee_serializable_serialize(). Externalization will wrap the serialized data * in a container format with versioning information and headers with type * information. * * Return value: A floating reference to a #GVariant with the externalized data. */ GVariant* dee_serializable_externalize (DeeSerializable *self) { GVariant *payload; GVariantBuilder b; g_return_val_if_fail (DEE_IS_SERIALIZABLE (self), NULL); payload = dee_serializable_serialize (self); g_variant_builder_init (&b, G_VARIANT_TYPE ("(ua{sv}v)")); g_variant_builder_add (&b, "u", DEE_SERIALIZABLE_FORMAT_VERSION); g_variant_builder_open (&b, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&b, "{sv}", "GType", g_variant_new_string (G_OBJECT_TYPE_NAME (self))); g_variant_builder_close (&b); g_variant_builder_add_value (&b, g_variant_new_variant (payload)); g_variant_unref (payload); return g_variant_builder_end (&b); } /** * dee_serializable_serialize: * @self: The instance to serialize * * Build a clean serialized representation of @self. The signature of the * returned variant is entirely determined by the underlying implementation. * You can recreate a serialized instance by calling dee_serializable_parse() * provided that you know the correct #GType for the serialized instance. * * Return value: (transfer full): A reference to a #GVariant with * the serialized data. The variants type signature is entirely * dependent of the underlying implementation. Free using * g_variant_unref(). */ GVariant* dee_serializable_serialize (DeeSerializable *self) { DeeSerializableIface *iface; GVariant *result; g_return_val_if_fail (DEE_IS_SERIALIZABLE (self), NULL); iface = DEE_SERIALIZABLE_GET_IFACE (self); result = iface->serialize (self); /* Make sure we return a real reference * FIXME: just use g_variant_take_ref once we depend on glib 2.30 */ if (g_variant_is_floating (result)) return g_variant_ref_sink (result); return result; } dee-1.2.7+15.04.20150304/src/dee-tree-index.h0000644000015300001610000000465512475676210020370 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_TREE_INDEX_H #define _HAVE_DEE_TREE_INDEX_H #include #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_TREE_INDEX (dee_tree_index_get_type ()) #define DEE_TREE_INDEX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_TREE_INDEX, DeeTreeIndex)) #define DEE_TREE_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_TREE_INDEX, DeeTreeIndexClass)) #define DEE_IS_TREE_INDEX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_TREE_INDEX)) #define DEE_IS_TREE_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_TREE_INDEX)) #define DEE_TREE_INDEX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_TREE_INDEX, DeeTreeIndexClass)) typedef struct _DeeTreeIndexClass DeeTreeIndexClass; typedef struct _DeeTreeIndex DeeTreeIndex; typedef struct _DeeTreeIndexPrivate DeeTreeIndexPrivate; /** * DeeTreeIndex: * * All fields in the DeeTreeIndex structure are private and should never be * accessed directly */ struct _DeeTreeIndex { /*< private >*/ DeeIndex parent; DeeTreeIndexPrivate *priv; }; struct _DeeTreeIndexClass { DeeIndexClass parent_class; }; GType dee_tree_index_get_type (void); DeeTreeIndex* dee_tree_index_new (DeeModel *model, DeeAnalyzer *analyzer, DeeModelReader *reader); G_END_DECLS #endif /* _HAVE_DEE_TREE_INDEX_H */ dee-1.2.7+15.04.20150304/src/dee-model-reader.c0000644000015300001610000001352712475676210020655 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-model-reader * @title: Model Readers * @short_description: Extracting strings from #DeeModels * @include: dee.h * * The purpose of a #DeeModelReader is to extract string from a #DeeModel. * These strings are usually passed through a #DeeAnalyzer on into a #DeeIndex. * * Most readers will extract a value of a given type from a given column, * but it must be noted that this is not a requirement. The strings may be * built from several columns. */ #ifdef HAVE_CONFIG_H #include #endif #include // memset() #include "dee-model.h" #include "dee-model-reader.h" /** * dee_model_reader_read: * @self: The #DeeModelReader used to read @model * @model: The #DeeModel to read a string from * @iter: The row to read a string from * * Read data from a row in a #DeeModel and extract a string representation from * it. * * Note that generally a #DeeModelReader need not be confined to reading from * one specific column, although in practice most are. * * Returns: A newly allocated string. Free with g_free(). */ gchar* dee_model_reader_read (DeeModelReader *self, DeeModel *model, DeeModelIter *iter) { g_return_val_if_fail (self != NULL, NULL); return self->reader_func (model, iter, self->userdata); } /** * dee_model_reader_destroy: * @reader: The reader to destroy * * Release resources associated with @reader, but does not free the * #DeeModelReader structure itself. * * This will call the destroy() function registered with the reader * if it is set. */ void dee_model_reader_destroy (DeeModelReader *reader) { g_return_if_fail (reader != NULL); if (reader->destroy) reader->destroy (reader->userdata); } /** * dee_model_reader_new: * @reader_func: (scope notified): The #DeeModelReaderFunc to use for the reader * @userdata: (closure) (allow-none): The user data to pass to @reader_func * @destroy: (allow-none): The #GDestroyNotify to call on * @userdata when disposing of the reader * @out_reader: (out): A pointer to an uninitialized #DeeModelReader struct * * Create a new #DeeModelReader with the given parameters. This call will zero * the @out_reader struct. * */ void dee_model_reader_new (DeeModelReaderFunc reader_func, gpointer userdata, GDestroyNotify destroy, DeeModelReader *out_reader) { g_return_if_fail (reader_func != NULL); g_return_if_fail (out_reader != NULL); memset (out_reader, 0, sizeof (DeeModelReader)); out_reader->reader_func = reader_func; out_reader->userdata = userdata; out_reader->destroy = destroy; } static gchar* _string_reader_func (DeeModel *model, DeeModelIter *iter, gpointer userdata) { return g_strdup ( dee_model_get_string (model, iter, GPOINTER_TO_UINT (userdata))); } /** * dee_model_reader_new_for_string_column: * @column: The column index to read a string from * @out_reader: (out): A pointer to a #DeeModelReader instance which will have * all fields initialized appropriately * * A #DeeModelReader reading a string from a #DeeModel at a given column */ void dee_model_reader_new_for_string_column (guint column, DeeModelReader *out_reader) { dee_model_reader_new (_string_reader_func, GUINT_TO_POINTER (column), NULL, out_reader); } static gchar* _int32_reader_func (DeeModel *model, DeeModelIter *iter, gpointer userdata) { return g_strdup_printf ( "%i", dee_model_get_int32 (model, iter, GPOINTER_TO_UINT (userdata))); } /** * dee_model_reader_new_for_int32_column: * @column: The column index to read a %gint32 from * @out_reader: (out): A pointer to a #DeeModelReader instance which will have * all fields initialized appropriately * * A #DeeModelReader reading a %gint32 from a #DeeModel at a given column */ void dee_model_reader_new_for_int32_column (guint column, DeeModelReader *out_reader) { dee_model_reader_new (_int32_reader_func, GUINT_TO_POINTER (column), NULL, out_reader); } static gchar* _uint32_reader_func (DeeModel *model, DeeModelIter *iter, gpointer userdata) { return g_strdup_printf ("%"G_GUINT32_FORMAT, dee_model_get_uint32 (model, iter, GPOINTER_TO_UINT (userdata))); } /** * dee_model_reader_new_for_uint32_column: * @column: The column index to read a %guint32 from * @out_reader: (out): A pointer to a #DeeModelReader instance which will have * all fields initialized appropriately * * A #DeeModelReader reading a %guint32 from a #DeeModel at a given column */ void dee_model_reader_new_for_uint32_column (guint column, DeeModelReader *out_reader) { dee_model_reader_new (_uint32_reader_func, GUINT_TO_POINTER (column), NULL, out_reader); } dee-1.2.7+15.04.20150304/src/dee-file-resource-manager.h0000644000015300001610000000471112475676210022471 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #ifndef _DEE_FILE_RESOURCE_MANAGER_H_ #define _DEE_FILE_RESOURCE_MANAGER_H_ #include #include #include G_BEGIN_DECLS #define DEE_TYPE_FILE_RESOURCE_MANAGER (dee_file_resource_manager_get_type ()) #define DEE_FILE_RESOURCE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_FILE_RESOURCE_MANAGER, DeeFileResourceManager)) #define DEE_FILE_RESOURCE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_FILE_RESOURCE_MANAGER, DeeFileResourceManagerClass)) #define DEE_IS_FILE_RESOURCE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_FILE_RESOURCE_MANAGER)) #define DEE_IS_FILE_RESOURCE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_FILE_RESOURCE_MANAGER)) #define DEE_FILE_RESOURCE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DEE_TYPE_FILE_RESOURCE_MANAGER, DeeFileResourceManagerClass)) typedef struct _DeeFileResourceManager DeeFileResourceManager; typedef struct _DeeFileResourceManagerClass DeeFileResourceManagerClass; struct _DeeFileResourceManager { GObject parent_instance; }; struct _DeeFileResourceManagerClass { GObjectClass parent_class; }; GType dee_file_resource_manager_get_type (void); DeeResourceManager* dee_file_resource_manager_new (const gchar *primary_path); void dee_file_resource_manager_add_search_path (DeeResourceManager *self, const gchar *path); const gchar* dee_file_resource_manager_get_primary_path (DeeResourceManager *self); G_END_DECLS #endif /* _DEE_FILE_RESOURCE_MANAGER_H_ */ dee-1.2.7+15.04.20150304/src/dee-server.h0000644000015300001610000000452412475676210017625 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_SERVER_H #define _HAVE_DEE_SERVER_H #include #include #include "dee-peer.h" G_BEGIN_DECLS #define DEE_TYPE_SERVER dee_server_get_type() #define DEE_SERVER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_SERVER, DeeServer)) #define DEE_SERVER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), DEE_TYPE_SERVER, DeeServerClass)) #define DEE_IS_SERVER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_SERVER)) #define DEE_IS_SERVER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), DEE_TYPE_SERVER)) #define DEE_SERVER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), DEE_TYPE_SERVER, DeeServerClass)) typedef struct _DeeServerPrivate DeeServerPrivate; typedef struct { /*< private >*/ DeePeer parent; DeeServerPrivate *priv; } DeeServer; typedef struct { /*< private >*/ DeePeerClass parent_class; } DeeServerClass; /** * dee_server_get_type: * * The GType of #DeeServer * * Return value: the #GType of #DeeServer **/ GType dee_server_get_type (void); DeeServer* dee_server_new (const gchar *swarm_name); DeeServer* dee_server_new_for_address (const gchar *swarm_name, const gchar *bus_address); const gchar* dee_server_get_client_address (DeeServer *server); gchar* dee_server_bus_address_for_name (const gchar *name, gboolean include_username); G_END_DECLS #endif /* _HAVE_DEE_SERVER_H */ dee-1.2.7+15.04.20150304/src/dee-transaction.c0000644000015300001610000014272212475676214020646 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-transaction * @short_description: A self contained change set for a #DeeModel * @include: dee.h * * #DeeTransaction is a self contained change set related to some particular * #DeeModel called the target model. * * The transaction instance itself implements the #DeeModel interface in a way * that overlays the target model. In database terms the target model has * isolation level READ_COMMITTED. Meaning that the target model is not modified * until you call dee_transaction_commit(). * * To flush the changes to the target model call dee_transaction_commit(). * After committing the transaction will become invalid and must be freed with * g_object_unref(). It is a programming error to try and access a transaction * that has been committed with the sole exception of calling * dee_transaction_is_committed(). */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "dee-model.h" #include "dee-transaction.h" #include "dee-serializable-model.h" #include "dee-marshal.h" #include "trace-log.h" /* * IMPLEMENTATION NOTES * * The idea is to keep the transaction as light as possible by reusing the * iters from the target model, letting them fall through to the target when * unchanged in the transaction. * * This gives some overhead when reading stuff from a transaction model, but * that is considered an acceptable tradeoff as the transaction is optimized * for writing and not reading. * * The transaction keeps a "journal" of the changes it needs to apply and * commit. The relevant data structure is the JournalIter. Journal iters * are characterized in 2 ways. JournalIters are called 'jiters' for short. * * Firstly there is the ChangeType of a jiter. * * CHANGE_TYPE_REMOVE: The jiter is an 'override' for an iter in the * target model that has been deleted. The jiter.overlay * member points to the original * * CHANGE_TYPE_CHANGE: The jiter is an 'override' for an iter in the * target model that has been changed. The jiter.overlay * points to the original. The jiter.row_data member will * contain all the values for the changed row * * CHANGE_TYPE_ADD: The jiter does not correspond to an iter in the target, and * the jiter.override member will be unset. Additions are * grouped together in "segments" and these segments points * to an iter in the target they attach *before* * * To ease internal book keeping we also have the IterType enumeration which * describes if a given pointer is a jiter or an iter. * * Segments * Jiters that are additions are grouped into "segments" and each segment * points to a row in the target model. * */ typedef struct _JournalSegment JournalSegment; typedef struct _JournalIter JournalIter; typedef enum { CHANGE_TYPE_REMOVE, CHANGE_TYPE_CHANGE, CHANGE_TYPE_ADD } ChangeType; typedef enum { ITER_TYPE_TARGET, ITER_TYPE_JOURNAL } IterType; struct _JournalSegment { /* End points of the journal iters in the segment */ JournalIter *first_iter; JournalIter *last_iter; /* The row in the target model that this segment is attached before. * Note that the target row may be changed or deleted */ DeeModelIter *target_iter; /* The transaction owning the segment. It is mainly here as an optimization * to allow fast access to the number of column sin the model for copying * and freeing the row_data */ DeeTransaction *txn; /* Used on commit() because we commit whole segments at a time, * disregarding playback order */ gboolean is_committed; }; /* Implements a two dimensional doubly linked list. One dimension is the * playback queue and the other is the order of the iters (inside a segment). * INVARIANT: Set if and only if change_type == CHANGE_TYPE_ADD */ struct _JournalIter { /* Added rows all belong to a specific segment * attached before a row in the target */ JournalSegment *segment; /* Linked list for the playback queue */ JournalIter *next_playback; JournalIter *prev_playback; /* Linked list for the itersinside a segment */ JournalIter *next_iter; JournalIter *prev_iter; /* Points to a row in the targetmodel * INVARIANT: Set if and only if change_type == CHANGE_TYPE_{CHANGE,REMOVE} */ DeeModelIter *override_iter; /* FIXME: Not implemented. I am not even sure it's theoretically possible */ GSList *tags; ChangeType change_type; GVariant **row_data; }; /** * DeeTransactionPrivate: * * Ignore this structure. */ struct _DeeTransactionPrivate { /* The model the transaction applies against */ DeeModel *target; /* The journal maps DeeModelIters from the target model to JournalIters. * Also maps JournalIters to them selves. * INAVARIANT: If an iter is not in the journal it * it is an untouched iter from the target model * NOTE: Different keys may point to the same jiter */ GHashTable *journal; /* The segments map DeeModelIters from the target model, * to JournalSegments that lie immediately before them. * INVARIANT: A JournalIter that has a segment has change_type CHANGE_TYPE_ADD * NOTE: different keys may correspond to the same segment */ GHashTable *segments; /* The head and the tail of the queue of JournalIters constituting * the changes we must play back on the target model. * NOTE: jiters that become irrelevant must be unlinked from the * playback queue and freed. We use the playback queue on finalize * to walk and free the jiters and segments */ JournalIter *first_playback; JournalIter *last_playback; /* Signals handlers to check for the concurrent modification of the back end */ gulong target_row_added_handler; gulong target_row_removed_handler; gulong target_row_changed_handler; /* canary to saniy check that no one sneaks changes into the target while * the txn is open */ guint64 begin_seqnum; /* DeeTransactionError code. If != 0 we have an error state set */ guint error_code; /* Number of columns in target model (just a cache) */ guint n_cols; }; static JournalIter* journal_iter_new (ChangeType ct) { JournalIter *jent; jent = g_slice_new0 (JournalIter); jent->change_type = ct; return jent; } static void journal_iter_free (JournalIter *jiter) { GVariant **v; if (jiter->row_data) { for (v = jiter->row_data; *v != NULL; v++) { g_variant_unref (*v); *v = NULL; } g_free (jiter->row_data); jiter->row_data = NULL; } // FIXME: free tags, when/if we implement tags g_slice_free (JournalIter, jiter); } #define journal_iter_is_removed(jiter) (jiter->change_type == CHANGE_TYPE_REMOVE) static JournalSegment* journal_segment_new_before (DeeModelIter *iter, DeeTransaction *txn) { JournalSegment *jseg = g_slice_new0 (JournalSegment); jseg->target_iter = iter; jseg->txn = txn; jseg->is_committed = FALSE; return jseg; } static void journal_segment_free (JournalSegment *segment) { g_slice_free (JournalSegment, segment); } static GVariant** copy_row_data (GVariant **row_data, guint n_cols) { GVariant **iter, **copy; guint i; for (iter = row_data, i = 0; i < n_cols; iter++, i++) { g_variant_ref_sink (*iter); } copy = g_new (GVariant*, n_cols + 1); memcpy (copy, row_data, n_cols * sizeof (GVariant*)); copy[n_cols] = NULL; return copy; } static JournalIter* journal_segment_append (JournalSegment *jseg, GVariant **row_data) { JournalIter *new_jiter; g_assert ((jseg->last_iter == NULL && jseg->first_iter == NULL) || jseg->last_iter->next_iter == NULL); new_jiter = journal_iter_new (CHANGE_TYPE_ADD); new_jiter->segment = jseg; new_jiter->row_data = copy_row_data (row_data, jseg->txn->priv->n_cols); if (jseg->last_iter == NULL) { jseg->last_iter = new_jiter; jseg->first_iter = new_jiter; } else { new_jiter->prev_iter = jseg->last_iter; jseg->last_iter->next_iter = new_jiter; jseg->last_iter = new_jiter; } return new_jiter; } static JournalIter* journal_segment_prepend (JournalSegment *jseg, GVariant **row_data) { JournalIter *new_jiter; g_assert ((jseg->last_iter == NULL && jseg->first_iter == NULL) || jseg->first_iter->prev_iter == NULL); new_jiter = journal_iter_new (CHANGE_TYPE_ADD); new_jiter->segment = jseg; new_jiter->row_data = copy_row_data (row_data, jseg->txn->priv->n_cols); if (jseg->first_iter == NULL) { jseg->last_iter = new_jiter; jseg->first_iter = new_jiter; } else { new_jiter->next_iter = jseg->first_iter; jseg->first_iter->prev_iter = new_jiter; jseg->first_iter = new_jiter; } return new_jiter; } static JournalIter* journal_segment_insert_before (JournalSegment *jseg, JournalIter *jiter, GVariant **row_data) { JournalIter *new_jiter; g_assert ((jseg->first_iter == NULL && jseg->last_iter == NULL) || (jseg->first_iter != NULL && jseg->last_iter != NULL)); if (jiter == NULL) { return journal_segment_append (jseg, row_data); } else if (jiter == jseg->first_iter) { return journal_segment_prepend (jseg, row_data); } /* It's not a pre- or append(), but a genuine insertion */ new_jiter = journal_iter_new (CHANGE_TYPE_ADD); new_jiter->segment = jseg; new_jiter->row_data = copy_row_data (row_data, jseg->txn->priv->n_cols); if (jseg->first_iter == NULL) { jseg->first_iter = new_jiter; jseg->last_iter = new_jiter; } else { jiter->prev_iter->next_iter = new_jiter; new_jiter->prev_iter = jiter->prev_iter; new_jiter->next_iter = jiter; jiter->prev_iter = new_jiter; } return new_jiter; } #define AS_TXN(ptr) ((DeeTransaction*)ptr) #define get_journal_segment_before(iter) \ g_hash_table_lookup (priv->segments, iter) #define set_journal_segment_before(iter,jseg) \ g_hash_table_insert (priv->segments, iter, jseg) #define register_journal_iter(jiter) \ g_hash_table_insert (priv->journal, jiter, jiter); \ if (jiter->override_iter) g_hash_table_insert (priv->journal, jiter->override_iter, jiter); #define unregister_journal_iter(jiter) \ g_hash_table_remove (priv->journal, jiter); #define check_journal_iter(iter,jiter_p) \ g_hash_table_lookup_extended (priv->journal, iter, NULL, (void**)jiter_p) #define JOURNAL_ITER(iter) \ ((JournalIter*)iter) #define MODEL_ITER(jiter) \ ((DeeModelIter*)jiter) #define append_to_playback(jiter) \ if (priv->first_playback == NULL) \ priv->first_playback = jiter; \ \ if (priv->last_playback) \ { \ priv->last_playback->next_playback = jiter; \ jiter->prev_playback = priv->last_playback; \ } \ \ priv->last_playback = jiter; static void dee_transaction_model_iface_init (DeeModelIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeTransaction, dee_transaction, DEE_TYPE_SERIALIZABLE_MODEL, G_IMPLEMENT_INTERFACE (DEE_TYPE_MODEL, dee_transaction_model_iface_init)); #define DEE_TRANSACTION_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_TRANSACTION, DeeTransactionPrivate)) enum { PROP_0, PROP_TARGET }; /* INVARIANT: The end iter of the transaction is the same as the end iter * of the target model */ #define DEE_TRANSACTION_TARGET(txn) (DEE_TRANSACTION(txn)->priv->target) /* * DeeModel forward declarations */ static DeeModelIter* dee_transaction_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static void dee_transaction_remove (DeeModel *self, DeeModelIter *iter); static void dee_transaction_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value); static void dee_transaction_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static GVariant* dee_transaction_get_value (DeeModel *self, DeeModelIter *iter, guint column); static DeeModelIter* dee_transaction_get_first_iter (DeeModel *self); static DeeModelIter* dee_transaction_get_last_iter (DeeModel *self); static DeeModelIter* dee_transaction_next (DeeModel *self, DeeModelIter *iter); static DeeModelIter* dee_transaction_prev (DeeModel *self, DeeModelIter *iter); static DeeModelTag* dee_transaction_register_tag (DeeModel *self, GDestroyNotify tag_destroy); static gpointer dee_transaction_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag); static void dee_transaction_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value); static void dee_transaction_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value); static const gchar* dee_transaction_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column); static void on_target_modified (DeeTransaction *self, DeeModelIter *iter); /* GObject Init */ static void dee_transaction_finalize (GObject *object) { DeeTransactionPrivate *priv = DEE_TRANSACTION (object)->priv; if (priv->target) { g_signal_handler_disconnect (priv->target, priv->target_row_added_handler); g_signal_handler_disconnect (priv->target, priv->target_row_removed_handler); g_signal_handler_disconnect (priv->target, priv->target_row_changed_handler); g_object_unref (priv->target); } if (priv->journal) { g_hash_table_unref (priv->journal); priv->journal = NULL; } if (priv->segments) { g_hash_table_unref (priv->segments); priv->segments = NULL; } if (priv->first_playback) { JournalIter *jiter, *free_jiter; GHashTable *freed_segments = g_hash_table_new (g_direct_hash, g_direct_equal); for (jiter = priv->first_playback; jiter != NULL; ) { if (jiter->segment && g_hash_table_lookup (freed_segments, jiter->segment)) { g_hash_table_insert (freed_segments,jiter->segment, jiter->segment); journal_segment_free (jiter->segment); } free_jiter = jiter; jiter = jiter->next_playback; journal_iter_free (free_jiter); } priv->first_playback = NULL; priv->last_playback = NULL; g_hash_table_destroy (freed_segments); } G_OBJECT_CLASS (dee_transaction_parent_class)->finalize (object); } /* GObject Post-Init. Properties have been set */ static void dee_transaction_constructed (GObject *object) { const gchar* const *schema; const gchar **column_names; guint n_columns; DeeTransactionPrivate *priv = DEE_TRANSACTION (object)->priv; /* Chain up before we do too much, to make sure the parent class is ready */ if (G_OBJECT_CLASS (dee_transaction_parent_class)->constructed != NULL) G_OBJECT_CLASS (dee_transaction_parent_class)->constructed (object); if (priv->target == NULL) { g_critical ("You must set the 'target' property of " "the DeeTransaction upon creation."); return; } /* Adopt schema of target model */ schema = dee_model_get_schema (priv->target, &n_columns); dee_model_set_schema_full (DEE_MODEL (object), schema, n_columns); priv->n_cols = n_columns; /* Also adopt column names of target model */ column_names = dee_model_get_column_names (priv->target, &n_columns); if (column_names) { dee_model_set_column_names_full (DEE_MODEL (object), column_names, n_columns); } /* Adopt seqnums of target model, if it has any */ if (DEE_IS_SERIALIZABLE_MODEL (priv->target)) { priv->begin_seqnum = dee_serializable_model_get_seqnum (priv->target); } else { priv->begin_seqnum = 0; } dee_serializable_model_set_seqnum (DEE_MODEL (object), priv->begin_seqnum); priv->target_row_added_handler = g_signal_connect_swapped (priv->target, "row-added", G_CALLBACK (on_target_modified), object); priv->target_row_removed_handler = g_signal_connect_swapped (priv->target, "row-removed", G_CALLBACK (on_target_modified), object); priv->target_row_changed_handler = g_signal_connect_swapped (priv->target, "row-changed", G_CALLBACK (on_target_modified), object); } static void dee_transaction_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeeTransactionPrivate *priv = DEE_TRANSACTION (object)->priv; switch (id) { case PROP_TARGET: priv->target = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_transaction_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { case PROP_TARGET: g_value_set_object (value, DEE_TRANSACTION (object)->priv->target); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_transaction_class_init (DeeTransactionClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; obj_class->finalize = dee_transaction_finalize; obj_class->constructed = dee_transaction_constructed; obj_class->set_property = dee_transaction_set_property; obj_class->get_property = dee_transaction_get_property; /** * DeeTransaction:back-end: * * The backend model used by this proxy model. **/ pspec = g_param_spec_object ("target", "Target", "Target model", DEE_TYPE_MODEL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_TARGET, pspec); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeTransactionPrivate)); } static void dee_transaction_model_iface_init (DeeModelIface *iface) { DeeModelIface *piface = g_type_interface_peek_parent (iface); iface->set_schema_full = piface->set_schema_full; iface->get_schema = piface->get_schema; iface->get_field_schema = dee_transaction_get_field_schema; iface->get_column_schema = piface->get_column_schema; iface->get_n_columns = piface->get_n_columns; iface->get_n_rows = piface->get_n_rows; iface->clear = piface->clear; iface->prepend_row = piface->prepend_row; iface->append_row = piface->append_row; iface->insert_row = piface->insert_row; iface->insert_row_before = dee_transaction_insert_row_before; iface->remove = dee_transaction_remove; iface->set_value = dee_transaction_set_value; iface->set_row = dee_transaction_set_row; iface->get_value = dee_transaction_get_value; iface->get_first_iter = dee_transaction_get_first_iter; iface->get_last_iter = dee_transaction_get_last_iter; iface->get_iter_at_row = piface->get_iter_at_row; iface->get_bool = piface->get_bool; iface->get_uchar = piface->get_uchar; iface->get_int32 = piface->get_int32; iface->get_uint32 = piface->get_uint32; iface->get_int64 = piface->get_int64; iface->get_uint64 = piface->get_uint64; iface->get_double = piface->get_double; iface->get_string = piface->get_string; iface->next = dee_transaction_next; iface->prev = dee_transaction_prev; iface->is_first = piface->is_first; iface->is_last = piface->is_last; iface->get_position = piface->get_position; iface->register_tag = dee_transaction_register_tag; iface->get_tag = dee_transaction_get_tag; iface->set_tag = dee_transaction_set_tag; } static void dee_transaction_init (DeeTransaction *model) { DeeTransactionPrivate *priv; priv = model->priv = DEE_TRANSACTION_GET_PRIVATE (model); priv->target = NULL; priv->journal = g_hash_table_new (g_direct_hash, g_direct_equal); priv->segments = g_hash_table_new (g_direct_hash, g_direct_equal); priv->target_row_added_handler = 0; priv->target_row_removed_handler = 0; priv->target_row_changed_handler = 0; } /* * DeeModel Interface Implementation */ /* Inherited methods */ // dee_transaction_set_schema_full () // dee_transaction_get_schema () // dee_transaction_get_column_schema () // dee_transaction_get_n_columns () // dee_transaction_get_n_rows () (slow O(n)) // dee_transaction_clear () (slow O(n)) // dee_transaction_append_row () // dee_transaction_prepend_row () // dee_transaction_insert_row () static const gchar* dee_transaction_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column) { DeeModelIface *piface; const gchar *schema; schema = dee_model_get_field_schema (DEE_TRANSACTION_TARGET (self), field_name, out_column); if (schema) return schema; // maybe the names are registered just for the transaction instance? piface = g_type_interface_peek_parent (DEE_MODEL_GET_IFACE (self)); return (* piface->get_field_schema) (self, field_name, out_column); } static DeeModelIter* dee_transaction_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeTransactionPrivate *priv; JournalIter *new_jiter, *jiter; JournalSegment *jseg; g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (row_members != NULL, NULL); g_return_val_if_fail (!dee_transaction_is_committed (AS_TXN (self)), NULL); priv = DEE_TRANSACTION (self)->priv; /* new_jiter is what we'll eventually return */ new_jiter = NULL; if (check_journal_iter (iter, &jiter)) { /* If the jiter has a segment it must be an added row and we wire * into that segment */ if (jiter->segment) { g_assert (jiter->change_type == CHANGE_TYPE_ADD); new_jiter = journal_segment_insert_before (jiter->segment, jiter, row_members); } else { g_assert (jiter->change_type == CHANGE_TYPE_CHANGE || jiter->change_type == CHANGE_TYPE_REMOVE); /* Inserting relative to a removed row is a consumer error, but * note we still want the assertion above to verify our own * invariants */ if (G_UNLIKELY (jiter->change_type == CHANGE_TYPE_REMOVE)) { g_critical ("Inserting new row relative to previously removed row"); return iter; } /* Note that on commit time we might have removed the iter we attach * before. We handle that at that point by scanning forwards until we * find a valid iter */ if ((jseg = get_journal_segment_before (iter)) != NULL) { new_jiter = journal_segment_append (jseg, row_members); } else { jseg = journal_segment_new_before (iter, DEE_TRANSACTION (self)); new_jiter = journal_segment_append (jseg, row_members); set_journal_segment_before (iter, jseg); } } } else if ((jseg = get_journal_segment_before (iter)) != NULL) { /* This is an untouched row from the target model with a segment * attached before it */ new_jiter = journal_segment_append (jseg, row_members); } else { /* This is an untouched row from the target model without any new rows * attached before it. * Set up a new segment before it and attach to that */ jseg = journal_segment_new_before (iter, DEE_TRANSACTION (self)); new_jiter = journal_segment_append (jseg, row_members); set_journal_segment_before (iter, jseg); } /* We have b0rked up this function if new_jiter is not set by now */ g_assert (new_jiter != NULL); append_to_playback (new_jiter); register_journal_iter (new_jiter); dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-added", new_jiter); return MODEL_ITER (new_jiter); } static void dee_transaction_remove (DeeModel *self, DeeModelIter *iter) { DeeTransactionPrivate *priv; JournalIter *jiter; gboolean should_free_jiter; g_return_if_fail (DEE_IS_TRANSACTION (self)); g_return_if_fail (!dee_transaction_is_committed (AS_TXN (self))); priv = DEE_TRANSACTION (self)->priv; should_free_jiter = FALSE; if (check_journal_iter (iter, &jiter)) { if (G_UNLIKELY (jiter->change_type == CHANGE_TYPE_REMOVE)) { g_critical ("Row %p already removed from transaction", iter); return; } /* If jiter is something we've added we can just unlink it from the * playback queue and its segment and free it. If it's a change we * can simply mark it as a removal in stead. * Note that if a segment is attached to a removed row, we resolve * that at commit() time by scanning forwards to the next non-removed * row. */ if (jiter->change_type == CHANGE_TYPE_CHANGE) { jiter->change_type = CHANGE_TYPE_REMOVE; } else { g_assert (jiter->change_type == CHANGE_TYPE_ADD); should_free_jiter = TRUE; } } else { jiter = journal_iter_new (CHANGE_TYPE_REMOVE); jiter->override_iter = iter; register_journal_iter (jiter); append_to_playback (jiter); } /* Emit the removed signal while the iter is still valid, * but after we increased the seqnum */ dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-removed", jiter->override_iter ? jiter->override_iter : MODEL_ITER (jiter)); if (should_free_jiter) { /* Detach from segment */ if (jiter->segment->first_iter == jiter) jiter->segment->first_iter = jiter->next_iter; if (jiter->segment->last_iter == jiter) jiter->segment->last_iter = jiter->prev_iter; /* If our host segment is empty we must get rid of it, * else we unlink our selves from the internal list */ if (jiter->segment->first_iter == NULL) { g_assert (jiter->segment->last_iter == NULL); g_hash_table_remove (priv->segments, jiter->segment->target_iter); } else{ if (jiter->prev_iter) jiter->prev_iter->next_iter = jiter->next_iter; if (jiter->next_iter) jiter->next_iter->prev_iter = jiter->prev_iter; } /* Remove from playback queue */ if (jiter->prev_playback) jiter->prev_playback->next_playback = jiter->next_playback; if (jiter->next_playback) jiter->next_playback->prev_playback = jiter->prev_playback; unregister_journal_iter (jiter); } } static void dee_transaction_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeTransactionPrivate *priv; JournalIter *jiter; GVariant **v; g_return_if_fail (DEE_IS_TRANSACTION (self)); g_return_if_fail (!dee_transaction_is_committed (AS_TXN (self))); priv = DEE_TRANSACTION (self)->priv; if (check_journal_iter (iter, &jiter)) { if (G_UNLIKELY (jiter->change_type == CHANGE_TYPE_REMOVE)) { g_critical ("Trying to update row which have been removed from " "the transaction"); return; } g_assert (jiter->row_data != NULL); for (v = jiter->row_data; *v != NULL; v++) { g_variant_unref (*v); } g_free (jiter->row_data); jiter->row_data = copy_row_data (row_members, priv->n_cols); } else { /* A simple check to raise the probability of iter being a valid * iter in the target */ if (strcmp (g_variant_get_type_string (row_members[0]), g_variant_get_type_string (dee_model_get_value (priv->target, iter, 0))) != 0) { g_critical ("Error setting row in transaction %p. The iter is " "probably not in the target model", self); return; } jiter = journal_iter_new (CHANGE_TYPE_CHANGE); jiter->row_data = copy_row_data (row_members, priv->n_cols); jiter->override_iter = iter; register_journal_iter (jiter); append_to_playback (jiter); } g_assert (jiter != NULL); g_assert ( (jiter->override_iter != NULL && jiter->change_type == CHANGE_TYPE_CHANGE) || (jiter->override_iter == NULL && jiter->change_type == CHANGE_TYPE_ADD)); dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-changed", jiter->override_iter ? jiter->override_iter : MODEL_ITER (jiter)); } static void dee_transaction_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value) { DeeTransactionPrivate *priv; JournalIter *jiter; g_return_if_fail (DEE_IS_TRANSACTION (self)); g_return_if_fail (iter != NULL); g_return_if_fail (value != NULL); g_return_if_fail (!dee_transaction_is_committed (AS_TXN (self))); priv = DEE_TRANSACTION (self)->priv; g_return_if_fail (column < priv->n_cols); if (check_journal_iter (iter, &jiter)) { if (G_UNLIKELY (jiter->change_type == CHANGE_TYPE_REMOVE)) { g_critical ("Trying to change value of removed row"); return; } g_variant_unref (jiter->row_data[column]); jiter->row_data[column] = g_variant_ref_sink (value); } else { /* We haven't touched this row before, which guarantees that the iter * must point to a row in the target model */ jiter = journal_iter_new (CHANGE_TYPE_CHANGE); jiter->override_iter = iter; /* Assume row data */ jiter->row_data = dee_model_get_row (priv->target, iter, NULL); g_variant_unref (jiter->row_data[column]); jiter->row_data[column] = g_variant_ref_sink (value); register_journal_iter (jiter); append_to_playback (jiter); } g_assert (jiter != NULL); g_assert ( (jiter->override_iter != NULL && jiter->change_type == CHANGE_TYPE_CHANGE) || (jiter->override_iter == NULL && jiter->change_type == CHANGE_TYPE_ADD)); dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-changed", jiter->override_iter ? jiter->override_iter : MODEL_ITER (jiter)); } static GVariant* dee_transaction_get_value (DeeModel *self, DeeModelIter *iter, guint column) { DeeTransactionPrivate *priv; JournalIter *jiter; g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_return_val_if_fail (!dee_transaction_is_committed (AS_TXN (self)), NULL); priv = DEE_TRANSACTION (self)->priv; if(check_journal_iter (iter, &jiter)) { if (G_UNLIKELY (jiter->change_type == CHANGE_TYPE_REMOVE)) { g_critical ("Trying to get value from a row that has been removed " "from the transaction"); return NULL; } g_return_val_if_fail (column < priv->n_cols, NULL); return g_variant_ref (jiter->row_data[column]); } else { return dee_model_get_value (priv->target, iter, column); } } /* Inherited methods */ // dee_transaction_get_bool () // dee_transaction_get_uchar () // dee_transaction_get_int32 () // dee_transaction_get_uint32 () // dee_transaction_get_int64 () // dee_transaction_get_uint64 () // dee_transaction_get_double () // dee_transaction_get_string () /* This method returns the next logical iter, which may be a removal */ static DeeModelIter* dee_transaction_next_raw (DeeModel *self, DeeModelIter *iter, IterType *out_iter_type) { DeeTransactionPrivate *priv; JournalIter *jiter, *jiter_next; JournalSegment *jseg; DeeModelIter *end; g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); priv = DEE_TRANSACTION (self)->priv; end = dee_model_get_last_iter (self); /* end shared with target */ g_return_val_if_fail (iter != end, (*out_iter_type = ITER_TYPE_TARGET, end)); if (check_journal_iter(iter, &jiter)) { *out_iter_type = ITER_TYPE_JOURNAL; /* If jiter points to a next jiter we're somewhere inside a segment and * that's well and good. * * Otherwise we reached the end of the segment and we return * the iter the segment was attached before (which may have * an override jiter, mind you). Or we wheren't in a segment to * begin with */ if (jiter->next_iter) { return MODEL_ITER (jiter->next_iter); } else if (jiter->segment) { if (check_journal_iter (jiter->segment->target_iter, &jiter_next)) { return MODEL_ITER (jiter_next); } else { *out_iter_type = ITER_TYPE_TARGET; return jiter->segment->target_iter; } } else { /* This was an overlay jiter, either CHANGE or REMOVE. * We use the structure of the target model to step. * If there's a segment before the next iter in the target model, * step into the segment. Otherwise just step normally on the target * */ g_assert (jiter->override_iter != NULL); iter = dee_model_next (priv->target, jiter->override_iter); jseg = get_journal_segment_before (iter); if (jseg != NULL) { return MODEL_ITER (jseg->first_iter); } else { if (check_journal_iter (iter, &jiter_next)) { return MODEL_ITER (jiter_next); } else { *out_iter_type = ITER_TYPE_TARGET; return iter; } } } g_assert_not_reached (); } /* If we get here, then this was not a journal iter, * but an unmodifed row from the target model */ /* We share end iter with the target model, so it's an error to hit that */ if (iter == end) { g_critical ("Trying to step past end of transaction model"); return iter; } /* If there's a segment before the next iter in the target model, * step into the segment. Otherwise just step normally on the target */ iter = dee_model_next (priv->target, iter); jseg = get_journal_segment_before (iter); if (jseg != NULL) { *out_iter_type = ITER_TYPE_JOURNAL; return MODEL_ITER (jseg->first_iter); } else { *out_iter_type = ITER_TYPE_TARGET; return iter; } } static DeeModelIter* dee_transaction_get_first_iter (DeeModel *self) { DeeTransactionPrivate *priv; JournalSegment *jseg; JournalIter *jiter; DeeModelIter *iter; IterType itype; g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_return_val_if_fail (!dee_transaction_is_committed (AS_TXN (self)), NULL); priv = DEE_TRANSACTION (self)->priv; iter = dee_model_get_first_iter (priv->target); if ((jseg = get_journal_segment_before (iter)) != NULL) { g_assert (jseg->first_iter != NULL); itype = ITER_TYPE_JOURNAL; jiter = jseg->first_iter; iter = MODEL_ITER (jseg->first_iter); } else if (check_journal_iter (iter, &jiter)) { /* Since iter is from the target, then jiter must * be an overlay and not in a segment (ie. not an addition) */ g_assert (jiter->segment == NULL); g_assert (jiter->override_iter == iter); itype = ITER_TYPE_JOURNAL; iter = MODEL_ITER (jiter); } else { /* The first target iter is untouched */ itype = ITER_TYPE_TARGET; } /* Now scan forwards until we have something which is not deleted. * This is complicated by the fact that we may have attached segments * before iters that have been marked removed */ while (itype == ITER_TYPE_JOURNAL && journal_iter_is_removed (jiter)) { iter = dee_transaction_next_raw (self, iter, &itype); if (itype == ITER_TYPE_JOURNAL) { jiter = JOURNAL_ITER (iter); } if ((jseg = get_journal_segment_before (iter)) != NULL) { return MODEL_ITER (jseg->first_iter); } } /* Finally - for override iters (changes, this shouldn't be a removal), * we return the original iter from the target model */ if (itype == ITER_TYPE_JOURNAL && jiter->override_iter != NULL) { g_assert (!journal_iter_is_removed (jiter)); iter = jiter->override_iter; itype = ITER_TYPE_TARGET; } return iter; } static DeeModelIter* dee_transaction_get_last_iter (DeeModel *self) { g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_return_val_if_fail (!dee_transaction_is_committed (AS_TXN (self)), NULL); /* We share end iter with the target model */ return dee_model_get_last_iter (DEE_TRANSACTION_TARGET (self)); } // dee_transaction_get_iter_at_row () // Fall through to O(n) scan static DeeModelIter* dee_transaction_next (DeeModel *self, DeeModelIter *iter) { DeeTransactionPrivate *priv; IterType itype; JournalIter *jiter; JournalSegment *jseg; // FIXME: Strictly - this method will work even if 'iter' has been marked // removed. It might be nice to complain if anyone does this... g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_return_val_if_fail (!dee_transaction_is_committed (AS_TXN (self)), NULL); priv = DEE_TRANSACTION (self)->priv; iter = dee_transaction_next_raw (self, iter, &itype); /* Now scan forwards until we have something which is not deleted. * This is complicated by the fact that we may have attached segments * before iters that have been marked removed */ jiter = JOURNAL_ITER (iter); while (itype == ITER_TYPE_JOURNAL && journal_iter_is_removed (jiter)) { iter = dee_transaction_next_raw (self, iter, &itype); if (itype == ITER_TYPE_JOURNAL) { jiter = JOURNAL_ITER (iter); } if ((jseg = get_journal_segment_before (iter)) != NULL) { return MODEL_ITER (jseg->first_iter); } } return iter; } static DeeModelIter* dee_transaction_prev (DeeModel *self, DeeModelIter *iter) { DeeTransactionPrivate *priv; JournalIter *jiter, *jiter_prev; JournalSegment *jseg; g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_return_val_if_fail (!dee_transaction_is_committed (AS_TXN (self)), NULL); priv = DEE_TRANSACTION (self)->priv; if (check_journal_iter(iter, &jiter)) { if (jiter->prev_iter) { return MODEL_ITER (jiter->prev_iter); } else if (dee_model_is_first (priv->target, jiter->segment->target_iter)) { g_critical ("Trying to step before beginning of transaction model"); return MODEL_ITER (jiter); } else { iter = dee_model_prev (priv->target, jiter->segment->target_iter); if (check_journal_iter (iter, &jiter_prev)) return MODEL_ITER (jiter_prev); else return iter; } g_assert_not_reached (); } /* If there's a segment before the current iter in the target model, * step into that segment. Otherwise just step normally on the target */ jseg = get_journal_segment_before (iter); if (jseg != NULL) return MODEL_ITER (jseg->last_iter); else return dee_model_prev (priv->target, iter); } /* Inherited methods */ // dee_transaction_is_first () // dee_transaction_is_last () // dee_transaction_get_position () // Generic O(n) traversal static DeeModelTag* dee_transaction_register_tag (DeeModel *self, GDestroyNotify tag_destroy) { g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); g_critical ("DeeTransaction models can not create new tags, " "only re-use those of the target model"); return NULL; } static gpointer dee_transaction_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag) { g_return_val_if_fail (DEE_IS_TRANSACTION (self), NULL); // FIXME: Read through, or fetch from journal g_error ("Not implemented"); return NULL; } static void dee_transaction_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value) { g_return_if_fail (DEE_IS_TRANSACTION (self)); // FIXME: Write through or to journal g_error ("Not implemented"); } /* * Signal handlers on the target model. * Used to detect concurrent changes - which we do not support */ static void on_target_modified (DeeTransaction *self, DeeModelIter *iter) { if (self->priv->error_code != DEE_TRANSACTION_ERROR_COMMITTED) self->priv->error_code = DEE_TRANSACTION_ERROR_CONCURRENT_MODIFICATION; } /* * PUBLIC API */ /** * dee_transaction_new: * @target: The #DeeModel the transaction applies against * * Returns: (transfer full) (type Dee.Transaction): * A newly allocated #DeeTransaction. Free with g_object_unref() when * done using it - no matter if you call dee_transaction_commit() or not. */ DeeModel* dee_transaction_new (DeeModel *target) { g_return_val_if_fail (DEE_IS_MODEL (target), NULL); return DEE_MODEL (g_object_new (DEE_TYPE_TRANSACTION, "target", target, NULL)); } /** * dee_transaction_get_target: * @self: The transaction to retrieve the target model for * * Get the target model of a transaction. This is just a convenience method * for accessing the :target property. * * Returns: (transfer none): The target model */ DeeModel* dee_transaction_get_target (DeeTransaction *self) { g_return_val_if_fail (DEE_IS_TRANSACTION (self), FALSE); return self->priv->target; } /** * dee_transaction_is_committed: * @self: The transaction to inspect * * Check if a #DeeTransaction has been committed. This method is mainly for * debugging and testing purposes. * * Returns: %TRUE if and only if dee_transaction_commit() has completed * successfully on the transaction. */ gboolean dee_transaction_is_committed (DeeTransaction *self) { g_return_val_if_fail (DEE_IS_TRANSACTION (self), FALSE); return self->priv->error_code == DEE_TRANSACTION_ERROR_COMMITTED; } static const gchar* get_txn_error_string (guint error_code) { switch (error_code) { case DEE_TRANSACTION_ERROR_COMMITTED: return "Already committed"; case DEE_TRANSACTION_ERROR_CONCURRENT_MODIFICATION: return "Target model has been concurrently modified"; default: return "Unknown error"; } } /** * dee_transaction_commit: * @self: The transaction to commit * @error: (allow-none): Location to return a #GError in or %NULL to disregard * errors * * Apply a transaction to its target model. After this call the transaction * is invalidated and must be freed with g_object_unref(). * * Returns: %TRUE if and only if the transaction successfully applies to :target. */ gboolean dee_transaction_commit (DeeTransaction *self, GError **error) { DeeTransactionPrivate *priv; JournalIter *jiter, *seg_iter, *free_jiter; GSList *segments_to_free; DeeModelIter *iter; g_return_val_if_fail (DEE_IS_TRANSACTION (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); priv = self->priv; if (priv->error_code != 0) { g_set_error (error, DEE_TRANSACTION_ERROR, priv->error_code, "Error committing transaction. %s", get_txn_error_string (priv->error_code)); return FALSE; } if (DEE_IS_SERIALIZABLE_MODEL (priv->target) && priv->begin_seqnum != dee_serializable_model_get_seqnum (priv->target)) { g_set_error (error, DEE_TRANSACTION_ERROR, DEE_TRANSACTION_ERROR_CONCURRENT_MODIFICATION, "Target model seqnum has changed during the transaction"); return FALSE; } /* Because we have many criss crossing references to the segments we need * to collect them carefully to free all of them; and each only once! */ segments_to_free = NULL; /* To avoid an extra traversal on finalize() we free the journal iters * as we traverse them now. The txn is illegal after commit() by API * contract anyway */ for (jiter = priv->first_playback; jiter != NULL; ) { switch (jiter->change_type) { case CHANGE_TYPE_ADD: /* We can commit the whole segment since a segment is comprised * purely of additions */ if (jiter->segment->is_committed) break; for (seg_iter = jiter->segment->first_iter, iter = jiter->segment->target_iter; seg_iter; seg_iter = seg_iter->next_iter) { dee_model_insert_row_before (priv->target, iter, seg_iter->row_data); } jiter->segment->is_committed = TRUE; segments_to_free = g_slist_prepend (segments_to_free, jiter->segment); break; case CHANGE_TYPE_REMOVE: dee_model_remove (priv->target, jiter->override_iter); break; case CHANGE_TYPE_CHANGE: dee_model_set_row (priv->target, jiter->override_iter, jiter->row_data); break; default: g_critical ("Unexpected change type %u", jiter->change_type); break; } free_jiter = jiter; jiter = jiter->next_playback; journal_iter_free (free_jiter); } /* By now all jiters have been freed. Remaining is to free the segments */ g_slist_free_full (segments_to_free, (GDestroyNotify) journal_segment_free); priv->first_playback = NULL; priv->last_playback = NULL; priv->error_code = DEE_TRANSACTION_ERROR_COMMITTED; return TRUE; } GQuark dee_transaction_error_quark (void) { return g_quark_from_static_string ("dee-transaction-error-quark"); } dee-1.2.7+15.04.20150304/src/dee-glist-result-set.c0000644000015300001610000001377512475676210021551 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-glist-result-set * @short_description: Internal API do not use * * GList implementation of a #DeeResultSet on top of a #GList * */ #ifdef HAVE_CONFIG_H #include #endif #include "dee-glist-result-set.h" static void dee_glist_result_set_result_set_iface_init (DeeResultSetIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeGListResultSet, dee_glist_result_set, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (DEE_TYPE_RESULT_SET, dee_glist_result_set_result_set_iface_init)) #define DEE_GLIST_RESULT_SET_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_GLIST_RESULT_SET, DeeGListResultSetPrivate)) typedef struct { GList *rows; DeeModel *model; GList *cursor; GObject *row_owner; guint pos; guint n_rows; gboolean n_rows_calculated; } DeeGListResultSetPrivate; /* GObject Init */ static void dee_glist_result_set_finalize (GObject *object) { DeeGListResultSetPrivate *priv; priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (object); if (priv->model) g_object_unref (priv->model); if (priv->row_owner) g_object_unref (priv->row_owner); G_OBJECT_CLASS (dee_glist_result_set_parent_class)->finalize (object); } static void dee_glist_result_set_class_init (DeeGListResultSetClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_glist_result_set_finalize; /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeGListResultSetPrivate)); } static void dee_glist_result_set_init (DeeGListResultSet *self) { DeeGListResultSetPrivate *priv; priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); priv->pos = 0; priv->n_rows_calculated = FALSE; } static guint dee_glist_result_set_get_n_rows (DeeResultSet *self) { DeeGListResultSetPrivate *priv; g_return_val_if_fail (DEE_IS_GLIST_RESULT_SET (self), 0); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); if (!priv->n_rows_calculated) { priv->n_rows_calculated = TRUE; priv->n_rows = g_list_length (priv->rows); } return priv->n_rows; } static DeeModelIter* dee_glist_result_set_next (DeeResultSet *self) { DeeGListResultSetPrivate *priv; DeeModelIter *next; g_return_val_if_fail (DEE_IS_GLIST_RESULT_SET (self), NULL); g_return_val_if_fail (dee_result_set_has_next (self), NULL); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); next = dee_result_set_peek (self); priv->cursor = priv->cursor->next; priv->pos++; return next; } static gboolean dee_glist_result_set_has_next (DeeResultSet *self) { DeeGListResultSetPrivate *priv; g_return_val_if_fail (DEE_IS_GLIST_RESULT_SET (self), FALSE); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); return priv->cursor != NULL; } static DeeModelIter* dee_glist_result_set_peek (DeeResultSet *self) { DeeGListResultSetPrivate *priv; g_return_val_if_fail (DEE_IS_GLIST_RESULT_SET (self), NULL); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); if (priv->cursor == NULL) return NULL; return (DeeModelIter*) (priv->cursor->data); } static void dee_glist_result_set_seek (DeeResultSet *self, guint pos) { DeeGListResultSetPrivate *priv; g_return_if_fail (DEE_IS_GLIST_RESULT_SET (self)); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); priv->cursor = g_list_nth (priv->rows, pos); priv->pos = pos; if (priv->cursor == NULL && pos != 0) { g_warning ("Illegal seek in DeeGListResultSet. Seeking 0"); priv->cursor = priv->rows; priv->pos = 0; } } static guint dee_glist_result_set_tell (DeeResultSet *self) { DeeGListResultSetPrivate *priv; g_return_val_if_fail (DEE_IS_GLIST_RESULT_SET (self), 0); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); return priv->pos; } static DeeModel* dee_glist_result_set_get_model (DeeResultSet *self) { DeeGListResultSetPrivate *priv; g_return_val_if_fail (DEE_IS_GLIST_RESULT_SET (self), NULL); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); return priv->model; } static void dee_glist_result_set_result_set_iface_init (DeeResultSetIface *iface) { iface->get_n_rows = dee_glist_result_set_get_n_rows; iface->next = dee_glist_result_set_next; iface->has_next = dee_glist_result_set_has_next; iface->peek = dee_glist_result_set_peek; iface->seek = dee_glist_result_set_seek; iface->tell = dee_glist_result_set_tell; iface->get_model = dee_glist_result_set_get_model; } /* Internal constructor. Takes a ref on @model, @rows are implicitly reffed * by reffing @row_owner. Row owner may be NULL, in which case we cross fingers * and trust the caller that @rows are not freed */ DeeResultSet* dee_glist_result_set_new (GList *rows, DeeModel *model, GObject *row_owner) { GObject *self; DeeGListResultSetPrivate *priv; self = g_object_new (DEE_TYPE_GLIST_RESULT_SET, NULL); priv = DEE_GLIST_RESULT_SET_GET_PRIVATE (self); priv->rows = rows; priv->cursor = rows; priv->model = g_object_ref (model); if (row_owner != NULL) priv->row_owner = g_object_ref (row_owner); return (DeeResultSet*)self; } dee-1.2.7+15.04.20150304/src/dee-filter.c0000644000015300001610000004474712475676210017612 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-filter * @title: Filters * @short_description: A suite of simple #DeeFilters for use with #DeeFilterModels * @include: dee.h * * #DeeFilters are used together with #DeeFilterModels to build * "views" of some original #DeeModel. An example could be to build a view * of a model that exposes the rows of the original model sorted by a given * column (leaving the original model unaltered): * |[ * DeeModel *model, *view; * DeeFilter *collator; * * // Create and populate a model with some unsorted rows * model = dee_sequence_model_new (); * dee_model_set_schema (model, "i", "s", NULL); * dee_model_append (model, 27, "Foo"); * dee_model_append (model, 68, "Bar"); * * // Create a collator for column 1 * collator = dee_filter_new_collator (1); * * // Create the sorted view * view = dee_filter_model_new (collator, model); * g_free (collator); * * // When accessing the view the row with 'Bar' will be first * ]| */ #ifdef HAVE_CONFIG_H #include #endif #include // memset() #include "dee-filter-model.h" #include "dee-filter.h" #include "trace-log.h" typedef struct { guint n_cols; DeeCompareRowFunc cmp; gpointer user_data; GDestroyNotify destroy; GVariant **row_buf; } SortFilter; /* The CollatorFilter stores collation keys for the columns in a DeeModelTag */ typedef struct { guint column; DeeModelTag *collation_key_tag; } CollatorFilter; typedef struct { guint column; gchar *key; } KeyFilter; typedef struct { guint column; GRegex *regex; } RegexFilter; typedef struct { guint column; GVariant *value; } ValueFilter; /* * Private impl */ static gboolean _dee_filter_sort_map_notify (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *filter_model, gpointer user_data) { DeeModelIter *pos_iter; SortFilter *filter; guint i; gboolean was_found; g_return_val_if_fail (user_data != NULL, FALSE); filter = (SortFilter *) user_data; dee_model_get_row (orig_model, orig_iter, filter->row_buf); pos_iter = dee_model_find_row_sorted (DEE_MODEL (filter_model), filter->row_buf, filter->cmp, filter->user_data, &was_found); dee_filter_model_insert_iter_before (filter_model, orig_iter, pos_iter); for (i = 0; i < filter->n_cols; i++) g_variant_unref (filter->row_buf[i]); return was_found; } static void _dee_filter_sort_map_func (DeeModel *orig_model, DeeFilterModel *filter_model, gpointer user_data) { DeeModelIter *iter, *end; SortFilter *filter; g_return_if_fail (user_data != NULL); filter = (SortFilter *) user_data; filter->n_cols = dee_model_get_n_columns (orig_model); filter->row_buf = g_new0(GVariant*, filter->n_cols); iter = dee_model_get_first_iter (orig_model); end = dee_model_get_last_iter (orig_model); while (iter != end) { _dee_filter_sort_map_notify (orig_model, iter, filter_model, filter); iter = dee_model_next (orig_model, iter); } } static gint _cmp_collate_asc (GVariant **row1, GVariant **row2, gpointer user_data) { guint col = GPOINTER_TO_UINT (user_data); return g_utf8_collate (g_variant_get_string (row1[col], NULL), g_variant_get_string (row2[col], NULL)); } static gint _cmp_collate_desc (GVariant **row1, GVariant **row2, gpointer user_data) { guint col = GPOINTER_TO_UINT (user_data); return - g_utf8_collate (g_variant_get_string (row1[col], NULL), g_variant_get_string (row2[col], NULL)); } static void _dee_filter_key_map_func (DeeModel *orig_model, DeeFilterModel *filter_model, gpointer user_data) { DeeModelIter *iter, *end; KeyFilter *filter; guint column; const gchar *key, *val; g_return_if_fail (user_data != NULL); filter = (KeyFilter *) user_data; key = filter->key; column = filter->column; iter = dee_model_get_first_iter (orig_model); end = dee_model_get_last_iter (orig_model); while (iter != end) { val = dee_model_get_string (orig_model, iter, column); if (g_strcmp0 (key, val) == 0) { dee_filter_model_append_iter (filter_model, iter); } iter = dee_model_next (orig_model, iter); } } static gboolean _dee_filter_key_map_notify (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *filter_model, gpointer user_data) { KeyFilter *filter; const gchar *val; g_return_val_if_fail (user_data != NULL, FALSE); filter = (KeyFilter *) user_data; val = dee_model_get_string (orig_model, orig_iter, filter->column); /* Ignore rows that don't match the key */ if (g_strcmp0 (filter->key, val) != 0) return FALSE; dee_filter_model_insert_iter_with_original_order (filter_model, orig_iter); return TRUE; } static void _dee_filter_value_map_func (DeeModel *orig_model, DeeFilterModel *filter_model, gpointer user_data) { DeeModelIter *iter, *end; ValueFilter *filter; GVariant *val; g_return_if_fail (user_data != NULL); filter = (ValueFilter *) user_data; iter = dee_model_get_first_iter (orig_model); end = dee_model_get_last_iter (orig_model); while (iter != end) { val = dee_model_get_value (orig_model, iter, filter->column); if (g_variant_equal (filter->value, val)) { dee_filter_model_append_iter (filter_model, iter); } iter = dee_model_next (orig_model, iter); } } static gboolean _dee_filter_value_map_notify (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *filter_model, gpointer user_data) { ValueFilter *filter; GVariant *val; g_return_val_if_fail (user_data != NULL, FALSE); filter = (ValueFilter *) user_data; val = dee_model_get_value (orig_model, orig_iter, filter->column); /* Ignore rows that don't match the value */ if (!g_variant_equal (filter->value, val)) return FALSE; dee_filter_model_insert_iter_with_original_order (filter_model, orig_iter); return TRUE; } static void _dee_filter_regex_map_func (DeeModel *orig_model, DeeFilterModel *filter_model, gpointer user_data) { DeeModelIter *iter, *end; RegexFilter *filter; guint column; GRegex *regex; const gchar *val; g_return_if_fail (user_data != NULL); filter = (RegexFilter *) user_data; regex = filter->regex; column = filter->column; iter = dee_model_get_first_iter (orig_model); end = dee_model_get_last_iter (orig_model); while (iter != end) { val = dee_model_get_string (orig_model, iter, column); if (g_regex_match (regex, val, 0, NULL)) { dee_filter_model_append_iter (filter_model, iter); } iter = dee_model_next (orig_model, iter); } } static gboolean _dee_filter_regex_map_notify (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *filter_model, gpointer user_data) { RegexFilter *filter; const gchar *val; g_return_val_if_fail (user_data != NULL, FALSE); filter = (RegexFilter *) user_data; val = dee_model_get_string (orig_model, orig_iter, filter->column); /* Ignore rows that don't match the key */ if (!g_regex_match (filter->regex, val, 0, NULL)) return FALSE; dee_filter_model_insert_iter_with_original_order (filter_model, orig_iter); return TRUE; } static void sort_filter_free (SortFilter *filter) { if (filter->destroy != NULL) filter->destroy (filter->user_data); g_free (filter->row_buf); g_free (filter); } static void key_filter_free (KeyFilter *filter) { g_free (filter->key); g_free (filter); } static void value_filter_free (ValueFilter *filter) { g_variant_unref (filter->value); g_free (filter); } static void regex_filter_free (RegexFilter *filter) { g_regex_unref (filter->regex); g_free (filter); } /* * API */ /** * dee_filter_notify: * @filter: The filter to apply * @orig_iter: The #DeeModelIter added to @orig_model * @orig_model: The model that is being filtered * @filter_model: The #DeeFilterModel that holds the * filtered subset of @orig_model * * Call the #DeeFilterMapNotify function of a #DeeFilter. * When using a #DeeFilterModel you should not call this method yourself. * * Returns: The return value from the #DeeFilterMapNotify. That is; %TRUE * if @orig_iter was added to @filter_model */ gboolean dee_filter_notify (DeeFilter *filter, DeeModelIter *orig_iter, DeeModel *orig_model, DeeFilterModel *filter_model) { g_return_val_if_fail (filter != NULL, FALSE); return filter->map_notify (orig_model, orig_iter, filter_model, filter->userdata); } /** * dee_filter_map: * @filter: The filter to apply * @orig_model: The model that is being filtered * @filter_model: The #DeeFilterModel that holds the * filtered subset of @orig_model * * Call the #DeeFilterMapFunc function of a #DeeFilter. * When using a #DeeFilterModel you should not call this method yourself. */ void dee_filter_map (DeeFilter *filter, DeeModel *orig_model, DeeFilterModel *filter_model) { g_return_if_fail (filter != NULL); filter->map_func (orig_model, filter_model, filter->userdata); } /** * dee_filter_destroy: * @filter: The filter to destroy * * Call the #GDestroyNotify function on the userdata pointer of a #DeeFilter * (if the destroy member is set, that is). * * When using a #DeeFilterModel you should not call this method yourself. * * This method will not free the memory allocated for @filter. */ void dee_filter_destroy (DeeFilter *filter) { g_return_if_fail (filter != NULL); if (filter->destroy) filter->destroy (filter->userdata); } /** * dee_filter_new: * @map_func: (scope notified): The #DeeFilterMapFunc to use for the filter * @map_notify: (scope notified): The #DeeFilterMapNotify to use for the filter * @userdata: (closure): The user data to pass to @map_func and @map_notify * @destroy: (allow-none): The #GDestroyNotify to call on * @userdata when disposing of the filter * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a new #DeeFilter with the given parameters. This call will zero * the @out_filter struct. * */ void dee_filter_new (DeeFilterMapFunc map_func, DeeFilterMapNotify map_notify, gpointer userdata, GDestroyNotify destroy, DeeFilter *out_filter) { g_return_if_fail (map_func != NULL); g_return_if_fail (map_notify != NULL); g_return_if_fail (out_filter != NULL); memset (out_filter, 0, sizeof (DeeFilter)); out_filter->map_func = map_func; out_filter->map_notify = map_notify; out_filter->userdata = userdata; out_filter->destroy = destroy; } /** * dee_filter_new_sort: * @cmp_row: (scope notified): A #DeeCompareRowFunc to use for sorting * @cmp_user_data: (closure): User data passed to @cmp_row * @cmp_destroy: (allow-none): The #GDestroyNotify to call on * @cmp_user_data when disposing of the filter * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a new #DeeFilter sorting a model according to a #DeeCompareRowFunc. * */ void dee_filter_new_sort (DeeCompareRowFunc cmp_row, gpointer cmp_user_data, GDestroyNotify cmp_destroy, DeeFilter *out_filter) { SortFilter *filter; filter = g_new0 (SortFilter, 1); filter->cmp = cmp_row; filter->user_data = cmp_user_data; filter->destroy = cmp_destroy; dee_filter_new (_dee_filter_sort_map_func, _dee_filter_sort_map_notify, filter, (GDestroyNotify) sort_filter_free, out_filter); } /** * dee_filter_new_collator: * @column: The index of a column containing the strings to sort after * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a #DeeFilter that takes string values from a column in the model * and builds a #DeeFilterModel with the rows sorted according to the * collation rules of the current locale. */ void dee_filter_new_collator (guint column, DeeFilter *out_filter) { dee_filter_new_sort (_cmp_collate_asc, GUINT_TO_POINTER (column), NULL, out_filter); } /** * dee_filter_new_collator_desc: * @column: The index of a column containing the strings to sort after * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a #DeeFilter that takes string values from a column in the model * and builds a #DeeFilterModel with the rows sorted descending according to the * collation rules of the current locale. */ void dee_filter_new_collator_desc (guint column, DeeFilter *out_filter) { dee_filter_new_sort (_cmp_collate_desc, GUINT_TO_POINTER (column), NULL, out_filter); } /** * dee_filter_new_for_key_column: * @column: The index of a column containing the string key to match * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a #DeeFilter that only includes rows from the original model * which has an exact match on some string column. A #DeeFilterModel created * with this filter will be ordered in accordance with its parent model. */ void dee_filter_new_for_key_column (guint column, const gchar *key, DeeFilter *out_filter) { KeyFilter *key_filter; g_return_if_fail (key != NULL); key_filter = g_new0 (KeyFilter, 1); key_filter->column = column; key_filter->key = g_strdup (key); dee_filter_new (_dee_filter_key_map_func, _dee_filter_key_map_notify, key_filter, (GDestroyNotify) key_filter_free, out_filter); } /** * dee_filter_new_for_any_column: * @column: The index of a column containing the string to match * @value: (transfer none): A #GVariant value columns must match exactly. * The matching semantics are those of g_variant_equal(). If @value * is floating the ownership will be transfered to the filter * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a #DeeFilter that only includes rows from the original model * which match a variant value in a given column. A #DeeFilterModel * created with this filter will be ordered in accordance with its parent model. * * This method will work on any column, disregarding its schema, since the * value comparison is done using g_variant_equal(). This means you can use * this filter as a convenient fallback when there is no predefined filter * for your column type if raw performance is not paramount. */ void dee_filter_new_for_any_column (guint column, GVariant *value, DeeFilter *out_filter) { ValueFilter *v_filter; g_return_if_fail (value != NULL); v_filter = g_new0 (ValueFilter, 1); v_filter->column = column; v_filter->value = g_variant_ref_sink (value); dee_filter_new (_dee_filter_value_map_func, _dee_filter_value_map_notify, v_filter, (GDestroyNotify) value_filter_free, out_filter); } /** * dee_filter_new_regex: * @column: The index of a column containing the string to match * @regex: (transfer none):The regular expression @column must match * @out_filter: (out): A pointer to an uninitialized #DeeFilter struct. * This struct will zeroed and configured with the filter * parameters * * Create a #DeeFilter that only includes rows from the original model * which match a regular expression on some string column. A #DeeFilterModel * created with this filter will be ordered in accordance with its parent model. */ void dee_filter_new_regex (guint column, GRegex *regex, DeeFilter *out_filter) { RegexFilter *r_filter; g_return_if_fail (regex != NULL); r_filter = g_new0 (RegexFilter, 1); r_filter->column = column; r_filter->regex = g_regex_ref (regex); dee_filter_new (_dee_filter_regex_map_func, _dee_filter_regex_map_notify, r_filter, (GDestroyNotify) regex_filter_free, out_filter); } dee-1.2.7+15.04.20150304/src/dee-icu-term-filter.c0000644000015300001610000001706712475676210021330 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #include #include #include #include #include #include #include #include #include "dee-icu.h" /** * SECTION:dee-icu * @title: Dee ICU Extensions * @short_description: A suite of #DeeTermFilters based on ICU * @include: dee-icu.h * * This module allows developers to easily construct powerful * #DeeTermFilters with ease. The filters leverage the ICU * framework to provide world class transliteration features. * * The filters can be employed manually by calling dee_icu_term_filter_apply() * or installed in a #DeeAnalyzer by calling dee_analyzer_add_term_filter() * passing the term filter instance as the user data and * dee_icu_term_filter_destroy() as the #GDestroyNotify. */ struct _DeeICUTermFilter { UTransliterator *transliterator; }; static UChar* gchar2uchar (const gchar *string, int32_t *u_len) { int32_t len; UChar *u_string; UErrorCode u_error_code = U_ZERO_ERROR; if (string == NULL) { *u_len = -1; return NULL; } len = strlen (string) * 2; u_string = g_new(UChar, 2*len + 1); u_string[2*len] = '\0'; u_strFromUTF8Lenient (u_string, len, u_len, string, -1, &u_error_code); if (U_FAILURE(u_error_code)) { g_critical ("Failed to convert string '%s' into UTF-16: %s", string, u_errorName(u_error_code)); return NULL; } return u_string; } static gchar* print_error (const gchar *system_id, const gchar *rules, UParseError *u_parse_error, UErrorCode u_error_code) { GString *str; gchar *msg; str = g_string_new (""); g_string_append_printf (str, "[%s]: Error creating transliterator " "for system id '%s' and rules '%s'.", u_errorName (u_error_code), system_id, rules); if (u_parse_error->line >= 0) g_string_append_printf(str, " On line %i.", u_parse_error->line); if (u_parse_error->offset >= 0) g_string_append_printf(str, " Offset %i.", u_parse_error->offset); msg = str->str; g_string_free (str, FALSE); return msg; } static DeeICUError get_error_code (UErrorCode u_error_code) { /* The ICU error codes are quite tangled up, * so excuse the spaghetti logic please :-) */ if ( ! (u_error_code > U_PARSE_ERROR_START && u_error_code < U_PARSE_ERROR_LIMIT) && u_error_code != U_ILLEGAL_ARGUMENT_ERROR) { return DEE_ICU_ERROR_UNKNOWN; } switch (u_error_code) { case U_INVALID_ID: case U_INVALID_FUNCTION: return DEE_ICU_ERROR_BAD_ID; case U_ILLEGAL_ARGUMENT_ERROR: default: return DEE_ICU_ERROR_BAD_RULE; } } /** * dee_icu_term_filter_new: * @system_id: A system id for the transliterator to use. * See userguide.icu-project.org/transforms/general * @rules: (allow-none): A set of transliteration rules to use. * See userguide.icu-project.org/transforms/general/rules * @error: (allow-none) (error-domains Dee.ICUError): A place to return a #GError, or %NULL to ignore errors * * Create a new #DeeICUTermFilter for a given ICU transliterator system id * and/or set of transliteration rules. * * Returns: (transfer full): A newly allocated #DeeICUTermFilter. * Free with dee_icu_term_filter_destroy(). */ DeeICUTermFilter* dee_icu_term_filter_new (const gchar *system_id, const gchar *rules, GError **error) { DeeICUTermFilter *self; UChar *u_rules, *u_id; int32_t u_rules_len, u_id_len; UErrorCode u_error_code = 0; UParseError u_parse_error = { 0 }; g_return_val_if_fail (error == NULL || *error == NULL, NULL); self = g_new0 (DeeICUTermFilter, 1); u_id = gchar2uchar (system_id, &u_id_len); u_rules = gchar2uchar (rules, &u_rules_len); self->transliterator = utrans_openU (u_id, u_id_len, UTRANS_FORWARD, u_rules, u_rules_len, &u_parse_error, &u_error_code); if (U_FAILURE(u_error_code)) { DeeICUError error_code; gchar *msg; error_code = get_error_code (u_error_code); msg = print_error (system_id, rules, &u_parse_error, u_error_code); g_set_error_literal (error, DEE_ICU_ERROR, error_code, msg); g_free (msg); return NULL; } g_free (u_rules); g_free (u_id); return self; } /** * dee_icu_term_filter_new_ascii_folder: * * Construct a term filter that folds any UTF-8 string into ASCII. * * Returns: (transfer full): A newly allocated #DeeICUTermFilter. Free with * dee_icu_term_filter_destroy(). */ DeeICUTermFilter* dee_icu_term_filter_new_ascii_folder () { return dee_icu_term_filter_new ("Latin; Latin-ASCII;", NULL, NULL); } /** * dee_icu_term_filter_apply: * @self: The filter to apply * @text: The text to apply the filter on * * Apply a #DeeICUTermFilter on a piece of UTF-8 text. * * Returns: (transfer full): A newly allocated string. Free with g_free(). */ gchar* dee_icu_term_filter_apply (DeeICUTermFilter *self, const gchar *text) { UChar *u_text; int32_t u_cap, u_len, u_limit; UErrorCode u_error_code = U_ZERO_ERROR; gchar *result; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (text != NULL, NULL); u_cap = strlen (text) * 4 + 1; u_text = g_new (UChar, u_cap); u_text[u_cap - 1] = '\0'; u_strFromUTF8Lenient (u_text, u_cap, &u_len, text, -1, &u_error_code); if (U_FAILURE(u_error_code)) { g_critical ("Failed to convert string '%s' into UTF-16: %s", text, u_errorName(u_error_code)); return NULL; } u_limit = u_len; utrans_transUChars (self->transliterator, u_text, &u_len, u_cap, 0, &u_limit, &u_error_code); if (U_FAILURE(u_error_code)) { g_critical ("Failed to transliterate '%s': %s", text, u_errorName(u_error_code)); g_free (u_text); return NULL; } result = g_utf16_to_utf8(u_text, u_len, NULL, NULL, NULL); g_free (u_text); return result; } /** * dee_icu_term_filter_destroy: * @filter: The filter to free * * Free all resources allocated by a #DeeICUTermFilter. */ void dee_icu_term_filter_destroy (DeeICUTermFilter *filter) { g_return_if_fail (filter != NULL); utrans_close (filter->transliterator); g_free (filter); } GQuark dee_icu_error_quark (void) { return g_quark_from_static_string ("dee-icu-error-quark"); } dee-1.2.7+15.04.20150304/src/dee-transaction.h0000644000015300001610000000743612475676210020651 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_TRANSACTION_H #define _HAVE_DEE_TRANSACTION_H #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_TRANSACTION (dee_transaction_get_type ()) #define DEE_TRANSACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_TRANSACTION, DeeTransaction)) #define DEE_TRANSACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_TRANSACTION, DeeTransactionClass)) #define DEE_IS_TRANSACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_TRANSACTION)) #define DEE_IS_TRANSACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_TRANSACTION)) #define DEE_TRANSACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_SEQUENCE_MODEL, DeeTransactionClass)) typedef struct _DeeTransaction DeeTransaction; typedef struct _DeeTransactionClass DeeTransactionClass; typedef struct _DeeTransactionPrivate DeeTransactionPrivate; /** * DeeTransaction: * * All fields in the DeeTransaction structure are private and should never be * accessed directly */ struct _DeeTransaction { /*< private >*/ DeeSerializableModel parent; DeeTransactionPrivate *priv; }; struct _DeeTransactionClass { /*< private >*/ DeeSerializableModelClass parent_class; /*< private >*/ void (*_dee_transaction_1) (void); void (*_dee_transaction_2) (void); void (*_dee_transaction_3) (void); void (*_dee_transaction_4) (void); }; /** * DEE_TRANSACTION_ERROR: * * Error domain for the #DeeTransaction. Error codes will be from the * #DeeTransactionError enumeration */ #define DEE_TRANSACTION_ERROR dee_transaction_error_quark() /** * DeeTransactionError: * * Error codes for the #DeeTransaction class. These codes will be set when the * error domain is #DEE_TRANSACTION_ERROR. * * @DEE_TRANSACTION_ERROR_CONCURRENT_MODIFICATION: The target model has been * modified while the transaction was open. * * @DEE_TRANSACTION_ERROR_COMMITTED: Raised when someone tries to commit a * transaction that has already been committed */ typedef enum { DEE_TRANSACTION_ERROR_CONCURRENT_MODIFICATION = 1, DEE_TRANSACTION_ERROR_COMMITTED = 2 } DeeTransactionError; /** * dee_transaction_get_type: * * The GType of #DeeTransaction * * Return value: the #GType of #DeeTransaction **/ GType dee_transaction_get_type (void); DeeModel* dee_transaction_new (DeeModel *target); DeeModel* dee_transaction_get_target (DeeTransaction *self); gboolean dee_transaction_is_committed (DeeTransaction *self); gboolean dee_transaction_commit (DeeTransaction *self, GError **error); GQuark dee_transaction_error_quark (void); G_END_DECLS #endif /* _HAVE_DEE_TRANSACTION_H */ dee-1.2.7+15.04.20150304/src/dee-analyzer.h0000644000015300001610000001427212475676210020145 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_ANALYZER_H #define _HAVE_DEE_ANALYZER_H #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_ANALYZER (dee_analyzer_get_type ()) #define DEE_ANALYZER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_ANALYZER, DeeAnalyzer)) #define DEE_ANALYZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_ANALYZER, DeeAnalyzerClass)) #define DEE_IS_ANALYZER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_ANALYZER)) #define DEE_IS_ANALYZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_ANALYZER)) #define DEE_ANALYZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DEE_TYPE_ANALYZER, DeeAnalyzerClass)) typedef struct _DeeAnalyzer DeeAnalyzer; typedef struct _DeeAnalyzerClass DeeAnalyzerClass; typedef struct _DeeAnalyzerPrivate DeeAnalyzerPrivate; /** * DeeCollatorFunc: * @input: The string to produce a collation key for * @data: (closure): User data set when registering the collator * * A collator takes an input string, most often a term produced from a * #DeeAnalyzer, and outputs a collation key. * * Returns: (transfer full): The collation key. Free with g_free() when done * using it. */ typedef gchar* (*DeeCollatorFunc) (const gchar *input, gpointer data); /** * DeeTermFilterFunc: * @terms_in: A #DeeTermList with the terms to filter * @terms_out: A #DeeTermList to write the filtered terms to * @filter_data: (closure): User data set when registering the filter * * A term filter takes a list of terms and runs it through a filtering and/or * set of transformations and stores the output in a #DeeTermList. * * You can register term filters on a #DeeAnalyzer with * dee_analyzer_add_term_filter(). * * Returns: Nothing. Output is stored in @terms_out. */ typedef void (*DeeTermFilterFunc) (DeeTermList *terms_in, DeeTermList *terms_out, gpointer filter_data); /** * DeeAnalyzer: * * All fields in the DeeAnalyzer structure are private and should never be * accessed directly */ struct _DeeAnalyzer { /*< private >*/ GObject parent; DeeAnalyzerPrivate *priv; }; struct _DeeAnalyzerClass { /*< private >*/ GObjectClass parent_class; void (*analyze) (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out, DeeTermList *colkeys_out); void (*tokenize) (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out); void (*add_term_filter) (DeeAnalyzer *self, DeeTermFilterFunc filter_func, gpointer filter_data, GDestroyNotify filter_destroy); gchar* (*collate_key) (DeeAnalyzer *self, const gchar *data); gint (*collate_cmp) (DeeAnalyzer *self, const gchar *key1, const gchar *key2); /*< private >*/ void (*_dee_analyzer_1) (void); void (*_dee_analyzer_2) (void); void (*_dee_analyzer_3) (void); void (*_dee_analyzer_4) (void); }; /** * dee_analyzer_get_type: * * The GType of #DeeAnalyzer * * Return value: the #GType of #DeeAnalyzer **/ GType dee_analyzer_get_type (void); void dee_analyzer_analyze (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out, DeeTermList *colkeys_out); void dee_analyzer_tokenize (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out); void dee_analyzer_add_term_filter (DeeAnalyzer *self, DeeTermFilterFunc filter_func, gpointer filter_data, GDestroyNotify filter_destroy); gchar* dee_analyzer_collate_key (DeeAnalyzer *self, const gchar *data); gint dee_analyzer_collate_cmp (DeeAnalyzer *self, const gchar *key1, const gchar *key2); gint dee_analyzer_collate_cmp_func (const gchar *key1, const gchar *key2, gpointer analyzer); DeeAnalyzer* dee_analyzer_new (void); G_END_DECLS #endif /* _HAVE_DEE_ANALYZER_H */ dee-1.2.7+15.04.20150304/src/dee-hash-index.c0000644000015300001610000003144412475676210020343 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-hash-index * @short_description: A #DeeHashIndex implementation doing lookups in a hash map * @include: dee.h * * #DeeHashIndex is an implementation of #DeeHashIndex which is backed * by a hashmap. This means that it only supports the #DEE_TERM_MATCH_EXACT * flag in dee_hash_index_lookup(). * */ #ifdef HAVE_CONFIG_H #include #endif #include "dee-hash-index.h" #include "dee-result-set.h" #include "dee-glist-result-set.h" #include "trace-log.h" G_DEFINE_TYPE (DeeHashIndex, dee_hash_index, DEE_TYPE_INDEX); #define DEE_HASH_INDEX_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_HASH_INDEX, DeeHashIndexPrivate)) /* * FORWARDS */ static DeeResultSet* dee_hash_index_lookup (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags); static void dee_hash_index_foreach (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata); static guint dee_hash_index_get_n_terms (DeeIndex *self); static guint dee_hash_index_get_n_rows (DeeIndex *self); static guint dee_hash_index_get_n_rows_for_term (DeeIndex *self, const gchar *term); static guint dee_hash_index_get_supported_term_match_flags (DeeIndex *self); static void on_row_added (DeeIndex *self, DeeModelIter *iter, DeeModel *model); static void on_row_removed (DeeIndex *self, DeeModelIter *iter, DeeModel *model); static void on_row_changed (DeeIndex *self, DeeModelIter *iter, DeeModel *model); /* * GOBJECT STUFF */ struct _DeeHashIndexPrivate { /* Holds map of term -> GHashTable. * The term keys are owned by term_list */ GHashTable *terms; /* Holds map of DeeModelIter -> GPtrArray. * The terms are owned by term_list */ GHashTable *row_terms; /* All terms are stored here */ DeeTermList *term_list; gulong on_row_added_handler; gulong on_row_removed_handler; gulong on_row_changed_handler; }; enum { PROP_0, }; /* GObject stuff */ static void dee_hash_index_finalize (GObject *object) { DeeHashIndexPrivate *priv = DEE_HASH_INDEX (object)->priv; DeeModel *model = dee_index_get_model (DEE_INDEX (object)); if (priv->on_row_added_handler) g_signal_handler_disconnect(model, priv->on_row_added_handler); if (priv->on_row_removed_handler) g_signal_handler_disconnect(model, priv->on_row_removed_handler); if (priv->on_row_changed_handler) g_signal_handler_disconnect(model, priv->on_row_changed_handler); if (priv->terms) { g_hash_table_unref (priv->terms); priv->terms = NULL; } if (priv->row_terms) { g_hash_table_unref (priv->row_terms); priv->row_terms = NULL; } if (priv->term_list) { g_object_unref (priv->term_list); priv->term_list = NULL; } G_OBJECT_CLASS (dee_hash_index_parent_class)->finalize (object); } static void dee_hash_index_constructed (GObject *object) { DeeHashIndexPrivate *priv = DEE_HASH_INDEX (object)->priv; DeeIndex *self = DEE_INDEX (object); DeeModel *model = dee_index_get_model (self); DeeModelIter *iter; /* Listen for changes in the model so we automagically pick those up */ priv->on_row_added_handler = g_signal_connect_swapped (model, "row-added", G_CALLBACK (on_row_added), self); priv->on_row_removed_handler = g_signal_connect_swapped (model, "row-removed", G_CALLBACK (on_row_removed), self); priv->on_row_changed_handler = g_signal_connect_swapped (model, "row-changed", G_CALLBACK (on_row_changed), self); /* Index existing rows in the model */ iter = dee_model_get_first_iter (model); while (!dee_model_is_last (model, iter)) { on_row_added (self, iter, model); iter = dee_model_next (model, iter); } } static void dee_hash_index_class_init (DeeHashIndexClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); DeeIndexClass *idx_class = DEE_INDEX_CLASS (klass); obj_class->finalize = dee_hash_index_finalize; obj_class->constructed = dee_hash_index_constructed; idx_class->lookup = dee_hash_index_lookup; idx_class->foreach = dee_hash_index_foreach; idx_class->get_n_terms = dee_hash_index_get_n_terms; idx_class->get_n_rows = dee_hash_index_get_n_rows; idx_class->get_n_rows_for_term = dee_hash_index_get_n_rows_for_term; idx_class->get_supported_term_match_flags = dee_hash_index_get_supported_term_match_flags; /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeHashIndexPrivate)); } static void dee_hash_index_init (DeeHashIndex *self) { self->priv = DEE_HASH_INDEX_GET_PRIVATE (self); self->priv->terms = g_hash_table_new (g_str_hash, g_str_equal); self->priv->row_terms = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref); self->priv->term_list = g_object_new (DEE_TYPE_TERM_LIST, NULL); } /* * IMPLEMENTATION */ static DeeResultSet* dee_hash_index_lookup (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags) { DeeHashIndexPrivate *priv; GHashTable *term_data; g_return_val_if_fail (DEE_IS_HASH_INDEX (self), NULL); g_return_val_if_fail (term != NULL, NULL); if (flags != DEE_TERM_MATCH_EXACT) g_warning ("The DeeHashIndex only supports exact matching of terms"); priv = DEE_HASH_INDEX (self)->priv; term_data = g_hash_table_lookup (priv->terms, term); if (term_data == NULL) return dee_glist_result_set_new (NULL, /* The empty GList */ dee_index_get_model (self), NULL); return dee_glist_result_set_new (g_hash_table_get_keys(term_data), dee_index_get_model (self), G_OBJECT (self)); } static void dee_hash_index_foreach (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata) { DeeResultSet *results; g_return_if_fail (DEE_IS_HASH_INDEX (self)); g_return_if_fail (func != NULL); if (start_term == NULL) return; results = dee_index_lookup (self, start_term, DEE_TERM_MATCH_EXACT); if (results != NULL) func (start_term, results, userdata); g_object_unref (results); return; } static guint dee_hash_index_get_n_terms (DeeIndex *self) { DeeHashIndexPrivate *priv; g_return_val_if_fail (DEE_IS_HASH_INDEX (self), 0); priv = DEE_HASH_INDEX (self)->priv; return g_hash_table_size(priv->terms); } static guint dee_hash_index_get_n_rows (DeeIndex *self) { DeeHashIndexPrivate *priv; g_return_val_if_fail (DEE_IS_HASH_INDEX (self), 0); priv = DEE_HASH_INDEX (self)->priv; return g_hash_table_size(priv->row_terms); } static guint dee_hash_index_get_n_rows_for_term (DeeIndex *self, const gchar *term) { DeeHashIndexPrivate *priv; GHashTable *term_data; g_return_val_if_fail (DEE_IS_HASH_INDEX (self), 0); g_return_val_if_fail (term != NULL, 0); priv = DEE_HASH_INDEX (self)->priv; term_data = g_hash_table_lookup (priv->terms, term); if (term_data == NULL) return 0; return g_hash_table_size (term_data); } static guint dee_hash_index_get_supported_term_match_flags (DeeIndex *self) { return DEE_TERM_MATCH_EXACT; } static void on_row_added (DeeIndex *self, DeeModelIter *iter, DeeModel *model) { DeeHashIndexPrivate *priv; DeeAnalyzer *analyzer; DeeModelReader *reader; guint i, num_terms; gchar *term_stream; const gchar *term; GHashTable *term_data; GPtrArray *row_term_data; priv = DEE_HASH_INDEX (self)->priv; analyzer = dee_index_get_analyzer (self); reader = dee_index_get_reader (self); dee_term_list_clear (priv->term_list); term_stream = dee_model_reader_read (reader, model, iter); dee_analyzer_analyze (analyzer, term_stream, priv->term_list, NULL); // FIXME: col keys? num_terms = dee_term_list_num_terms (priv->term_list); g_free (term_stream); if (num_terms == 0) return; /* Make sure we have row_terms registered for this iter */ row_term_data = (GPtrArray*) g_hash_table_lookup (priv->row_terms, iter); if (row_term_data == NULL) { row_term_data = g_ptr_array_sized_new (num_terms); g_hash_table_insert (priv->row_terms, iter, row_term_data); } for (i = 0; i < num_terms; i++) { /* Important: The following works because @term lives in the scope * of priv->term_list. This makes the 'const gchar*' to 'gpointer' * casts valid. Yes, they even survive term_list.clear(). */ term = dee_term_list_get_term (priv->term_list, i); /* Update priv->terms */ term_data = g_hash_table_lookup (priv->terms, term); if (term_data == NULL) { term_data = g_hash_table_new (g_direct_hash, g_direct_equal); g_hash_table_insert (priv->terms, (gpointer) term, term_data); } /* Register the row for the term */ g_hash_table_insert (term_data, iter, NULL); /* Update reverse map row -> terms */ g_ptr_array_add(row_term_data, (gpointer) term); } } static void on_row_removed (DeeIndex *self, DeeModelIter *iter, DeeModel *model) { DeeHashIndexPrivate *priv; GHashTable *term_data; GPtrArray *row_term_data; gint i; gchar *term; priv = DEE_HASH_INDEX (self)->priv; row_term_data = (GPtrArray*) g_hash_table_lookup (priv->row_terms, iter); if (row_term_data == NULL) return; for (i = 0; i < row_term_data->len; i++) { term = g_ptr_array_index (row_term_data, i); term_data = g_hash_table_lookup (priv->terms, term); if (term_data == NULL) continue; g_hash_table_remove (term_data, iter); if (g_hash_table_size (term_data) == 0) g_hash_table_remove (priv->terms, term); } g_hash_table_remove (priv->row_terms, iter); } static void on_row_changed (DeeIndex *self, DeeModelIter *iter, DeeModel *model) { on_row_removed (self, iter, model); on_row_added (self, iter, model); } /* * API */ /** * dee_hash_index_new: * @model: The model to index * @analyzer: The #DeeAnalyzer used to tokenize and filter the terms extracted * by @reader * @reader: The #DeeModelReader used to extract terms from the model * * Create a new hash index. * * Returns: A newly allocated hash index. Free with g_object_unref(). */ DeeHashIndex* dee_hash_index_new (DeeModel *model, DeeAnalyzer *analyzer, DeeModelReader *reader) { DeeHashIndex *self; g_return_val_if_fail (DEE_IS_MODEL (model), NULL); g_return_val_if_fail (DEE_IS_ANALYZER (analyzer), NULL); g_return_val_if_fail (reader != NULL, NULL); self = (DeeHashIndex*) g_object_new (DEE_TYPE_HASH_INDEX, "model", model, "analyzer", analyzer, "reader", reader, NULL); return self; } dee-1.2.7+15.04.20150304/src/dee-index.c0000644000015300001610000002660312475676210017423 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-index * @short_description: An inverted index interface for smart access to a #DeeModel * @include: dee.h * * #DeeIndex is an interface for doing key based access to a #DeeModel. * A key in the index is known as a term and each term is * mapped to a set of matching #DeeModelIters. * * The terms are calculated by means of a #DeeAnalyzer which extracts a set of * terms from a given row in the model adding these terms to a #DeeTermList. * There is a suite of analyzers shipped with Dee, which you can browse in the * Analyzers section. */ #ifdef HAVE_CONFIG_H #include #endif #include // memcpy() #include "dee-model.h" #include "dee-index.h" #include "dee-marshal.h" #include "trace-log.h" G_DEFINE_ABSTRACT_TYPE (DeeIndex, dee_index, G_TYPE_OBJECT); #define DEE_INDEX_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_INDEX, DeeIndexPrivate)) /** * DeeIndexPrivate: * * Ignore this structure. **/ struct _DeeIndexPrivate { DeeModel *model; DeeAnalyzer *analyzer; DeeModelReader *reader; }; enum { PROP_0, PROP_MODEL, PROP_ANALYZER, PROP_READER }; /* GObject stuff */ static void dee_index_finalize (GObject *object) { DeeIndexPrivate *priv = DEE_INDEX (object)->priv; if (priv->model) { g_object_unref (priv->model); priv->model = NULL; } if (priv->analyzer) { g_object_unref (priv->analyzer); priv->analyzer = NULL; } if (priv->reader) { dee_model_reader_destroy (priv->reader); g_free (priv->reader); priv->reader = NULL; } G_OBJECT_CLASS (dee_index_parent_class)->finalize (object); } static void dee_index_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeeIndexPrivate *priv = DEE_INDEX (object)->priv; DeeModelReader *reader; switch (id) { case PROP_MODEL: priv->model = DEE_MODEL (g_value_dup_object (value)); break; case PROP_ANALYZER: priv->analyzer = DEE_ANALYZER (g_value_dup_object(value)); break; case PROP_READER: priv->reader = g_new0 (DeeModelReader, 1); reader = (DeeModelReader*) g_value_get_pointer (value); memcpy (priv->reader, reader, sizeof (DeeModelReader)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_index_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { DeeIndexPrivate *priv = DEE_INDEX (object)->priv; switch (id) { case PROP_MODEL: g_value_set_object (value, priv->model); break; case PROP_ANALYZER: g_value_set_object (value, priv->analyzer); break; case PROP_READER: g_value_set_pointer (value, priv->reader); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_index_class_init (DeeIndexClass *klass) { GParamSpec *pspec; GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_index_finalize; obj_class->get_property = dee_index_get_property; obj_class->set_property = dee_index_set_property; /** * DeeIndex:model: * * The #DeeModel being indexed */ pspec = g_param_spec_object ("model", "Model", "The model being indexed", DEE_TYPE_MODEL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_MODEL, pspec); /** * DeeIndex:analyzer: * * The #DeeAnalyzer used to analyze terms extracted by the model reader * * Type: DeeAnalyzer */ pspec = g_param_spec_object("analyzer", "Analyzer", "Analyzing terms extracted by the reader", DEE_TYPE_ANALYZER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_ANALYZER, pspec); /** * DeeIndex:reader: * * The #DeeModelReader used to extract terms from rows in the model * * Type: DeeModelReader */ pspec = g_param_spec_pointer("reader", "Reader", "The reader extracting terms for each row", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_READER, pspec); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeIndexPrivate)); } static void dee_index_init (DeeIndex *self) { self->priv = DEE_INDEX_GET_PRIVATE (self); } /** * dee_index_lookup: * @self: The index to perform the lookup in * @term: The term to look up on * @flags: A bitmask of #DeeTermMatchFlag to control how matching is * done * * Returns: (transfer full): A #DeeResultSet. Free with g_object_unref(). */ DeeResultSet* dee_index_lookup (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags) { DeeIndexClass *klass; g_return_val_if_fail (DEE_IS_INDEX (self), NULL); klass = DEE_INDEX_GET_CLASS (self); return (* klass->lookup) (self, term, flags); } /** * dee_index_lookup_one: * @self: The index to do the lookup in * @term: The exact term to match * * Convenience function in for cases where you have a priori guarantee that * a dee_index_lookup() call will return exactly 0 or 1 row. If the lookup * returns more than 1 row a warning will be printed on standard error and * %NULL will be returned. * * The typical use case for this function is if you need something akin to * a primary key in a relational database. * * Return value: (transfer none): A #DeeModelIter pointing to the matching * row or %NULL in case no rows matches @term */ DeeModelIter* dee_index_lookup_one (DeeIndex *self, const gchar *term) { DeeResultSet *results; DeeModelIter *iter; g_return_val_if_fail (DEE_IS_INDEX (self), NULL); results = dee_index_lookup (self, term, DEE_TERM_MATCH_EXACT); if (!dee_result_set_has_next (results)) { g_object_unref (results); return NULL; } iter = dee_result_set_next (results); if (dee_result_set_has_next (results)) { g_warning ("dee_index_lookup_one(index, '%s') expects exactly 0 or 1" " rows in the result set. Found %u", term, dee_result_set_get_n_rows (results)); g_object_unref (results); return NULL; } g_object_unref (results); return iter; } /** * dee_index_foreach: * @self: The index to iterate over * @start_term: The term to start from or %NULL to iterate over all terms * @func: (scope call): Called for each term in the index * @userdata: (closure): Arbitrary data to pass back to @func * * Iterate over an index optionally starting from some given term. Note that * unordered indexes (like #DeeHashIndex) has undefined behaviour with * this method. */ void dee_index_foreach (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata) { DeeIndexClass *klass; g_return_if_fail (DEE_IS_INDEX (self)); klass = DEE_INDEX_GET_CLASS (self); (* klass->foreach) (self, start_term, func, userdata); } /** * dee_index_get_model: * @self: The index to get the model for * * Get the model being indexed by this index * * Returns: (transfer none): The #DeeModel being indexed by this index */ DeeModel* dee_index_get_model (DeeIndex *self) { g_return_val_if_fail (DEE_IS_INDEX (self), NULL); return self->priv->model; } /** * dee_index_get_analyzer: * @self: The index to get the analyzer for * * Get the analyzer being used to analyze terms extracted with the * #DeeModelReader used by this index. * * Returns: (transfer none): The #DeeAnalyzer used to analyze terms with */ DeeAnalyzer* dee_index_get_analyzer (DeeIndex *self) { g_return_val_if_fail (DEE_IS_INDEX (self), NULL); return self->priv->analyzer; } /** * dee_index_get_reader: * @self: The index to get the reader for * * Get the reader being used to extract terms from rows in the model * * Returns: (transfer none): The #DeeModelReader used to extract terms with */ DeeModelReader* dee_index_get_reader (DeeIndex *self) { g_return_val_if_fail (DEE_IS_INDEX (self), NULL); return self->priv->reader; } /** * dee_index_get_n_terms: * @self: The index to get the number of terms for * * Get the number of terms in the index * * Returns: The number of unique terms in the index */ guint dee_index_get_n_terms (DeeIndex *self) { DeeIndexClass *klass; g_return_val_if_fail (DEE_IS_INDEX (self), 0); klass = DEE_INDEX_GET_CLASS (self); return (* klass->get_n_terms) (self); } /** * dee_index_get_n_rows: * @self: The index to get the number of rows for * * Get the number of indexed rows. A row is only indexed if it has at least one * term associated with it. If the analyzer has returned 0 terms then the row * is omitted from the index. * * Returns: The number of rows in the index. Note that this may less than or * equal to dee_model_get_n_rows(). */ guint dee_index_get_n_rows (DeeIndex *self) { DeeIndexClass *klass; g_return_val_if_fail (DEE_IS_INDEX (self), 0); klass = DEE_INDEX_GET_CLASS (self); return (* klass->get_n_rows) (self); } /** * dee_index_get_n_rows_for_term: * @self: The index to inspect * @term: The term to look for * * Get the number of rows that matches a given term * * Returns: The number of rows in the index registered for the given term */ guint dee_index_get_n_rows_for_term (DeeIndex *self, const gchar *term) { DeeIndexClass *klass; g_return_val_if_fail (DEE_IS_INDEX (self), 0); klass = DEE_INDEX_GET_CLASS (self); return (* klass->get_n_rows_for_term) (self, term); } /** * dee_index_get_supported_term_match_flags: * @self: The index to inspect * * Get the #DeeTermMatchFlag supported by this #DeeIndex instance * * Returns: A bit mask of the acceptedd #DeeTermMatchFlags */ guint dee_index_get_supported_term_match_flags (DeeIndex *self) { DeeIndexClass *klass; g_return_val_if_fail (DEE_IS_INDEX (self), 0); klass = DEE_INDEX_GET_CLASS (self); return (* klass->get_supported_term_match_flags) (self); } dee-1.2.7+15.04.20150304/src/dee-marshal.list0000644000015300001610000000004512475676210020464 0ustar pbuserpbgroup00000000000000# DeeSharedModel VOID:UINT64,UINT64 dee-1.2.7+15.04.20150304/src/dee-sequence-model.h0000644000015300001610000000565512475676210021233 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_SEQUENCE_MODEL_H #define _HAVE_DEE_SEQUENCE_MODEL_H #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_SEQUENCE_MODEL (dee_sequence_model_get_type ()) #define DEE_SEQUENCE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_SEQUENCE_MODEL, DeeSequenceModel)) #define DEE_SEQUENCE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DBUS_TYPE_SEQUENCE_MODEL, DeeSequenceModelClass)) #define DEE_IS_SEQUENCE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_SEQUENCE_MODEL)) #define DEE_IS_SEQUENCE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_SEQUENCE_MODEL)) #define DEE_SEQUENCE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_SEQUENCE_MODEL, DeeSequenceModelClass)) typedef struct _DeeSequenceModel DeeSequenceModel; typedef struct _DeeSequenceModelClass DeeSequenceModelClass; typedef struct _DeeSequenceModelPrivate DeeSequenceModelPrivate; /** * DeeSequenceModel: * * All fields in the DeeSequenceModel structure are private and should never be * accessed directly */ struct _DeeSequenceModel { /*< private >*/ DeeSerializableModel parent; DeeSequenceModelPrivate *priv; }; /** * DEE_SEQUENCE_MODEL_DBUS_IFACE: * * String constant defining the name of the DBus Model interface. */ #define DEE_SEQUENCE_MODEL_DBUS_IFACE "com.canonical.Dee.Model" struct _DeeSequenceModelClass { /*< private >*/ DeeSerializableModelClass parent_class; /*< private >*/ void (*_dee_sequence_model_1) (void); void (*_dee_sequence_model_2) (void); void (*_dee_sequence_model_3) (void); void (*_dee_sequence_model_4) (void); }; /** * dee_sequence_model_get_type: * * The GType of #DeeSequenceModel * * Return value: the #GType of #DeeSequenceModel **/ GType dee_sequence_model_get_type (void); DeeModel* dee_sequence_model_new (); G_END_DECLS #endif /* _HAVE_DEE_SEQUENCE_MODEL_H */ dee-1.2.7+15.04.20150304/src/dee-glist-result-set.h0000644000015300001610000000421312475676210021541 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #ifndef _DEE_GLIST_RESULT_SET_H_ #define _DEE_GLIST_RESULT_SET_H_ #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_GLIST_RESULT_SET (dee_glist_result_set_get_type ()) #define DEE_GLIST_RESULT_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_GLIST_RESULT_SET, DeeGListResultSet)) #define DEE_GLIST_RESULT_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_GLIST_RESULT_SET, DeeGListResultSetClass)) #define DEE_IS_GLIST_RESULT_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_GLIST_RESULT_SET)) #define DEE_IS_GLIST_RESULT_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_GLIST_RESULT_SET)) #define DEE_GLIST_RESULT_SET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DEE_TYPE_GLIST_RESULT_SET, DeeGListResultSetClass)) typedef struct _DeeGListResultSet DeeGListResultSet; typedef struct _DeeGListResultSetClass DeeGListResultSetClass; struct _DeeGListResultSet { GObject parent_instance; }; struct _DeeGListResultSetClass { GObjectClass parent_class; }; GType dee_glist_result_set_get_type (void); DeeResultSet* dee_glist_result_set_new (GList *rows, DeeModel *model, GObject *row_owner); G_END_DECLS #endif /* _DEE_GLIST_RESULT_SET_H_ */ dee-1.2.7+15.04.20150304/src/dee-server.c0000644000015300001610000005222512475676210017621 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: Michal Hruby * */ /** * SECTION:dee-server * @short_description: Creates a server object you can connect to. * @include: dee.h * * #DeeServer allows you to create private connections (connections which * are not routed via dbus-daemon). Note that, unlike #DeePeer, #DeeServer * will always be swarm leader, and clients connected to it cannot overtake * swarm leadership once the server connection is closed. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "dee-server.h" #include "dee-marshal.h" #include "trace-log.h" G_DEFINE_TYPE (DeeServer, dee_server, DEE_TYPE_PEER) #define GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEE_TYPE_SERVER, DeeServerPrivate)) #define ACTIVE_CONNECTIONS_KEY "dee-active-connections-list" #define CONNECTION_ACCEPTED_KEY "dee-connection-accepted" /** * DeeServerPrivate: * * Ignore this structure. **/ struct _DeeServerPrivate { GCredentials *our_creds; GDBusServer *server; gchar *bus_address; gboolean same_user_only; guint initialize_server_timer_id; GSList *active_connections; guint connection_id; GHashTable *connection_id_map; }; /* Globals */ enum { PROP_0, PROP_BUS_ADDRESS, PROP_SAME_USER_ONLY }; enum { LAST_SIGNAL }; //static guint32 _server_signals[LAST_SIGNAL] = { 0 }; static GHashTable *active_servers = NULL; /* Forwards */ static gboolean on_new_connection (GDBusServer *server, GDBusConnection *connection, gpointer user_data); static void on_connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, gpointer user_data); static gboolean dee_server_is_swarm_leader (DeePeer *peer); static const gchar* dee_server_get_swarm_leader (DeePeer *peer); static GSList* dee_server_get_connections (DeePeer *peer); static gchar** dee_server_list_peers (DeePeer *peer); /* GObject methods */ static void dee_server_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { DeeServerPrivate *priv = DEE_SERVER (object)->priv; switch (property_id) { case PROP_BUS_ADDRESS: g_value_set_string (value, priv->bus_address); break; case PROP_SAME_USER_ONLY: g_value_set_boolean (value, priv->same_user_only); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void dee_server_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { DeeServerPrivate *priv = DEE_SERVER (object)->priv; switch (property_id) { case PROP_BUS_ADDRESS: if (priv->bus_address) g_free (priv->bus_address); priv->bus_address = g_value_dup_string (value); break; case PROP_SAME_USER_ONLY: priv->same_user_only = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void remove_connection (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, GDBusServer *server) { GSList *list; trace_object (server, "Removing [%p] from list of active connections", connection); list = (GSList*) g_object_steal_data (G_OBJECT (server), ACTIVE_CONNECTIONS_KEY); list = g_slist_remove (list, connection); g_object_set_data_full (G_OBJECT (server), ACTIVE_CONNECTIONS_KEY, list, (GDestroyNotify) g_slist_free); } static void connection_finalized (gpointer server, GObject *connection) { remove_connection ((GDBusConnection*) connection, FALSE, NULL, (GDBusServer*) server); } static gboolean add_new_connection (GDBusServer *server, GDBusConnection *connection, gpointer user_data) { GSList *list; gpointer *data; data = g_object_steal_data (G_OBJECT (connection), CONNECTION_ACCEPTED_KEY); if (data != NULL) { list = (GSList*) g_object_steal_data (G_OBJECT (server), ACTIVE_CONNECTIONS_KEY); list = g_slist_prepend (list, connection); g_object_set_data_full (G_OBJECT (server), ACTIVE_CONNECTIONS_KEY, list, (GDestroyNotify) g_slist_free); g_signal_connect (connection, "closed", G_CALLBACK (remove_connection), server); /* the connections in our list are weak references, need to make sure * they're not used after they're freed */ g_object_weak_ref (G_OBJECT (connection), connection_finalized, server); } /* accept the connection if any of the DeeServers accepted it */ return data != NULL; } static void server_toggle_cb (gpointer data, GObject *object, gboolean is_last_ref) { GSList *list, *iter; if (!is_last_ref) return; g_hash_table_remove (active_servers, data); g_dbus_server_stop (G_DBUS_SERVER (object)); list = (GSList*) g_object_get_data (object, ACTIVE_CONNECTIONS_KEY); for (iter = list; iter != NULL; iter = iter->next) { g_object_weak_unref (iter->data, connection_finalized, object); g_signal_handlers_disconnect_by_func (iter->data, remove_connection, object); } /* and this will finalize the object */ g_object_remove_toggle_ref (object, server_toggle_cb, data); } static GDBusServer* get_server_for_address (const gchar *bus_address, GError **error) { gchar *guid; gchar *address; GDBusServer *server; GDBusServerFlags server_flags; server = g_hash_table_lookup (active_servers, bus_address); if (server != NULL) { return g_object_ref (server); } /* create new GDBusServer instance */ guid = g_dbus_generate_guid (); server_flags = G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; server = g_dbus_server_new_sync (bus_address, server_flags, guid, NULL, NULL, error); if (error && *error) return NULL; /* need to keep a list of all connections for this GDBusServer */ g_signal_connect_after (server, "new-connection", G_CALLBACK (add_new_connection), NULL); address = g_strdup (bus_address); /* transfer ownership of address to the hash table */ g_hash_table_insert (active_servers, address, server); /* we need to stop the server before unreffing it the last time, so we'll * use toggle ref. FIXME: toggle ref is odd, use just weak ref (to remove * it from the active_servers hash table) once it properly stops * the listener on finalize. */ g_object_add_toggle_ref (G_OBJECT (server), server_toggle_cb, address); g_free (guid); return server; } static gboolean initialize_server (DeeServer *self) { DeeServerPrivate *priv; GSList *connections; GError *error = NULL; priv = self->priv; priv->initialize_server_timer_id = 0; /* create new server or get the existing instance for this bus_address */ priv->server = get_server_for_address (priv->bus_address, &error); if (error) { g_critical ("Unable to set up DBusServer: %s", error->message); g_error_free (error); g_object_notify (G_OBJECT (self), "swarm-leader"); return FALSE; } g_signal_connect (priv->server, "new-connection", G_CALLBACK (on_new_connection), self); g_dbus_server_start (priv->server); g_object_notify (G_OBJECT (self), "swarm-leader"); /* were there any connections already? */ connections = (GSList*) g_object_get_data (G_OBJECT (priv->server), ACTIVE_CONNECTIONS_KEY); for ( ; connections != NULL; connections = connections->next) { on_new_connection (priv->server, (GDBusConnection*) connections->data, self); } return FALSE; } static void dee_server_constructed (GObject *self) { DeeServerPrivate *priv; const gchar *swarm_name; priv = DEE_SERVER (self)->priv; /* we should chain up the constructed method here, but peer does things we * don't want to, so not chaining up... */ swarm_name = dee_peer_get_swarm_name (DEE_PEER (self)); if (swarm_name == NULL) { g_critical ("DeeServer created without a swarm name. You must specify " "a non-NULL swarm name"); return; } priv->our_creds = g_credentials_new (); if (!priv->bus_address) { priv->bus_address = dee_server_bus_address_for_name (swarm_name, priv->same_user_only); } /* Ideally we'd call the async variant of g_dbus_server_new (which doesn't * exist atm) */ priv->initialize_server_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)initialize_server, self, NULL); } static void close_connection (gpointer data, gpointer user_data) { GDBusConnection *connection = G_DBUS_CONNECTION (data); g_signal_handlers_disconnect_by_func (connection, on_connection_closed, user_data); // FIXME: should we use the sync variant? and flush first? //g_dbus_connection_close (connection, NULL, NULL, NULL); } static void dee_server_finalize (GObject *object) { DeeServerPrivate *priv; priv = DEE_SERVER (object)->priv; if (priv->initialize_server_timer_id) { g_source_remove (priv->initialize_server_timer_id); priv->initialize_server_timer_id = 0; } if (priv->active_connections) { g_slist_foreach (priv->active_connections, close_connection, object); g_slist_free_full (priv->active_connections, g_object_unref); priv->active_connections = NULL; } if (priv->server) { g_signal_handlers_disconnect_by_func (priv->server, on_new_connection, object); /* this should be done automatically on unref, but it doesn't seem so */ g_dbus_server_stop (priv->server); g_object_unref (priv->server); } if (priv->connection_id_map) { g_hash_table_unref (priv->connection_id_map); priv->connection_id_map = NULL; } if (priv->bus_address) { g_free (priv->bus_address); } if (priv->our_creds) { g_object_unref (priv->our_creds); priv->our_creds = NULL; } G_OBJECT_CLASS (dee_server_parent_class)->finalize (object); } static void dee_server_class_init (DeeServerClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); DeePeerClass *peer_class = DEE_PEER_CLASS (klass); g_type_class_add_private (klass, sizeof (DeeServerPrivate)); object_class->constructed = dee_server_constructed; object_class->get_property = dee_server_get_property; object_class->set_property = dee_server_set_property; object_class->finalize = dee_server_finalize; peer_class->is_swarm_leader = dee_server_is_swarm_leader; peer_class->get_swarm_leader = dee_server_get_swarm_leader; peer_class->get_connections = dee_server_get_connections; peer_class->list_peers = dee_server_list_peers; /** * DeeServer::bus-address: * * D-Bus address the server is bound to. If you do not specify this property * #DeeServer will use dee_server_bus_address_for_name() using current swarm * name to determine the value of this property. * You can use dee_server_get_client_address() to get address string * that can be used by clients to connect to this #DeeServer instance. */ pspec = g_param_spec_string ("bus-address", "Bus address", "Bus address to use for the connection", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_BUS_ADDRESS, pspec); /** * DeeServer::same-user-only: * * A boolean specifying whether the server should only accept connections * from same user as the current process owner. */ pspec = g_param_spec_boolean ("same-user-only", "Same user only", "Accept connections from current user only", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SAME_USER_ONLY, pspec); /* we'll use this variable to share GDBusServer instances between mutliple * DeeServers - this will enable us to use one connection with multiple * models with different swarm_names */ active_servers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void dee_server_init (DeeServer *self) { self->priv = GET_PRIVATE (self); self->priv->connection_id_map = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); } /** * dee_server_new: * @swarm_name: Name of swarm to join. * * Creates a new instance of #DeeServer and tries to bind * to #DeeServer:bus-address. The #DeePeer:swarm-leader property will be set * when the binding succeeds. * * * * Note that this function will automatically determine the value * of #DeeServer:bus-address property and will generally cause your * application to use new socket for every #DeeServer with different swarm * name. See dee_server_new_for_address() if you'd like to share one * connection between multiple #DeeServer instances. * * * * Return value: (transfer full): A newly constructed #DeeServer. */ DeeServer* dee_server_new (const gchar* swarm_name) { g_return_val_if_fail (swarm_name != NULL, NULL); return DEE_SERVER (g_object_new (DEE_TYPE_SERVER, "swarm-name", swarm_name, NULL)); } /** * dee_server_new_for_address: * @swarm_name: Name of swarm to join. * @bus_address: D-Bus address to use for the connection. * * Creates a new instance of #DeeServer and tries to bind to @bus_address. * The #DeePeer:swarm-leader property will be set when the binding succeeds. * * If there is already a #DeeServer instance bound to @bus_address, * the connection will be shared with the newly constructed instance. * * * * This function is primarily meant for sharing of one connection (socket) * between multiple DeeServers, so that you can create #DeeServer instances * with varying swarm names, but the same bus address, which will cause * them to share the connection (the sharing is possible only within * the same process though). * * * * Return value: (transfer full): A newly constructed #DeeServer. */ DeeServer* dee_server_new_for_address (const gchar* swarm_name, const gchar* bus_address) { g_return_val_if_fail (swarm_name != NULL, NULL); return DEE_SERVER (g_object_new (DEE_TYPE_SERVER, "swarm-name", swarm_name, "bus-address", bus_address, NULL)); } /** * dee_server_get_client_address: * @server: A #DeeServer. * * Gets a D-Bus address string that can be used by clients to connect to server. * * Return value: A D-Bus address string. Do not free. */ const gchar* dee_server_get_client_address (DeeServer *server) { DeeServerPrivate *priv; g_return_val_if_fail (DEE_IS_SERVER (server), NULL); priv = server->priv; return priv->server != NULL ? g_dbus_server_get_client_address (priv->server) : NULL; } /** * dee_server_bus_address_for_name: * @name: A name to create bus address for. * @include_username: Include current user name as part of the bus address. * * Helper method which creates bus address string for the given name, which * should have the same format as a DBus unique name. * * Return value: (transfer full): Newly allocated string with bus address. * Use g_free() to free. */ gchar* dee_server_bus_address_for_name (const gchar *name, gboolean include_username) { gchar *result; g_return_val_if_fail (name != NULL, NULL); if (g_unix_socket_address_abstract_names_supported ()) { result = include_username ? g_strdup_printf ("unix:abstract=%s-%s", g_get_user_name (), name) : g_strdup_printf ("unix:abstract=%s", name); } else { result = include_username ? g_strdup_printf ("unix:path=%s/%s-%s", g_get_tmp_dir (), g_get_user_name (), name) : g_strdup_printf ("unix:path=%s/%s", g_get_tmp_dir (), name); } return result; } /* Private Methods */ static gboolean on_new_connection (GDBusServer *server, GDBusConnection *connection, gpointer user_data) { gchar *connection_name; GCredentials *creds; DeeServer *self = DEE_SERVER (user_data); DeeServerPrivate *priv = self->priv; trace_object (self, "New connection: [%p]", connection); creds = g_dbus_connection_get_peer_credentials (connection); if (!g_credentials_is_same_user (creds, priv->our_creds, NULL) && priv->same_user_only) { trace_object (self, "User id doesn't match, rejecting connection"); return FALSE; } priv->active_connections = g_slist_prepend (priv->active_connections, g_object_ref (connection)); g_signal_connect (connection, "closed", G_CALLBACK (on_connection_closed), self); /* time to register dbus objects on this connection */ g_signal_emit_by_name (self, "connection-acquired", connection); connection_name = g_strdup_printf ("%s:%u", g_dbus_server_get_guid (priv->server), ++priv->connection_id); /* hash table assumes ownership of connection_name */ g_hash_table_insert (priv->connection_id_map, connection, connection_name); g_signal_emit_by_name (self, "peer-found", connection_name); g_object_set_data (G_OBJECT (connection), CONNECTION_ACCEPTED_KEY, GINT_TO_POINTER (1)); /* we can't return TRUE, otherwise handlers in other DeeServers wouldn't run, * see add_new_connection callback */ return FALSE; } static void on_connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, gpointer user_data) { GSList *element; DeeServer *self = DEE_SERVER (user_data); DeeServerPrivate *priv = self->priv; trace_object (self, "Connection [%p] was closed", connection); element = g_slist_find (priv->active_connections, connection); if (element == NULL) { g_warning ("Connection closed for element which isn't " "in active_connections"); return; } priv->active_connections = g_slist_delete_link (priv->active_connections, element); /* reverse order of signals than in new-connection handler */ g_signal_emit_by_name (self, "peer-lost", g_hash_table_lookup (priv->connection_id_map, connection)); g_hash_table_remove (priv->connection_id_map, connection); g_signal_emit_by_name (self, "connection-closed", connection); g_object_unref (connection); } static gboolean dee_server_is_swarm_leader (DeePeer *peer) { DeeServerPrivate *priv = DEE_SERVER (peer)->priv; return priv->server != NULL; } static const gchar* dee_server_get_swarm_leader (DeePeer *peer) { DeeServerPrivate *priv = DEE_SERVER (peer)->priv; return priv->server ? g_dbus_server_get_guid (priv->server) : NULL; } static GSList* dee_server_get_connections (DeePeer *peer) { DeeServerPrivate *priv = DEE_SERVER (peer)->priv; return g_slist_copy (priv->active_connections); } static gchar** dee_server_list_peers (DeePeer *peer) { GSList *it; gchar **result; int i; DeeServerPrivate *priv = DEE_SERVER (peer)->priv; result = g_new (gchar*, g_slist_length (priv->active_connections) + 1); i = 0; for (it = priv->active_connections; it != NULL; it = it->next) { result[i++] = g_strdup ((gchar*) g_hash_table_lookup (priv->connection_id_map, it->data)); } result[i] = NULL; return result; } dee-1.2.7+15.04.20150304/src/dee-peer.c0000644000015300001610000015242012475676210017244 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authors: * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ /** * SECTION:dee-peer * @short_description: Finds other objects with the same swarm-name on the bus. * @include: dee.h * * #DeePeer allows you to build objects that can rendevouz on DBus * without the need for an central registration service. Think of it like * peer-to-peer for your application. The DBus session bus will also implicitly * elect a swarm leader - namely the one owning the swarm name on the bus, but * it's up to the consumer of this API to determine whether swarm leadership has * any concrete responsibilities associated. * * Peers find eachother through a well-known "swarm-name", which is a * well known DBus name, such as: org.myapp.MyPeers. Choose a namespaced * name that would not normally be used outside of your program. * * For example: * * { * DeePeer *peer; * * peer = g_object_new (DBUS_TYPE_PEER, * "swarm-name", "org.myapp.MyPeers", * NULL); * * g_signal_connect (peer, "peer-found", * G_CALLBACK (on_peer_found), NULL); * g_signal_connect (peer, "peer-lost", * G_CALLBACK (on_peer_lost), NULL); * } * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "dee-peer.h" #include "dee-marshal.h" #include "trace-log.h" G_DEFINE_TYPE (DeePeer, dee_peer, G_TYPE_OBJECT) #define DEE_PEER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_PEER, DeePeerPrivate)) #define _DeePeerIter GSequenceIter /** * DeePeerPrivate: * * Ignore this structure. **/ struct _DeePeerPrivate { GDBusConnection *connection; /* Used as a hash set with the unique addresses of the peers. * This hash table must be protected from concurrent access, * since it's used in the GDBus message dispatch thread */ GHashTable *peers; /* A List with the string-formatted match rules we've installed */ GSList *match_rules; /* The GDBus filter id, so we can uninstall our message filter again */ guint filter_id; /* GDBus id for the DBus signal subscriptions */ guint dbus_signals_id; /* The GDBus name owner id for g_bus_own_name() */ guint name_owner_id; /* The GDBus name watcher id from g_bus_watch_name() */ guint name_watcher_id; /* Swarm related properties */ gboolean swarm_owner; const gchar *own_name; gchar *swarm_name; gchar *swarm_path; gchar *swarm_leader; gboolean connected; gboolean is_swarm_leader; gboolean has_been_leader; gboolean is_first_name_check; GCancellable *list_cancellable; /* if priv->head_count != NULL it indicates that we are in * "head counting mode" in which case priv->head_count_source will be a * GSource id for a timeout that completes the head count */ GSList *head_count; guint head_count_source; /* Protecting the priv->peers table from concurrent access in * the GDBus message dispatch thread */ #if GLIB_CHECK_VERSION(2, 31, 16) GMutex lock_real; #endif GMutex *lock; }; /* Globals */ enum { PROP_0, PROP_SWARM_NAME, PROP_SWARM_LEADER, PROP_SWARM_OWNER }; enum { PEER_FOUND, PEER_LOST, CONNECTION_ACQUIRED, CONNECTION_CLOSED, LAST_SIGNAL }; static guint32 _peer_signals[LAST_SIGNAL] = { 0 }; /* Forwards */ static void remove_match_rule (GDBusConnection *conn, const gchar *rule); static void emit_peer_found (DeePeer *self, const gchar *name); static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data); static void on_leadership_lost (GDBusConnection *connection, const gchar *name, gpointer user_data); static void on_leadership_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data); static void on_leadership_changed (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data); static void on_join_received (DeePeer *self, const gchar *peer_address); static void on_bye_received (DeePeer *self, const gchar *peer_address); static void on_ping_received (DeePeer *self, const gchar *leader_address); static void on_pong_received (DeePeer *self, const gchar *peer_address); static void on_list_received (GObject *source_object, GAsyncResult *res, gpointer user_data); static void set_swarm_name (DeePeer *self, const gchar *swarm_name); static void emit_ping (DeePeer *self); static void emit_pong (DeePeer *self); static void on_dbus_peer_signal (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data); static GDBusMessage* gdbus_message_filter (GDBusConnection *connection, GDBusMessage *message, gboolean incoming, gpointer user_data); static const gchar* dee_peer_real_get_swarm_leader (DeePeer *self); static gboolean dee_peer_real_is_swarm_leader (DeePeer *self); static GSList* dee_peer_real_get_connections (DeePeer *self); static gchar** dee_peer_real_list_peers (DeePeer *self); /* GObject methods */ static void dee_peer_dispose (GObject *object) { DeePeerPrivate *priv; GSList *match_iter; priv = DEE_PEER (object)->priv; /* Remove match rules from the bus, and free the string repr. of the rule */ if (priv->connection) { /* Uninstall filter. * Implementation note: We must remove the filter and signal listener * _before_ dropping the swarm name because gdbus currently does a * sync dbus call to release the name which makes us race against * getting a NameOwnerChanged */ /* The removal of the filter rules also has to be done in dispose, * not finalize, cause the filter callback can ref this object and * therefore postpone finalization, although that shouldn't happen, * as this uses locking and the call will not return until all current * invocations of the filter callback finish. */ g_dbus_connection_remove_filter (priv->connection, priv->filter_id); for (match_iter = priv->match_rules; match_iter != NULL; match_iter = match_iter->next) { remove_match_rule (priv->connection, match_iter->data); g_free (match_iter->data); } /* Stop listening for signals */ if (priv->dbus_signals_id != 0) { g_dbus_connection_signal_unsubscribe (priv->connection, priv->dbus_signals_id); priv->dbus_signals_id = 0; } g_object_unref (priv->connection); priv->connection = NULL; } if (priv->match_rules) { g_slist_free (priv->match_rules); priv->match_rules = NULL; } /* Stop trying to own the swarm name. * See implementation note above */ if (priv->name_owner_id != 0) { g_bus_unown_name (priv->name_owner_id); priv->name_owner_id = 0; } /* Stop listening for swarm leadership changes */ if (priv->name_watcher_id != 0) { g_bus_unwatch_name (priv->name_watcher_id); priv->name_watcher_id = 0; } G_OBJECT_CLASS (dee_peer_parent_class)->dispose (object); } static void dee_peer_finalize (GObject *object) { DeePeerPrivate *priv; priv = DEE_PEER (object)->priv; if (priv->list_cancellable != NULL) { g_cancellable_cancel (priv->list_cancellable); g_object_unref (priv->list_cancellable); priv->list_cancellable = NULL; } /* Free resources */ if (priv->swarm_name) { g_free (priv->swarm_name); priv->swarm_name = NULL; } if (priv->swarm_path) { g_free (priv->swarm_path); priv->swarm_path = NULL; } if (priv->swarm_leader) { g_free (priv->swarm_leader); priv->swarm_leader = NULL; } if (priv->peers) { g_hash_table_destroy (priv->peers); priv->peers = NULL; } if (priv->lock != NULL) { #if GLIB_CHECK_VERSION(2, 31, 16) g_mutex_clear (priv->lock); #else g_mutex_free (priv->lock); #endif priv->lock = NULL; } if (priv->head_count != NULL) { g_slist_foreach(priv->head_count, (GFunc) g_free, NULL); g_slist_free (priv->head_count); priv->head_count = NULL; } if (priv->head_count_source != 0) { g_source_remove (priv->head_count_source); priv->head_count_source = 0; } G_OBJECT_CLASS (dee_peer_parent_class)->finalize (object); } static void dee_peer_constructed (GObject *self) { DeePeerPrivate *priv; GBusNameOwnerFlags flags; priv = DEE_PEER (self)->priv; if (priv->swarm_name == NULL) { g_critical ("DeePeer created without a swarm name. You must specify " "a non-NULL swarm name"); return; } /* Contend to be swarm leaders. Pick me! Pick me! */ flags = priv->swarm_owner ? G_BUS_NAME_OWNER_FLAGS_REPLACE : G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT; priv->name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, priv->swarm_name, /* name to own */ flags, on_bus_acquired, on_leadership_acquired, on_leadership_lost, self, /* user data */ NULL); /* free func */ /* Listen for changes in leadership */ priv->name_watcher_id = g_bus_watch_name(G_BUS_TYPE_SESSION, priv->swarm_name, G_BUS_NAME_WATCHER_FLAGS_NONE, on_leadership_changed, NULL, /* name vanished cb */ self, /* user data */ NULL); /* user data free func */ } static void dee_peer_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeePeerPrivate *priv = DEE_PEER (object)->priv; switch (id) { case PROP_SWARM_NAME: set_swarm_name (DEE_PEER (object), g_value_get_string (value)); break; case PROP_SWARM_LEADER: g_free (priv->swarm_leader); priv->swarm_leader = g_value_dup_string (value); break; case PROP_SWARM_OWNER: priv->swarm_owner = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_peer_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { case PROP_SWARM_NAME: g_value_set_string (value, DEE_PEER (object)->priv->swarm_name); break; case PROP_SWARM_LEADER: g_value_set_string (value, dee_peer_get_swarm_leader (DEE_PEER (object))); break; case PROP_SWARM_OWNER: g_value_set_boolean (value, DEE_PEER (object)->priv->swarm_owner); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_peer_class_init (DeePeerClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; obj_class->dispose = dee_peer_dispose; obj_class->finalize = dee_peer_finalize; obj_class->set_property = dee_peer_set_property; obj_class->get_property = dee_peer_get_property; obj_class->constructed = dee_peer_constructed; /* Virtual methods */ klass->get_swarm_leader = dee_peer_real_get_swarm_leader; klass->is_swarm_leader = dee_peer_real_is_swarm_leader; klass->get_connections = dee_peer_real_get_connections; klass->list_peers = dee_peer_real_list_peers; /* Add Signals */ /** * DeePeer::peer-found: * @self: the #DeePeer on which the signal is emitted * @name: the DBus name of the object found * * Connect to this signal to be notified of existing and new peers that are * in your swarm. **/ _peer_signals[PEER_FOUND] = g_signal_new ("peer-found", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeePeerClass, peer_found), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * DeePeer::peer-lost: * @self: the #DeePeer on which the signal is emitted * @name: the DBus name of the object that disconnected * * Connect to this signal to be notified when peers disconnect from the swarm **/ _peer_signals[PEER_LOST] = g_signal_new ("peer-lost", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeePeerClass, peer_lost), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * DeePeer::new-connection: * @self: the #DeePeer on which the signal is emitted * @connection: the new #GDBusConnection * * Connect to this signal to be notified when peers connect via * new #GDBusConnection. **/ _peer_signals[CONNECTION_ACQUIRED] = g_signal_new ("connection-acquired", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeePeerClass, connection_acquired), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_DBUS_CONNECTION); /** * DeePeer::connection-closed: * @self: the #DeePeer on which the signal is emitted * @connection: the closed #GDBusConnection * * Connect to this signal to be notified when peers close * their #GDBusConnection. **/ _peer_signals[CONNECTION_CLOSED] = g_signal_new ("connection-closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeePeerClass, connection_closed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_DBUS_CONNECTION); /* Add properties */ /** * DeePeer::swarm-name: * * The name of the swarm that this peer is connected to. All swarm members * will try and own this name on the session bus. The one owning the name * is the swarm leader. */ pspec = g_param_spec_string ("swarm-name", "Swarm Name", "Well-known name to find other peers with", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_SWARM_NAME, pspec); /** * DeePeer::swarm-leader: * * The name of the swarm that this peer is connected to. All swarm members * will try and own this name on the session bus. The one owning the name * is the swarm leader. **/ pspec = g_param_spec_string ("swarm-leader", "Swarm Leader", "Unique DBus address of the swarm leader", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_SWARM_LEADER, pspec); /** * DeePeer::swarm-owner: * * If set, this peer will try to become a leader of the swarm. * * Creating a #DeeSharedModel with a peer that successfully assumes ownership * of a swarm will skip cloning of the model, therefore you need to set * the schema and fill the model with data yourself. * * Setting this property to TRUE does NOT guarantee that this peer will * become a leader. You should always check the :swarm-leader property. **/ pspec = g_param_spec_boolean ("swarm-owner", "Swarm Owner", "Try to assume leadership of the swarm", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_SWARM_OWNER, pspec); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeePeerPrivate)); } static void dee_peer_init (DeePeer *peer) { DeePeerPrivate *priv; priv = peer->priv = DEE_PEER_GET_PRIVATE (peer); priv->swarm_name = NULL; priv->swarm_leader = NULL; priv->own_name = NULL; priv->match_rules = NULL; priv->peers = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); priv->connected = FALSE; priv->is_swarm_leader = FALSE; priv->has_been_leader = FALSE; priv->is_first_name_check = TRUE; priv->list_cancellable = NULL; #if GLIB_CHECK_VERSION(2, 31, 16) g_mutex_init (&priv->lock_real); priv->lock = &priv->lock_real; #else priv->lock = g_mutex_new (); #endif priv->head_count_source = 0; } /* Private Methods */ /* Async callback for com.canonical.Dee.Peer.List */ static void on_list_received (GObject *source_object, GAsyncResult *res, gpointer user_data) { DeePeer *self; DeePeerPrivate *priv; GHashTable *peers, *old_peers_ht; GSList *new_peers, *iter; guint i; GVariant *val, *_val; const gchar **names; gsize n_names; GError *error; GHashTableIter hiter; gpointer hkey, hval; error = NULL; _val = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error); if (error != NULL) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning ("%s: Unable to list peers: %s", G_STRLOC, error->message); } g_error_free (error); return; } g_return_if_fail (DEE_IS_PEER (user_data)); self = DEE_PEER (user_data); priv = self->priv; /* Unpack the wrapping struct from the reply */ val = g_variant_get_child_value (_val, 0); g_variant_unref (_val); names = g_variant_get_strv (val, &n_names); trace_object (self, "Got list of %d peers", n_names); /* We diff the current list of peers against the new list * and emit signals accordingly. New peers are added to new_peers, * and lost peers will remain in priv->peers: */ new_peers = NULL; peers = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); g_mutex_lock (priv->lock); for (i = 0; i < n_names; i++) { g_hash_table_insert (peers, g_strdup (names[i]), NULL); if (!g_hash_table_remove (priv->peers, names[i])) { /* The peer was not previously known */ new_peers = g_slist_prepend (new_peers, (gchar *) names[i]); } } /* Signal about lost peers */ g_hash_table_iter_init (&hiter, priv->peers); while (g_hash_table_iter_next (&hiter, &hkey, &hval)) { g_signal_emit (self, _peer_signals[PEER_LOST], 0, hkey); } old_peers_ht = priv->peers; priv->peers = peers; g_mutex_unlock (priv->lock); /* Signal about new peers */ for (iter = new_peers; iter; iter = iter->next) { emit_peer_found (self, (const gchar*)iter->data); } /* The return value of g_variant_get_strv() is a shallow copy */ g_free (names); g_variant_unref (val); /* Free just the array, not the strings - they are owned by 'peers' now */ g_slist_free (new_peers); g_hash_table_destroy (old_peers_ht); } /* Install a DBus match rule, async, described by a printf-like format string. * The match rule will be properly retracted when @self is finalized */ static void install_match_rule (DeePeer *self, const char *rule, ...) { DeePeerPrivate *priv; gchar *f_rule; va_list args; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (rule != NULL); priv = self->priv; va_start (args, rule); f_rule = g_strdup_vprintf (rule, args); va_end (args); /* By setting the error argument to NULL libdbus will use async mode * for adding the match rule. We want that. */ g_dbus_connection_call (priv->connection, "org.freedesktop.DBus", "/org/freedesktop/dbus", "org.freedesktop.DBus", "AddMatch", g_variant_new ("(s)", f_rule), NULL, /* reply type */ G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* cancellable */ NULL, /* callback */ NULL); /* user_data */ priv->match_rules = g_slist_prepend (priv->match_rules, f_rule); } static void remove_match_rule (GDBusConnection *conn, const gchar *rule) { g_dbus_connection_call (conn, "org.freedesktop.DBus", "/org/freedesktop/dbus", "org.freedesktop.DBus", "RemoveMatch", g_variant_new ("(s)", rule), NULL, /* reply type */ G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* cancellable */ NULL, /* callback */ NULL); /* user_data */ } static const gchar* dee_peer_real_get_swarm_leader (DeePeer *self) { return self->priv->swarm_leader; } static gboolean dee_peer_real_is_swarm_leader (DeePeer *self) { return self->priv->is_swarm_leader; } static GSList* dee_peer_real_get_connections (DeePeer *self) { GSList *list = NULL; if (self->priv->connection) { list = g_slist_append (list, self->priv->connection); } return list; } static gchar** dee_peer_real_list_peers (DeePeer *self) { DeePeerPrivate *priv; GHashTableIter iter; gpointer key, value; gchar **result; int i; priv = self->priv; i = 0; g_mutex_lock (priv->lock); result = g_new (gchar*, g_hash_table_size (priv->peers) + 1); g_hash_table_iter_init (&iter, priv->peers); while (g_hash_table_iter_next (&iter, &key, &value)) { result[i++] = g_strdup ((gchar*) key); } g_mutex_unlock (priv->lock); result[i] = NULL; return result; } /* Public Methods */ /** * dee_peer_new: * @swarm_name: The name of the swarm to join. * Fx "org.example.DataProviders" * * Create a new #DeePeer. The peer will immediately connect to the swarm * and start the peer discovery. * * Return value: (transfer full): A newly constructed #DeePeer. * Free with g_object_unref(). */ DeePeer* dee_peer_new (const gchar* swarm_name) { g_return_val_if_fail (swarm_name != NULL, NULL); return g_object_new (DEE_TYPE_PEER, "swarm-name", swarm_name, NULL); } /** * dee_peer_is_swarm_leader: * @self: a #DeePeer * * Return value: %TRUE if and only if this peer owns the swarm name on * the session bus */ gboolean dee_peer_is_swarm_leader (DeePeer *self) { g_return_val_if_fail (DEE_IS_PEER (self), FALSE); DeePeerClass *klass = DEE_PEER_GET_CLASS (self); return klass->is_swarm_leader (self); } /** * dee_peer_get_swarm_leader: * @self: a #DeePeer * * In case this peer is connected to a message bus, gets the unique DBus * address of the current swarm leader, otherwise returns id of the leader. * * Return value: Unique DBus address of the current swarm leader, * possibly %NULL if the leader has not been detected yet */ const gchar* dee_peer_get_swarm_leader (DeePeer *self) { g_return_val_if_fail (DEE_IS_PEER (self), NULL); DeePeerClass *klass = DEE_PEER_GET_CLASS (self); return klass->get_swarm_leader (self); } /** * dee_peer_get_swarm_name: * @self: a #DeePeer * * Gets the unique name for this swarm. The swarm leader is the Peer owning * this name on the session bus. * * Return value: The swarm name **/ const gchar* dee_peer_get_swarm_name (DeePeer *self) { g_return_val_if_fail (DEE_IS_PEER (self), NULL); return self->priv->swarm_name; } /** * dee_peer_get_connections: * @self: a #DeePeer * * Gets list of #GDBusConnection instances used by this #DeePeer instance. * * Return value: (transfer container) (element-type Gio.DBusConnection): * List of connections. */ GSList* dee_peer_get_connections (DeePeer *self) { g_return_val_if_fail (DEE_IS_PEER (self), NULL); DeePeerClass *klass = DEE_PEER_GET_CLASS (self); return klass->get_connections (self); } /** * dee_peer_list_peers: * @self: a #DeePeer * * Gets list of all peers currently in this swarm. * * Return value: (transfer full): List of peers (free using g_strfreev()). */ gchar** dee_peer_list_peers (DeePeer *self) { g_return_val_if_fail (DEE_IS_PEER (self), NULL); DeePeerClass *klass = DEE_PEER_GET_CLASS (self); return klass->list_peers (self); } /** * dee_peer_is_swarm_owner: * @self: a #DeePeer * * Gets the value of the :swarm-owner property. * * Note that this does NOT mean that the peer is leader of the swarm! Check also * dee_peer_is_swarm_leader(). * * Return value: TRUE if the :swarm-owner property was set during construction. */ gboolean dee_peer_is_swarm_owner (DeePeer *self) { g_return_val_if_fail (DEE_IS_PEER (self), FALSE); return self->priv->swarm_owner; } static void emit_peer_found (DeePeer *self, const gchar *name) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER(self)); g_return_if_fail (name != NULL); priv = self->priv; if (!g_str_equal (name, priv->own_name)) { g_signal_emit (self, _peer_signals[PEER_FOUND], 0, name); } } static void set_swarm_name (DeePeer *self, const gchar *swarm_name) { DeePeerPrivate *priv; gchar *dummy; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (swarm_name != NULL); priv = self->priv; if (priv->swarm_name) { g_warning ("%s: Unable to set previously set swarm_name (%s) to (%s)", G_STRLOC, priv->swarm_name, swarm_name); return; } /* If swarm_name is org.example.MyService then the swarm_path will * become /com/canonical/dee/org/example/MyService. Note that * the actual object path of the peer is not used in the Swarm spec */ priv->swarm_name = g_strdup (swarm_name); dummy = g_strdelimit (g_strdup(swarm_name), ".", '/'); priv->swarm_path = g_strdup_printf ("/com/canonical/dee/peer/%s", dummy); g_free (dummy); } static void dispose_weak_ref (gpointer data) { GWeakRef *weak_ref = (GWeakRef*) data; g_weak_ref_clear (weak_ref); g_free (data); } /* Called when we get the bus connection the first time. */ static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { DeePeer *self; DeePeerPrivate *priv; GWeakRef *weak_ref; GPtrArray *ptr_array; g_return_if_fail (DEE_IS_PEER (user_data)); self = DEE_PEER (user_data); priv = self->priv; priv->connection = g_object_ref (connection); priv->own_name = g_strdup (g_dbus_connection_get_unique_name (connection)); g_signal_emit (self, _peer_signals[CONNECTION_ACQUIRED], 0, priv->connection); /* Using GPtrArray as a ref-count container for the weak ref */ weak_ref = (GWeakRef*) g_new (GWeakRef, 1); g_weak_ref_init (weak_ref, self); ptr_array = g_ptr_array_new_full (1, dispose_weak_ref); g_ptr_array_add (ptr_array, weak_ref); /* FIXME: the last param should be g_ptr_array_unref, but there's a bug * in gio that can cause a crash, we'll rather have a small leak than * random crashes. * https://bugzilla.gnome.org/show_bug.cgi?id=704568 */ priv->filter_id = g_dbus_connection_add_filter (priv->connection, gdbus_message_filter, ptr_array, NULL); /* Detect when someone joins the swarm */ install_match_rule (self, "interface='org.freedesktop.DBus'," "member='RequestName'," "arg0='%s'", priv->swarm_name); /* Listen for all signals on the Dee interface concerning this swarm */ priv->dbus_signals_id = g_dbus_connection_signal_subscribe(priv->connection, NULL, /* sender */ DEE_PEER_DBUS_IFACE, /* iface */ NULL, /* member */ NULL, /* object path */ priv->swarm_name, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, on_dbus_peer_signal, /* callback */ self, /* user_data */ NULL); /* user data free */ } static void assume_leadership (DeePeer *self) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); priv = self->priv; if (priv->is_swarm_leader) { trace_object (self, "Leadership acquired, but we are already leaders"); } else { trace_object (self, "Got swarm leadership"); /* The first time we become leaders we install a broad match rule * that triggers any time someone drops off the bus. * Please note that we don't bother cleaning up that rule in the * rare case we loose leadership (which only happens if someone * forcefully grabs the swarm name) */ if (!priv->has_been_leader) { install_match_rule (self, "interface='org.freedesktop.DBus'," "member='NameOwnerChanged'," "arg2=''"); } priv->is_swarm_leader = TRUE; priv->has_been_leader = TRUE; g_free (priv->swarm_leader); priv->swarm_leader = g_strdup (priv->own_name); /* Emit a Ping so we can do a head count */ emit_ping (self); /* Signal emission must be a "tail call" * because we can in theory be finalized by * one of the callbacks */ g_object_notify (G_OBJECT (self), "swarm-leader"); } } /* GDBus callback from the name owner installed with g_bus_own_name(). * Called when losing leadership. */ static void on_leadership_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { DeePeer *self; DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (user_data)); self = DEE_PEER (user_data); priv = self->priv; if (priv->is_swarm_leader) { /* We signal the change of swarm leadership in on_ping_received(), * only at that point do we know the unique name of the new leader */ trace_object (self, "Lost swarm leadership"); // FIXME. We ought to remove the Pong match rule, but it's not paramount priv->is_swarm_leader = FALSE; } else { trace_object (self, "Did not become leader"); } /* If this is the first time we are notified that we are not the leader * then request a roster from the leader */ if (priv->is_first_name_check) { trace_object (self, "Requesting peer roster from leader"); if (priv->list_cancellable) { g_cancellable_cancel (priv->list_cancellable); g_object_unref (priv->list_cancellable); } priv->list_cancellable = g_cancellable_new (); g_dbus_connection_call (priv->connection, priv->swarm_name, priv->swarm_path, DEE_PEER_DBUS_IFACE, "List", g_variant_new ("()"), NULL, /* reply type */ G_DBUS_CALL_FLAGS_NONE, -1, priv->list_cancellable, /* cancellable */ on_list_received, /* callback */ self); /* user_data */ priv->is_first_name_check = FALSE; } } /* GDBus callback from the name owner installed with g_bus_own_name(). * Called when we become leaders. */ static void on_leadership_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_return_if_fail (DEE_IS_PEER (user_data)); assume_leadership (DEE_PEER (user_data)); } /* Callback from the GDBus name watcher installed with g_bus_watch_name() */ static void on_leadership_changed (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { DeePeer *self; DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (user_data)); self = DEE_PEER (user_data); priv = self->priv; /* Don't bother if we already know this leader */ if (g_strcmp0 (priv->swarm_leader, name_owner) == 0) return; /* At this point we assume we have a new leader */ if (g_strcmp0 (priv->own_name, name_owner) == 0) assume_leadership (self); else { g_free (priv->swarm_leader); priv->swarm_leader = g_strdup (name_owner); priv->is_swarm_leader = FALSE; g_object_notify (G_OBJECT (self), "swarm-leader"); } } /* Callback from gdbus_message_filter() for custom match rules * Indicates that @peer_address joined the swarm. * This method is thread safe */ static void on_join_received (DeePeer *self, const gchar *peer_address) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (peer_address != NULL); trace_object (self, "Found peer %s", peer_address); priv = self->priv; /* If the peer is already known it must have tried to acquire the swarm name * twice... Just ignore it */ g_mutex_lock (priv->lock); if (g_hash_table_lookup_extended (priv->peers, peer_address, NULL, NULL)) { g_mutex_unlock (priv->lock); return; } g_hash_table_insert (priv->peers, g_strdup (peer_address), NULL); g_mutex_unlock (priv->lock); emit_peer_found (self, peer_address); } /* Callback from _gdbus_message_filter() for custom match rules * Indicates that @peer_address left the swarm */ static void on_bye_received (DeePeer *self, const gchar *peer_address) { DeePeerPrivate *priv; gboolean removed; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (peer_address != NULL); trace_object (self, "Bye %s", peer_address); priv = self->priv; g_mutex_lock (priv->lock); removed = g_hash_table_remove (self->priv->peers, peer_address); g_mutex_unlock (priv->lock); if (removed) { trace_object (self, "Leader said Bye to %s", peer_address); g_signal_emit (self, _peer_signals[PEER_LOST], 0, peer_address); } else { trace_object (self, "Unknown peer '%s' dropped out of the swarm", peer_address); } } /* Broadcast a Bye signal to to notify the swarm that someone left. * Only call this method as swarm leader - that's the contract * of the Swarm spec. * This method is thread safe */ static void emit_bye (DeePeer *self, const gchar *peer_address) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (self->priv->is_swarm_leader); g_return_if_fail (self->priv->connection != NULL); g_return_if_fail (peer_address != NULL); trace_object (self, "Emit Bye(%s)", peer_address); g_signal_emit (self, _peer_signals[PEER_LOST], 0, peer_address); priv = self->priv; g_dbus_connection_emit_signal (priv->connection, NULL, /* destination */ priv->swarm_path, /* object path */ DEE_PEER_DBUS_IFACE, /* interface */ "Bye", /* signal name */ g_variant_new ("(ss)", priv->swarm_name, peer_address), NULL); /* error */ } /* Timeout started when we receive a Ping. When this timeout triggers * we do a diff of priv->head_count and priv->peers and emit peer-lost * as appropriate. We don't need to emit peer-found because that is already * done in when receiving the Pongs from the peers */ static gboolean on_head_count_complete (DeePeer *self) { DeePeerPrivate *priv; GHashTable *new_peers; gpointer hkey, hval; GHashTableIter hiter; GSList *iter; g_return_val_if_fail (DEE_IS_PEER (self), FALSE); priv = self->priv; /* First we build a new_peers hash set with the names of the * head counted peers. Then we diff the old and the new peers * sets and emit peer-lost appropriately */ new_peers = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); /* Build new_peers hash set */ iter = priv->head_count; for (iter = priv->head_count; iter; iter = iter->next) { g_hash_table_insert (new_peers, g_strdup (iter->data), NULL); } /* Emit peer-lost and Bye on peers that didn't emit Pong in due time */ g_mutex_lock (priv->lock); g_hash_table_iter_init (&hiter, priv->peers); while (g_hash_table_iter_next (&hiter, &hkey, &hval)) { if (!g_hash_table_lookup_extended (new_peers, hkey, NULL, NULL)) { if (priv->is_swarm_leader) emit_bye (self, hkey); else g_signal_emit (self, _peer_signals[PEER_LOST], 0, hkey); } } /* Swap old and new peers hash sets */ g_hash_table_destroy (priv->peers); priv->peers = new_peers; g_mutex_unlock (priv->lock); /* Unregister the head count timeout source. And reset head counting mode */ priv->head_count_source = 0; g_slist_foreach (priv->head_count, (GFunc) g_free, NULL); g_slist_free (priv->head_count); priv->head_count = NULL; return FALSE; } /* Indicates that @leader_address send a Ping to the swarm to * initiate a head count */ static void on_ping_received (DeePeer *self, const gchar *leader_address) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (leader_address != NULL); priv = self->priv; trace_object (self, "Got Ping from: %s", leader_address); /* When we receive a Ping (and note that the swarm leader will receive its * own Ping as well) we enter a "head count mode" where we will consider * all peers that haven't given us a Pong within a certain timeout for lost. * * We indicate that we are in head counting mode by setting * priv->head_count != NULL */ if (priv->head_count) { g_slist_foreach (priv->head_count, (GFunc) g_free, NULL); g_slist_free (priv->head_count); } priv->head_count = g_slist_prepend (NULL, g_strdup (priv->own_name)); if (priv->head_count_source != 0) { /* Reset the timer if we got another ping */ g_source_remove (priv->head_count_source); } priv->head_count_source = g_timeout_add (500, (GSourceFunc) on_head_count_complete, self); /* The DeePeer protocol says we must respond with a Pong to any Ping */ emit_pong(self); } /* Indicates that @peer_address has emitted a Pong */ static void on_pong_received (DeePeer *self, const gchar *peer_address) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (peer_address != NULL); priv = self->priv; trace_object (self, "Got pong %s", peer_address); g_mutex_lock (priv->lock); if (!g_hash_table_lookup_extended (priv->peers, peer_address, NULL, NULL)) { g_hash_table_insert (priv->peers, g_strdup (peer_address), NULL); emit_peer_found (self, peer_address); } g_mutex_unlock (priv->lock); /* If we are in head counting mode register this Ping */ if (priv->head_count) priv->head_count = g_slist_prepend (priv->head_count, g_strdup (peer_address)); } static void on_dbus_peer_signal (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { DeePeer *self; gchar *peer_address = NULL; g_return_if_fail (DEE_IS_PEER (user_data)); self = DEE_PEER (user_data); if (g_strcmp0 ("Bye", signal_name) == 0) { g_variant_get (parameters, "(ss)", NULL, &peer_address); on_bye_received (self, peer_address); } else if (g_strcmp0 ("Ping", signal_name) == 0) on_ping_received (self, sender_name); else if (g_strcmp0 ("Pong", signal_name) == 0) on_pong_received (self, sender_name); else g_critical ("Unexpected signal from peer %s: %s.%s", sender_name, interface_name, signal_name); } /* Broadcast a Ping signal to do a head-count on the swarm. * Only call this method as swarm leader - that's the contract * of the Swarm spec. * This method is thread safe */ static void emit_ping (DeePeer *self) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (self->priv->is_swarm_leader); g_return_if_fail (self->priv->connection != NULL); trace_object (self, "Emit ping"); priv = self->priv; g_dbus_connection_emit_signal (priv->connection, NULL, /* destination */ priv->swarm_path, /* object path */ DEE_PEER_DBUS_IFACE, /* interface */ "Ping", /* signal name */ g_variant_new ("(s)", priv->swarm_name), NULL); /* error */ } /* Broadcast a Pong signal as a response to a Ping. * This method is thread safe */ static void emit_pong (DeePeer *self) { DeePeerPrivate *priv; g_return_if_fail (DEE_IS_PEER (self)); g_return_if_fail (self->priv->connection != NULL); trace_object (self, "Emit pong"); priv = self->priv; g_dbus_connection_emit_signal (priv->connection, NULL, /* destination */ priv->swarm_path, /* object path */ DEE_PEER_DBUS_IFACE, /* interface */ "Pong", /* signal name */ g_variant_new ("(s)", priv->swarm_name), NULL); /* error */ } /* Return floating variant of type '(as)' with unique DBus names of all peers. * This method is thread safe */ static GVariant* build_peer_list (DeePeer *self) { DeePeerPrivate *priv; GHashTableIter iter; GVariantBuilder b; gpointer key, val; g_return_val_if_fail (DEE_IS_PEER (self), FALSE); priv = self->priv; g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)")); g_variant_builder_open (&b, G_VARIANT_TYPE ("as")); g_mutex_lock (priv->lock); g_hash_table_iter_init (&iter, priv->peers); while (g_hash_table_iter_next (&iter, &key, &val)) { g_variant_builder_add (&b, "s", key); } g_mutex_unlock (priv->lock); g_variant_builder_close (&b); return g_variant_builder_end (&b); } /* This method is thread safe */ static gboolean check_method (GDBusMessage *msg, const gchar *iface, const gchar *member, const gchar *path) { return msg != NULL && G_DBUS_MESSAGE_TYPE_METHOD_CALL == g_dbus_message_get_message_type (msg) && (iface == NULL || g_strcmp0 (g_dbus_message_get_interface (msg), iface) == 0) && (member == NULL || g_strcmp0 (g_dbus_message_get_member (msg), member) == 0) && (path == NULL || g_strcmp0 (g_dbus_message_get_path (msg), path) == 0); } /* This method is thread safe */ static gboolean check_signal (GDBusMessage *msg, const gchar *iface, const gchar *member, const gchar *path) { return msg != NULL && G_DBUS_MESSAGE_TYPE_SIGNAL == g_dbus_message_get_message_type (msg) && (iface == NULL || g_strcmp0 (g_dbus_message_get_interface (msg), iface) == 0) && (member == NULL || g_strcmp0 (g_dbus_message_get_member (msg), member) == 0) && (path == NULL || g_strcmp0 (g_dbus_message_get_path (msg), path) == 0); } /* Used to transfer data to the mainloop. * Use only for good, not evil, and only from gdbus_message_filter() */ static gboolean transfer_to_mainloop (gpointer *args) { GPtrArray *ptr_array; GWeakRef *weak_ref; GObject *object; GFunc cb = (GFunc) args[0]; ptr_array = (GPtrArray*) args[1]; weak_ref = (GWeakRef*) g_ptr_array_index (ptr_array, 0); object = (GObject*) g_weak_ref_get (weak_ref); if (object != NULL) { cb (object, args[2]); g_object_unref (object); } g_ptr_array_unref (ptr_array); g_free (args[2]); g_free (args); return FALSE; } /* Callback applied to all incoming DBus messages. We use this to grab * messages for our match rules and dispatch to the right on_*_received * function. * WARNING: This callback is run in the GDBus message handling thread - * and NOT in the mainloop! */ static GDBusMessage* gdbus_message_filter (GDBusConnection *connection, GDBusMessage *msg, gboolean incoming, gpointer user_data) { DeePeer *self; DeePeerPrivate *priv; GVariant *body; GDBusMessageType msg_type; const gchar *sender_address; gpointer *data; GPtrArray *ptr_array; GWeakRef *weak_ref; ptr_array = (GPtrArray*) user_data; weak_ref = (GWeakRef*) g_ptr_array_index (ptr_array, 0); body = g_dbus_message_get_body (msg); sender_address = g_dbus_message_get_sender (msg); msg_type = g_dbus_message_get_message_type (msg); /* We have no business with outgoing messages */ if (!incoming) return msg; /* We're only interested in method calls and signals */ if (msg_type != G_DBUS_MESSAGE_TYPE_METHOD_CALL && msg_type != G_DBUS_MESSAGE_TYPE_SIGNAL) return msg; /*trace ("FILTER: %p", user_data); trace ("Msg filter: From: %s, Iface: %s, Member: %s", dbus_message_get_sender (msg), dbus_message_get_interface (msg), dbus_message_get_member (msg));*/ /* Important note: Apps consuming this lib will likely install custom match * rules which will trigger this filter. Hence we must do very * strict matching before we dispatch our methods */ if (check_method (msg, "org.freedesktop.DBus", "RequestName", NULL) && g_strcmp0 (sender_address, g_dbus_connection_get_unique_name (connection)) != 0 && body != NULL) { gchar *swarm_name; self = (DeePeer*) g_weak_ref_get (weak_ref); if (self == NULL) return msg; priv = self->priv; g_variant_get (body, "(su)", &swarm_name, NULL); if (g_strcmp0 (swarm_name, priv->swarm_name) == 0) { /* Call on_join_received() in the main loop */ data = g_new (gpointer, 3); data[0] = on_join_received; data[1] = g_ptr_array_ref (ptr_array); data[2] = g_strdup (sender_address); g_idle_add ((GSourceFunc) transfer_to_mainloop, data); } g_object_unref (self); g_free (swarm_name); } else if (check_signal (msg, "org.freedesktop.DBus", "NameOwnerChanged", NULL) && body != NULL) { gchar *old_address, *new_address, *peer_address; gboolean should_emit_bye; self = (DeePeer*) g_weak_ref_get (weak_ref); if (self == NULL) return msg; priv = self->priv; g_variant_get (body, "(sss)", &peer_address, &old_address, &new_address); /* Check if a known peer dropped off the bus and emit the Bye signal * if we are the swarm leaders */ g_mutex_lock (priv->lock); should_emit_bye = priv->is_swarm_leader && g_strcmp0 (peer_address, old_address) == 0 && g_strcmp0 (new_address, "") == 0 && g_strcmp0 (peer_address, g_dbus_connection_get_unique_name (connection)) != 0 && g_hash_table_lookup_extended (priv->peers, peer_address, NULL, NULL); g_mutex_unlock (priv->lock); if (should_emit_bye) { /* Call emit_bye() in the main loop */ data = g_new (gpointer, 3); data[0] = emit_bye; data[1] = g_ptr_array_ref (ptr_array); data[2] = peer_address; // own g_idle_add ((GSourceFunc) transfer_to_mainloop, data); peer_address = NULL; } g_object_unref (self); g_free (old_address); g_free (new_address); g_free (peer_address); } else { self = (DeePeer*) g_weak_ref_get (weak_ref); if (self == NULL) return msg; priv = self->priv; if (check_method (msg, DEE_PEER_DBUS_IFACE, "List", priv->swarm_path)) { /* We don't want to go through the whole GDBus * interface/introspection setup just to export the List method. * We just handle this particular method inline */ GDBusMessage *reply; reply = g_dbus_message_new_method_reply (msg); g_dbus_message_set_body (reply, build_peer_list (self)); g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, /* out serial */ NULL); /* error */ g_object_unref (reply); g_object_unref (self); /* Convince GDBus that we handled this message by returning NULL */ return NULL; } g_object_unref (self); } return msg; } dee-1.2.7+15.04.20150304/src/dee-model.c0000644000015300001610000024563612475676210017425 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * * NB: Inspiration for column storage taken from GtkListStore * API inspired by ClutterModel by Matthew Allumn * Neil Patel * Emmanuele Bassi */ /** * SECTION:dee-model * @short_description: A generic table model interface * @include: dee.h * * #DeeModel is a generic table model that can holds #GVariants as * column values. Each column is restricted to hold variants with some * predefined type signature. This is known as the * column schema. * * * Indexes - Access by Key or Full Text Analysis * * Instead of forcing you to search the rows and columns for given values * or patterns #DeeModel is integrated with a powerful #DeeIndex that allows * you to create custom indexes over the model content that are updated * automatically as the model changes. * * * Indexes can be created for integer keys, string keys (fx. URIs), or for * full text search into the model contents. The indexing API is flexible * and extensible and can provide huge optimizations in terms of access times * if you find yourself iterating over the model searching for something. * * * * * Sorting * * As a simpler alternative to using indexes you can rely on sorted models. * This is done by using the dee_model_insert_sorted() and * dee_model_find_sorted() family of APIs. Some model classes have * accelerated implementations of sorted inserts and lookups. * Notably #DeeSequenceModel. * * * * * Tags - Attach Arbitrary Data to Rows * * It's a very common pattern that you want to render a #DeeModel into some * view in a classinc MVC pattern. If the view needs to reflect changes in the * model dynamically you often find yourself creating ad-hoc mappings between * the rows of the model and the widgets in your view. * * * In situations where you need to pair the rows in a model with some external * data structure the tags API may come in handy. * It consists of the functions dee_model_register_tag(), dee_model_set_tag(), * dee_model_get_tag(), and dee_model_clear_tag(). * * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "dee-model.h" #include "dee-marshal.h" #include "trace-log.h" typedef DeeModelIface DeeModelInterface; G_DEFINE_INTERFACE (DeeModel, dee_model, G_TYPE_OBJECT) enum { /* Public signals */ DEE_MODEL_SIGNAL_ROW_ADDED, DEE_MODEL_SIGNAL_ROW_REMOVED, DEE_MODEL_SIGNAL_ROW_CHANGED, DEE_MODEL_SIGNAL_CHANGESET_STARTED, DEE_MODEL_SIGNAL_CHANGESET_FINISHED, DEE_MODEL_LAST_SIGNAL }; static guint32 dee_model_signals[DEE_MODEL_LAST_SIGNAL] = { 0 }; #define CHECK_SCHEMA(self,out_num_cols,return_expression) \ if (G_UNLIKELY (dee_model_get_schema (self, out_num_cols) == NULL)) \ { \ g_critical ("The model %s@%p doesn't have a schema", \ G_OBJECT_TYPE_NAME (self), self); \ return_expression; \ } static void dee_model_set_schema_valist (DeeModel *self, va_list *args); static void dee_model_set_column_names_valist (DeeModel *self, const gchar *first_column_name, va_list *args); static DeeModelIter* dee_model_append_valist (DeeModel *self, va_list *args); static DeeModelIter* dee_model_prepend_valist (DeeModel *self, va_list *args); static DeeModelIter* dee_model_insert_valist (DeeModel *self, guint pos, va_list *args); static DeeModelIter* dee_model_insert_before_valist (DeeModel *self, DeeModelIter *iter, va_list *args); static void dee_model_set_valist (DeeModel *self, DeeModelIter *iter, va_list *args); static void dee_model_get_valist (DeeModel *self, DeeModelIter *iter, va_list args); static GVariant** dee_model_build_row_valist (DeeModel *self, GVariant **out_row_members, va_list *args); /* * We provide here a couple of DeeModelIter functions, so that they're usable * from introspected languages. */ static gpointer dee_model_iter_copy (gpointer boxed) { /* FIXME: this implementation will work fine with DeeSequenceModel, but what * about others? */ return boxed; } static void dee_model_iter_free (gpointer boxed) { } GType dee_model_iter_get_type (void) { static GType dee_model_iter_type = 0; if (dee_model_iter_type == 0) { dee_model_iter_type = g_boxed_type_register_static ("DeeModelIter", dee_model_iter_copy, dee_model_iter_free); } return dee_model_iter_type; } static void dee_model_default_init (DeeModelInterface *klass) { /** * DeeModel::row-added: * @self: the #DeeModel on which the signal is emitted * @iter: (transfer none) (type Dee.ModelIter): a #DeeModelIter pointing to the newly added row * * Connect to this signal to be notified when a row is added to @self. **/ dee_model_signals[DEE_MODEL_SIGNAL_ROW_ADDED] = g_signal_new ("row-added", DEE_TYPE_MODEL, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeeModelIface,row_added), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DEE_TYPE_MODEL_ITER); /** * DeeModel::row-removed: * @self: the #DeeModel on which the signal is emitted * @iter: (transfer none) (type Dee.ModelIter): a #DeeModelIter pointing to the removed row * * Connect to this signal to be notified when a row is removed from @self. * The row is still valid while the signal is being emitted. **/ dee_model_signals[DEE_MODEL_SIGNAL_ROW_REMOVED] = g_signal_new ("row-removed", DEE_TYPE_MODEL, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeeModelIface,row_removed), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DEE_TYPE_MODEL_ITER); /** * DeeModel::row-changed: * @self: the #DeeModel on which the signal is emitted * @iter: (transfer none) (type Dee.ModelIter): a #DeeModelIter pointing to the changed row * * Connect to this signal to be notified when a row is changed. **/ dee_model_signals[DEE_MODEL_SIGNAL_ROW_CHANGED] = g_signal_new ("row-changed", DEE_TYPE_MODEL, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeeModelIface,row_changed), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DEE_TYPE_MODEL_ITER); /** * DeeModel::changeset-started * @self: the #DeeModel on which the signal is emitted * * Connect to this signal to be notified when a changeset that can contain * multiple row additions / changes / removals is about to be committed * to the model. * Note that not all model implementations use the changeset approach and * you might still get a row change signal outside of changeset-started and * changeset-finished signals. It also isn't guaranteed that a changeset * would always be non-empty. */ dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_STARTED] = g_signal_new ("changeset-started", DEE_TYPE_MODEL, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeeModelIface, changeset_started), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * DeeModel::changeset-finished * @self: the #DeeModel on which the signal is emitted * * Connect to this signal to be notified when a changeset that can contain * multiple row additions / changes / removals has been committed * to the model. */ dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_FINISHED] = g_signal_new ("changeset-finished", DEE_TYPE_MODEL, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (DeeModelIface, changeset_finished), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /** * dee_model_set_schema: * @self: The #DeeModel to set the column layout for * @VarArgs: A list of #GVariant type strings terminated by a %NULL * * Set the #GVariant types and the number of columns used by @self. * This method must be called exactly once before using @self. Note that * some constructors will do this for you. * * To create a model with three columns; a 32 bit integer, a string, * and lastly an array of strings, you would do: * * DeeModel *model; * model = dee_sequence_model_new (); * dee_model_set_schema (model, "i", "s", "as", NULL); * */ void dee_model_set_schema (DeeModel *self, ...) { va_list args; g_return_if_fail (DEE_IS_MODEL (self)); va_start (args, self); dee_model_set_schema_valist(self, &args); va_end (args); } /** * dee_model_set_schema_valist: (skip) * @self: The #DeeModel to change * @VarArgs: A list of #GVariant type strings terminated by a %NULL * * Like dee_model_set_schema() but for language bindings. */ static void dee_model_set_schema_valist (DeeModel *self, va_list *args) { DeeModelIface *iface; GSList *columns, *iter; const gchar *column_schema; gchar **column_schemas; guint n_columns, i; g_return_if_fail (DEE_IS_MODEL (self)); /* Extract and validate the column schema strings from the va_list */ column_schema = va_arg (*args, const gchar*); n_columns = 0; columns = NULL; while (column_schema != NULL) { if (!g_variant_type_string_is_valid (column_schema)) { g_critical ("When setting schema for DeeModel %p: '%s' is not a " "valid GVariant type string", self, column_schema); return; } columns = g_slist_prepend (columns, g_strdup (column_schema)); column_schema = va_arg (*args, const gchar*); n_columns++; } /* Construct a string array with the validated column schemas */ columns = g_slist_reverse (columns); column_schemas = g_new0 (gchar*, n_columns + 1); for ((i = 0, iter = columns); iter; (i++, iter = iter->next)) { column_schemas[i] = iter->data; // steal the duped type string } #ifdef ENABLE_TRACE_LOG gchar* schema = g_strjoinv (", ", column_schemas); trace_object (self, "Set schema: (%s)", schema); g_free (schema); #endif iface = DEE_MODEL_GET_IFACE (self); (* iface->set_schema_full) (self, (const gchar**) column_schemas, n_columns); g_slist_free (columns); g_strfreev (column_schemas); } /** * dee_model_set_schema_full: * @self: The #DeeModel to set the column layout for * @column_schemas: (array length=num_columns zero-terminated=1) (element-type utf8) (transfer none): A list of #GVariant type strings terminated by a %NULL * @num_columns: an integer specifying the array length for @VarArgs * * Set the #GVariant types and the number of columns used by @self. * This method must be called exactly once before using @self. Note that * some constructors will do this for you. */ void dee_model_set_schema_full (DeeModel *self, const gchar* const *column_schemas, guint num_columns) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); if (dee_model_get_schema (self, NULL) != NULL) { g_critical ("The model %s@%p already has a schema set", G_OBJECT_TYPE_NAME (self), self); return; } iface = DEE_MODEL_GET_IFACE (self); (* iface->set_schema_full) (self, column_schemas, num_columns); } void dee_model_set_column_names (DeeModel *self, const gchar *first_column_name, ...) { va_list args; g_return_if_fail (DEE_IS_MODEL (self)); va_start (args, first_column_name); dee_model_set_column_names_valist (self, first_column_name, &args); va_end (args); } static void dee_model_set_column_names_valist (DeeModel *self, const gchar *first_column_name, va_list *args) { DeeModelIface *iface; const gchar **column_names; guint n_columns, i; g_return_if_fail (DEE_IS_MODEL (self)); n_columns = dee_model_get_n_columns (self); g_return_if_fail (n_columns != 0); column_names = g_alloca (sizeof (gchar*) * n_columns); column_names[0] = first_column_name; i = 1; /* Extract and validate the column schema strings from the va_list */ while (i < n_columns) { gchar *name = va_arg (*args, gchar*); column_names[i++] = name; if (name == NULL) break; } iface = DEE_MODEL_GET_IFACE (self); (* iface->set_column_names_full) (self, column_names, i); } /** * dee_model_set_column_names_full: * @self: A #DeeModel. * @column_names: (array length=num_columns zero-terminated=1) (element-type utf8) (transfer none): A list of column names terminated by a %NULL * @num_columns: an integer specifying the array length for @annotations * * Set column names used by @self. * This method must be called exactly once, but only after setting * a schema of the model. Note that some constructors will do this for you. */ void dee_model_set_column_names_full (DeeModel *self, const gchar **column_names, guint num_columns) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); if (dee_model_get_schema (self, NULL) == NULL) { g_critical ("The model %s@%p doesn't have a schema set", G_OBJECT_TYPE_NAME (self), self); return; } iface = DEE_MODEL_GET_IFACE (self); (* iface->set_column_names_full) (self, column_names, num_columns); } /** * dee_model_get_column_names: * @self: The #DeeModel to get the the schema for * @num_columns: (out) (allow-none): Address of an integer in which to store the * number of columns in @self. Or %NULL to ignore the array length. * * Get a %NULL-terminated array of column names for the columns of @self. * These names can be used in calls to dee_model_build_named_row(). * * Returns: (array length=num_columns) (element-type utf8) (transfer none): * A %NULL-terminated array of #GVariant type strings. The length of * the returned array is written to @num_columns. The returned array * should not be freed or modified. It is owned by the model. */ const gchar** dee_model_get_column_names (DeeModel *self, guint *num_columns) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_column_names) (self, num_columns); } /** * dee_model_get_schema: * @self: The #DeeModel to get the the schema for * @num_columns: (out) (allow-none): Address of an integer in which to store the * number of columns in @self. Or %NULL to ignore the array length. * * Get a %NULL-terminated array of #GVariant type strings that defines the * required formats for the columns of @self. * * Returns: (array length=num_columns) (element-type utf8) (transfer none): * A %NULL-terminated array of #GVariant type strings. The length of * the returned array is written to @num_columns. The returned array * should not be freed or modified. It is owned by the model. */ const gchar* const* dee_model_get_schema (DeeModel *self, guint *num_columns) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_schema) (self, num_columns); } /** * dee_model_get_column_schema: * @self: a #DeeModel * @column: the column to get retrieve the #GVariant type string of * * Get the #GVariant signature of a column * * Return value: the #GVariant signature of the column at index @column */ const gchar* dee_model_get_column_schema (DeeModel *self, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_column_schema) (self, column); } /** * dee_model_get_field_schema: * @self: a #DeeModel * @field_name: name of vardict field to get schema of * @out_column: (out): column index of the associated vardict * * Get the #GVariant signature of field previously registered with * dee_model_register_vardict_schema(). * * Return value: the #GVariant signature for the field, or %NULL if given field * wasn't registered with dee_model_register_vardict_schema(). */ const gchar* dee_model_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_field_schema) (self, field_name, out_column); } /** * dee_model_get_column_index: * @self: a #DeeModel * @column_name: the column name to retrieve the index of * * Get the column index of a column. * * Return value: 0-based index of the column or -1 if column with this name * wasn't found */ gint dee_model_get_column_index (DeeModel *self, const gchar *column_name) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), -1); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_column_index) (self, column_name); } /** * dee_model_register_vardict_schema: * @self: a #DeeModel * @column: the column index to register the schemas with * @schemas: (element-type utf8 utf8): hashtable with keys specifying * names of the fields and values defining their schema * * Register schema for fields in a model containing column with variant * dictionary schema ('a{sv}'). * The keys registered with this function can be later used * with dee_model_build_named_row() function, as well as * dee_model_get_value_by_name(). Note that it is possible to register * the same field name for multiple columns, in which case you need to use * fully-qualified "column_name::field" name in the calls to * dee_model_build_named_row() and dee_model_get_field_schema(). */ void dee_model_register_vardict_schema (DeeModel *self, guint column, GHashTable *schemas) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); iface = DEE_MODEL_GET_IFACE (self); return (* iface->register_vardict_schema) (self, column, schemas); } /** * dee_model_get_vardict_schema: * @self: a #DeeModel * @column: the column index to get the schemas for * * Get a schema for variant dictionary column previously registered using * dee_model_register_vardict_schema(). * * Return value: (transfer container) (element-type utf8 utf8): Hashtable * containing a mapping from field names to schemas or NULL. * Note that keys and values in the hashtable may be owned * by the model, so you need to create a deep copy if you * intend to keep the hashtable around. */ GHashTable* dee_model_get_vardict_schema (DeeModel *self, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_vardict_schema) (self, column); } /** * dee_model_get_n_columns: * @self: a #DeeModel * * Gets the number of columns in @self * * Return value: the number of columns per row in @self **/ guint dee_model_get_n_columns (DeeModel *self) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_n_columns) (self); } /** * dee_model_get_n_rows: * @self: a #DeeModel * * Gets the number of rows in @self * * Return value: the number of rows in @self **/ guint dee_model_get_n_rows (DeeModel *self) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_n_rows) (self); } /** * dee_model_begin_changeset: * @self: a #DeeModel * * Notify listeners that the model is about to be changed, which means that * multiple row additions / changes / removals will follow. * The default implementation of this method will emit * the ::changeset-started signal. * * It is not stricly necessary to enclose every change to a model * in a dee_model_begin_changeset() and dee_model_end_changeset() calls, but * doing so is highly recommended and allows implementing various optimizations. * * The usual way to perform multiple changes to a model is as follows: * * * void update_model (DeeModel *model) * { * GVariant **added_row_data1 = ...; * GVariant **added_row_data2 = ...; * * dee_model_begin_changeset (model); * * dee_model_remove (model, dee_model_get_first_iter (model)); * dee_model_append_row (model, added_row_data1); * dee_model_append_row (model, added_row_data2); * * dee_model_end_changeset (model); * } * */ void dee_model_begin_changeset (DeeModel *self) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); iface = DEE_MODEL_GET_IFACE (self); if (iface->begin_changeset) (* iface->begin_changeset) (self); else g_signal_emit (self, dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_STARTED], 0); } /** * dee_model_end_changeset: * @self: a #DeeModel * * Notify listeners that all changes have been committed to the model. * The default implementation of this method will emit * the ::changeset-finished signal. * * See also dee_model_begin_changeset(). */ void dee_model_end_changeset (DeeModel *self) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); iface = DEE_MODEL_GET_IFACE (self); if (iface->end_changeset) (* iface->end_changeset) (self); else g_signal_emit (self, dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_FINISHED], 0); } static GVariant* collect_variant (const gchar* col_schema, va_list *args) { const gchar *col_string; GVariant *result; if (g_variant_type_is_basic (G_VARIANT_TYPE (col_schema))) { switch (col_schema[0]) { case 's': case 'o': case 'g': col_string = va_arg (*args, const gchar*); result = g_variant_new (col_schema, col_string ? col_string : ""); break; default: result = g_variant_new_va (col_schema, NULL, args); } } else result = va_arg (*args, GVariant*); return result; } /** * dee_model_build_row: * @self: The model to create a row for * @out_row_members: An array to write the values to or %NULL to allocate * a new array. If non-%NULL it must have a length * that is longer or equal to the number of columns in @self * @VarArgs: A list with values matching the column schemas of @self. * Basic variant types are passed directly while any other * types must be boxed in a #GVariant. It's important to note that * any floating references on variants passed to this method will be * not be consumed. A %NULL value for a string * type column will be converted to the empty string. * * Build an array of #GVariants with values from the variadic argument * list according to the model schema for @self. The caller must call * g_variant_ref_sink() and g_variant_unref() on all the returned variants and * g_free() the array itself if %NULL was passed as @out_row_members. * * This is utility function and will not touch or modify @self in any way. * * Returns: If @out_row_members is %NULL a newly allocated array of variants * will be returned and the array must be freed with g_free(). * If @out_row_members is non-%NULL it will be reused, and variants in * the array may or may not have floating references, which means the * caller must make sure that g_variant_ref_sink() and * g_variant_unref() are called on them. * */ GVariant** dee_model_build_row (DeeModel *self, GVariant **out_row_members, ...) { va_list args; GVariant **result; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); va_start (args, out_row_members); result = dee_model_build_row_valist (self, out_row_members, &args); va_end (args); return result; } /** * dee_model_build_row_valist: (skip): * @self: The model to build a row for * @out_row_members: An array to write the values to or %NULL to allocate * a new array * @args: A %va_list of arguments as described in dee_model_build_row() * * Like dee_model_build_row() but intended for language bindings. * * Returns: See dee_model_build_row() */ static GVariant** dee_model_build_row_valist (DeeModel *self, GVariant **out_row_members, va_list *args) { guint i, n_cols; const gchar *col_schema; const gchar *const *schema; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); schema = dee_model_get_schema (self, &n_cols); if (out_row_members == NULL) out_row_members = g_new0 (GVariant*, n_cols); for (i = 0; i < n_cols; i++) { col_schema = schema[i]; out_row_members[i] = collect_variant (col_schema, args); if (G_UNLIKELY (out_row_members[i] == NULL)) { g_critical ("Trying to build a row with a NULL member on position" " %i. This is probably an error in an application using" " libdee", i); return NULL; } } return out_row_members; } /** * dee_model_build_named_row: * @self: The model to create a row for * @out_row_members: An array to write the values to or %NULL to allocate * a new array. If non-%NULL it must have a length * that is longer or equal to the number of columns in @self * @first_column_name: A column name * @VarArgs: Value for given column, followed by more name/value pairs, * followed by %NULL. The passed names have to match the column names * (or field names registered with * dee_model_register_vardict_schema()) and values have to be set * according to schema of the given column or field. * Basic variant types are passed directly while any other types * must be boxed in a #GVariant, similar to dee_model_build_row(). * * Build an array of #GVariants with values from the variadic argument * list according to the column names and model schema for @self. * The caller must call g_variant_ref_sink() and g_variant_unref() * on all the returned variants and g_free() the array itself if %NULL * was passed as @out_row_members. * * This is utility function and will not touch or modify @self in any way. * * For example, to append a row to model with signature ("s", "u", "s") and * column names set to ("uri", "count", "description") you could do: * * GVariant *row_buf[3]; * * dee_model_append_row (model, * dee_model_build_named_row (model, row_buf, * "uri", "http://example.org", * "count", 435, * "description", "Example.org site", NULL)); * * * Returns: If @out_row_members is %NULL a newly allocated array of variants * will be returned and the array must be freed with g_free(). * If @out_row_members is non-%NULL it will be reused, and variants in * the array may or may not have floating references, which means the * caller must make sure that g_variant_ref_sink() and * g_variant_unref() are called on them. * */ GVariant** dee_model_build_named_row (DeeModel *self, GVariant **out_row_members, const gchar *first_column_name, ...) { va_list args; GVariant **result; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); va_start (args, first_column_name); result = dee_model_build_named_row_valist (self, out_row_members, first_column_name, &args); va_end (args); return result; } /** * dee_model_build_named_row_sunk: * @self: The model to create a row for * @out_row_members: (array): An array to write the values to or %NULL to * allocate a new array. If non-%NULL it must have a length * that is longer or equal to the number of columns in @self * @out_array_length: (out): Length of the returned variant array * @first_column_name: A column name * @VarArgs: Value for given column, followed by more name/value pairs, * followed by %NULL. The passed names have to match the column names * and values have to be set according to schema of @self. * Basic variant types are passed directly while any other types * must be boxed in a #GVariant, similar to dee_model_build_row(). * * Version of dee_model_build_named_row() for language bindings - as opposed to * dee_model_build_named_row(), the returned variants will be strong * references, therefore you always have to call g_variant_unref() on the items * and g_free() the array itself if %NULL was passed as @out_row_members. * * If @out_row_members is non-%NULL, g_variant_unref() will be called * on its elements (if also non-%NULL), which allows easy reuse of the array * memory in loops. * * This is utility function and will not touch or modify @self in any way. * * Example of memory management for model with schema ("s", "i") and * column names ("uri", "count"): * * GVariant **row_buf; * * row_buf = dee_model_build_named_row_sunk (model, NULL, "uri", "file:///", * "count", 0, NULL); * dee_model_append_row (model, row_buf); * * for (int i = 1; i < 100; i++) * { * dee_model_append_row (model, * dee_model_build_named_row_sunk (model, row_buf, "uri", "file:///", * "count", i, NULL)); * } * * g_variant_unref (row_buf[0]); * g_variant_unref (row_buf[1]); * g_free (row_buf); * * * Returns: (array length=out_array_length): If @out_row_members is %NULL * a newly allocated array of variants will be returned and the array * must be freed with g_free(). * If @out_row_members is non-%NULL it will be reused. Variants in * the array will have strong references, which means the * caller must make sure that g_variant_unref() is called on them. * */ GVariant** dee_model_build_named_row_sunk (DeeModel *self, GVariant **out_row_members, guint *out_array_length, const gchar *first_column_name, ...) { va_list args; guint num_columns, i; GVariant **result; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); if (out_row_members) { for (i = 0; i < num_columns; i++) { if (out_row_members[i]) g_variant_unref (out_row_members[i]); } } va_start (args, first_column_name); result = dee_model_build_named_row_valist (self, out_row_members, first_column_name, &args); va_end (args); if (result) { for (i = 0; i < num_columns; i++) { g_variant_ref_sink (result[i]); } } if (out_array_length) *out_array_length = result != NULL ? num_columns : 0; return result; } GVariant** dee_model_build_named_row_valist (DeeModel *self, GVariant **out_row_members, const gchar *first_column_name, va_list *args) { DeeModelIface *iface; guint n_cols, i; gint col_idx, last_unset_col; gboolean *variant_set; GVariantBuilder **builders; const gchar *col_name, *col_schema; const gchar *const *schema; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); schema = dee_model_get_schema (self, &n_cols); if (out_row_members == NULL) out_row_members = g_new0 (GVariant*, n_cols); variant_set = g_alloca (n_cols * sizeof (gboolean)); memset (variant_set, 0, n_cols * sizeof (gboolean)); builders = g_alloca (n_cols * sizeof (GVariantBuilder*)); memset (builders, 0, n_cols * sizeof (GVariantBuilder*)); iface = DEE_MODEL_GET_IFACE (self); col_name = first_column_name; while (col_name != NULL) { col_idx = (* iface->get_column_index) (self, col_name); if (col_idx >= 0) { col_schema = schema[col_idx]; out_row_members[col_idx] = collect_variant (col_schema, args); if (G_UNLIKELY (out_row_members[col_idx] == NULL)) { g_critical ("Trying to build a row with a NULL member for column" " %s. This is probably an error in an application using" " libdee", col_name); break; } else { variant_set[col_idx] = TRUE; } } else { // check if we have hints col_schema = (* iface->get_field_schema) (self, col_name, (guint*) &col_idx); if (col_schema != NULL) { const gchar *key_name; if (builders[col_idx] == NULL) { builders[col_idx] = g_variant_builder_new (G_VARIANT_TYPE (schema[col_idx])); } key_name = strstr (col_name, "::"); key_name = key_name != NULL ? key_name + 2 : col_name; g_variant_builder_add (builders[col_idx], "{sv}", key_name, collect_variant (col_schema, args)); } else { g_warning ("Unable to find column index for \"%s\"", col_name); /* need to break, cause there's no way to know size of the value */ break; } } col_name = va_arg (*args, const gchar*); } /* Finish builders */ for (i = 0; i < n_cols; i++) { if (builders[i]) { out_row_members[i] = g_variant_builder_end (builders[i]); g_variant_builder_unref (builders[i]); variant_set[i] = TRUE; } } /* Check if all columns were set */ last_unset_col = -1; for (i = 0; i < n_cols; i++) { if (!variant_set[i]) { /* Create empty a{sv} if needed */ if (g_variant_type_is_subtype_of (G_VARIANT_TYPE (schema[i]), G_VARIANT_TYPE_VARDICT)) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE (schema[i])); out_row_members[i] = g_variant_builder_end (&builder); variant_set[i] = TRUE; } else { last_unset_col = i; } } } if (last_unset_col >= 0) { /* Be nice and unref the variants we created */ for (i = 0; i < n_cols; i++) { if (variant_set[i]) { g_variant_unref (g_variant_ref_sink (out_row_members[i])); out_row_members[i] = NULL; } } const gchar **names = dee_model_get_column_names (self, NULL); g_critical ("Unable to build row: Column %d '%s' wasn't set", last_unset_col, names ? names[last_unset_col] : "unnamed"); return NULL; } return out_row_members; } /** * dee_model_append: * @self: a #DeeModel * @VarArgs: A list of values matching the column schemas of @self. * Any basic variant type is passed as the standard C type while * any other type must be boxed in a #GVariant. Any floating * references will be consumed. A %NULL value for a string * type column will be converted to the empty string. * * Creates and appends a new row to the end of a #DeeModel, setting the row * values upon creation. * * For and example see dee_model_insert_before(). * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_append (DeeModel *self, ...) { DeeModelIter *iter; va_list args; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); va_start (args, self); iter = dee_model_append_valist (self, &args); va_end (args); return iter; } /** * dee_model_append_valist: (skip): * @self: A #DeeModel * @args: A pointer to a variable argument list * * Returns: A #DeeModelIter pointing to the new row */ static DeeModelIter* dee_model_append_valist (DeeModel *self, va_list *args) { DeeModelIface *iface; DeeModelIter *iter; GVariant **row_members; guint num_columns; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); iface = DEE_MODEL_GET_IFACE (self); row_members = g_alloca (num_columns * sizeof (gpointer)); dee_model_build_row_valist (self, row_members, args); iter = (* iface->append_row) (self, row_members); return iter; } /** * dee_model_append_row: * @self: The model to prepend a row to * @row_members: (array zero-terminated=1): An array of #GVariants with type * signature matching those of the column schemas of @self. * If any of the variants have floating references they will be * consumed * * Like dee_model_append() but intended for language bindings or * situations where you work with models on a meta level and may not have * a prior knowledge of the column schemas of the models. See also * dee_model_build_row(). * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_append_row (DeeModel *self, GVariant **row_members) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->append_row) (self, row_members); } /** * dee_model_prepend: * @self: a #DeeModel * @VarArgs: A list of values matching the column schemas of @self. * Any basic variant type is passed as the standard C type while * any other type must be boxed in a #GVariant. Any floating * references will be consumed. A %NULL value for a string * type column will be converted to the empty string. * * Creates and prepends a new row to the beginning of a #DeeModel, setting the * row values upon creation. * * Example: * * * DeeModel *model; * model = ... * dee_model_set_schema (model, "i", "s", NULL); * dee_model_prepend (model, 10, "Rooney"); * * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_prepend (DeeModel *self, ...) { DeeModelIter *iter; va_list args; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); va_start (args, self); iter = dee_model_prepend_valist (self, &args); va_end (args); return iter; } /** * dee_model_prepend_valist: (skip): * @self: A #DeeModel * @args: A pointer to a variable argument list * * Returns: A #DeeModelIter pointing to the new row */ static DeeModelIter* dee_model_prepend_valist (DeeModel *self, va_list *args) { DeeModelIface *iface; DeeModelIter *iter; GVariant **row_members; guint num_columns; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); iface = DEE_MODEL_GET_IFACE (self); row_members = g_alloca (num_columns * sizeof (gpointer)); dee_model_build_row_valist (self, row_members, args); iter = (* iface->prepend_row) (self, row_members); return iter; } /** * dee_model_prepend_row: * @self: The model to prepend a row to * @row_members: (array zero-terminated=1): An array of * #GVariants with type signature matching those of * the column schemas of @self. If any of the variants have * floating references they will be consumed. * * Like dee_model_prepend() but intended for language bindings or * situations where you work with models on a meta level and may not have * a priori knowledge of the column schemas of the models. See also * dee_model_build_row(). * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_prepend_row (DeeModel *self, GVariant **row_members) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->prepend_row) (self, row_members); } /** * dee_model_insert: * @self: a #DeeModel * @pos: The index to insert the row on. The existing row will be pushed down * @VarArgs: A list of values matching the column schemas of @self. * Any basic variant type is passed as the standard C type while * any other type must be boxed in a #GVariant. Any floating * references will be consumed. A %NULL value for a string * type column will be converted to the empty string. * * Creates and inserts a new row into a #DeeModel, pushing the existing * rows down. * * For and example see dee_model_insert_before(). * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_insert (DeeModel *self, guint pos, ...) { DeeModelIter *iter; va_list args; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); va_start (args, pos); iter = dee_model_insert_valist (self, pos, &args); va_end (args); return iter; } /** * dee_model_insert_valist: (skip): * @self: A #DeeModel * @pos: The index to insert the row on. The existing row will be pushed down * @args: A pointer to a variable argument list * * Returns: A #DeeModelIter pointing to the new row */ static DeeModelIter* dee_model_insert_valist (DeeModel *self, guint pos, va_list *args) { DeeModelIface *iface; DeeModelIter *iter; GVariant **row_members; guint num_columns; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); iface = DEE_MODEL_GET_IFACE (self); row_members = g_alloca (num_columns * sizeof (gpointer)); dee_model_build_row_valist (self, row_members, args); iter = (* iface->insert_row) (self, pos, row_members); return iter; } /** * dee_model_insert_row: * @self: a #DeeModel * @pos: The index to insert the row on. The existing row will be pushed down. * @row_members: (array zero-terminated=1): An array of * #GVariants with type signature matching those of * the column schemas of @self. If any of the variants have * floating references they will be consumed. * * As dee_model_insert(), but intended for language bindings or * situations where you work with models on a meta level and may not have * a priori knowledge of the column schemas of the models. See also * dee_model_build_row(). * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_insert_row (DeeModel *self, guint pos, GVariant **row_members) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->insert_row) (self, pos, row_members); } /** * dee_model_insert_before: * @self: a #DeeModel * @iter: An iter pointing to the row before which to insert the new one * @VarArgs: A list of values matching the column schemas of @self. * Any basic variant type is passed as the standard C type while * any other type must be boxed in a #GVariant. Any floating * references will be consumed. A %NULL value for a string * type column will be converted to the empty string. * * Creates and inserts a new row into a #DeeModel just before the row pointed * to by @iter. * * For example, to insert a new row in a model with schema ("u", "s", "as") * you would do: * * * DeeModelIter *iter; * GVariantBuilder b; * * g_variant_builder_init (&b, "as"); * g_variant_builder_add (&b, "s", "Hello"); * g_variant_builder_add (&b, "s", "World"); * * iter = find_my_special_row (model); * dee_model_insert_before (model, iter, * 27, * "Howdy", * g_variant_builder_end (&b)); * * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_insert_before (DeeModel *self, DeeModelIter *iter, ...) { va_list args; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); va_start (args, iter); iter = dee_model_insert_before_valist (self, iter, &args); va_end (args); return iter; } /** * dee_model_insert_before_valist: (skip): * @self: a #DeeModel * @iter: An iter pointing to the row before which to insert the new one * @args: See dee_model_insert_before() * * As dee_model_insert_before(), but intended for language bindings. * * Returns: A #DeeModelIter pointing to the new row */ static DeeModelIter* dee_model_insert_before_valist (DeeModel *self, DeeModelIter *iter, va_list *args) { DeeModelIface *iface; GVariant **row_members; guint num_columns; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); iface = DEE_MODEL_GET_IFACE (self); row_members = g_alloca (num_columns * sizeof (gpointer)); dee_model_build_row_valist (self, row_members, args); iter = (* iface->insert_row_before) (self, iter, row_members); return iter; } /** * dee_model_insert_row_before: * @self: a #DeeModel * @iter: An iter pointing to the row before which to insert the new one * @row_members: (array zero-terminated=1): An array of * #GVariants with type signature matching those of the * column schemas of @self. If any of the variants have floating * references they will be consumed. * * As dee_model_insert_before(), but intended for language bindings or * situations where you work with models on a meta level and may not have * a priori knowledge of the column schemas of the models. See also * dee_model_build_row(). * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row **/ DeeModelIter* dee_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->insert_row_before) (self, iter, row_members); } /* Translates DeeCompareRowFunc callback into DeeCompareRowSizedFunc */ static gint dee_model_cmp_func_translate_func (GVariant **row1, GVariant **row2, gpointer data) { gpointer *all_data = (gpointer*) data; DeeCompareRowSizedFunc cmp_func = (DeeCompareRowSizedFunc) all_data[0]; gpointer user_data = all_data[1]; guint array_length = GPOINTER_TO_UINT (all_data[2]); return cmp_func (row1, array_length, row2, array_length, user_data); } /** * dee_model_insert_row_sorted: * @self: The model to do a sorted insert on * @row_members: (array zero-terminated=1): An array of * #GVariants with type signature matching those of the * column schemas of @self. If any of the variants have floating * references they will be consumed. * @cmp_func: (scope call): Callback used for comparison or rows * @user_data: (closure): Arbitrary pointer passed to @cmp_func during search * * Inserts a row in @self according to the sorting specified by @cmp_func. * If you use this method for insertion you should not use other methods as this * method assumes the model to be already sorted by @cmp_func. * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_insert_row_sorted (DeeModel *self, GVariant **row_members, DeeCompareRowFunc cmp_func, gpointer user_data) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->insert_row_sorted) (self, row_members, cmp_func, user_data); } /** * dee_model_insert_row_sorted_with_sizes: * @self: The model to do a sorted insert on * @row_members: (array zero-terminated=1): An array of * #GVariants with type signature matching those of the * column schemas of @self. If any of the variants have floating * references they will be consumed. * @cmp_func: (scope call): Callback used for comparison or rows * @user_data: (closure): Arbitrary pointer passed to @cmp_func during search * * Inserts a row in @self according to the sorting specified by @cmp_func. * If you use this method for insertion you should not use other methods as this * method assumes the model to be already sorted by @cmp_func. * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_insert_row_sorted_with_sizes (DeeModel *self, GVariant **row_members, DeeCompareRowSizedFunc cmp_func, gpointer user_data) { gpointer all_data[3]; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); all_data[0] = cmp_func; all_data[1] = user_data; all_data[2] = GUINT_TO_POINTER (dee_model_get_n_columns (self)); return dee_model_insert_row_sorted (self, row_members, dee_model_cmp_func_translate_func, all_data); } /** * dee_model_insert_sorted: * @self: The model to do a sorted insert on * @cmp_func: (scope call): Callback used for comparison or rows * @user_data: (closure): Arbitrary pointer passed to @cmp_func during search * @VarArgs: Specifies the row to insert. A collection of #GVariants * matching the number of columns @self * * Convenience function for calling dee_model_insert_row_sorted(). * Inserts a row in @self according to the sorting specified by @cmp_func. * If you use this method for insertion you should not use other methods as this * method assumes the model to be already sorted by @cmp_func. * * Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row */ DeeModelIter* dee_model_insert_sorted (DeeModel *self, DeeCompareRowFunc cmp_func, gpointer user_data, ...) { DeeModelIface *iface; DeeModelIter *iter; GVariant **row_members; guint num_columns; va_list args; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); iface = DEE_MODEL_GET_IFACE (self); row_members = g_alloca (num_columns * sizeof (gpointer)); va_start (args, user_data); dee_model_build_row_valist (self, row_members, &args); va_end (args); iter = (* iface->insert_row_sorted) (self, row_members, cmp_func, user_data); return iter; } /** * dee_model_find_row_sorted: * @self: The model to search * @row_spec: (array zero-terminated=1): An array of * #GVariants with type signature matching those of the * column schemas of @self. No references will be taken on the variants. * @cmp_func: (scope call): Callback used for comparison or rows * @user_data: (closure): Arbitrary pointer passed to @cmp_func during search * @out_was_found: (out): A place to store a boolean value that will be set when * this method returns. If %TRUE then an exact match was found. * If %FALSE then the returned iter points to a row just after * where @row_spec would have been inserted. * Pass %NULL to ignore. * * Finds a row in @self according to the sorting specified by @cmp_func. * This method will assume that @self is already sorted by @cmp_func. * * If you use this method for searching you should only use * dee_model_insert_row_sorted() to insert rows in the model. * * Returns: (transfer none) (type Dee.ModelIter): If @out_was_found is set to * %TRUE then a #DeeModelIter pointing to the last matching row. * If it is %FALSE then the iter pointing to the row just after where * @row_spec_would have been inserted. */ DeeModelIter* dee_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, NULL, return NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->find_row_sorted) (self, row_spec, cmp_func, user_data, out_was_found); } /** * dee_model_find_row_sorted_with_sizes: * @self: The model to search * @row_spec: (array zero-terminated=1): An array of * #GVariants with type signature matching those of the * column schemas of @self. No references will be taken on the variants. * @cmp_func: (scope call): Callback used for comparison or rows * @user_data: (closure): Arbitrary pointer passed to @cmp_func during search * @out_was_found: (out): A place to store a boolean value that will be set when * this method returns. If %TRUE then an exact match was found. * If %FALSE then the returned iter points to a row just after * where @row_spec would have been inserted. * Pass %NULL to ignore. * * Like dee_model_find_row_sorted(), but uses DeeCompareRowSizedFunc and * therefore doesn't cause trouble when used from introspected languages. * * Finds a row in @self according to the sorting specified by @cmp_func. * This method will assume that @self is already sorted by @cmp_func. * * If you use this method for searching you should only use * dee_model_insert_row_sorted() (or dee_model_insert_row_sorted_with_sizes()) * to insert rows in the model. * * Returns: (transfer none) (type Dee.ModelIter): If @out_was_found is set to * %TRUE then a #DeeModelIter pointing to the last matching row. * If it is %FALSE then the iter pointing to the row just after where * @row_spec_would have been inserted. */ DeeModelIter* dee_model_find_row_sorted_with_sizes (DeeModel *self, GVariant **row_spec, DeeCompareRowSizedFunc cmp_func, gpointer user_data, gboolean *out_was_found) { gpointer all_data[3]; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); all_data[0] = cmp_func; all_data[1] = user_data; all_data[2] = GUINT_TO_POINTER (dee_model_get_n_columns (self)); return dee_model_find_row_sorted (self, row_spec, dee_model_cmp_func_translate_func, all_data, out_was_found); } /** * dee_model_find_sorted: * @self: The model to search * @cmp_func: (scope call): Callback used for comparison or rows * @user_data: (closure): Arbitrary pointer passed to @cmp_func during search * @out_was_found: (out): A place to store a boolean value that will be set when * this method returns. If %TRUE then an exact match was found. * If %FALSE then the returned iter points to a row just after * where @row_spec would have been inserted. * Pass %NULL to ignore. * @VarArgs: A sequence of variables with type signature matching those of the * column schemas of @self. * * Finds a row in @self according to the sorting specified by @cmp_func. * This method will assume that @self is already sorted by @cmp_func. * * If you use this method for searching you should only use * dee_model_insert_row_sorted() to insert rows in the model. * * Returns: (transfer none) (type Dee.ModelIter): If @out_was_found is set to * %TRUE then a #DeeModelIter pointing to the last matching row. * If it is %FALSE then the iter pointing to the row just after where * @row_spec_would have been inserted. */ DeeModelIter* dee_model_find_sorted (DeeModel *self, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found, ...) { DeeModelIface *iface; DeeModelIter *iter; GVariant **row_members; guint num_columns; va_list args; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); CHECK_SCHEMA (self, &num_columns, return NULL); iface = DEE_MODEL_GET_IFACE (self); row_members = g_alloca (num_columns * sizeof (gpointer)); va_start (args, out_was_found); dee_model_build_row_valist (self, row_members, &args); va_end (args); iter = (* iface->find_row_sorted) (self, row_members, cmp_func, user_data, out_was_found); return iter; } /** * dee_model_remove: * @self: a #DeeModel * @iter: a #DeeModelIter pointing to the row to remove * * Removes the row at the given position from the model. */ void dee_model_remove (DeeModel *self, DeeModelIter *iter) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); CHECK_SCHEMA (self, NULL, return); iface = DEE_MODEL_GET_IFACE (self); (* iface->remove) (self, iter); } /** * dee_model_clear: * @self: a #DeeModel object to clear * * Removes all rows in the model. Signals are emitted for each row in the model */ void dee_model_clear (DeeModel *self) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); CHECK_SCHEMA (self, NULL, return); iface = DEE_MODEL_GET_IFACE (self); return (* iface->clear) (self); } /** * dee_model_set: * @self: a #DeeModel * @iter: a #DeeModelIter * @VarArgs: A list of values to set matching the column schemas. * Any basic variant type is passed as the standard C type while * any other type must be boxed in a #GVariant. Any floating * references will be consumed. A %NULL value for a string * type column will be converted to the empty string. * * Sets all values across the entire row referenced by @iter. The * variable argument list should contain values that match the column schemas * for the model. All basic variant type (see g_variant_type_is_basic()) are * passed in as their raw C type while all other types are passed in boxed in * a #GVariant. Any floating references on variants passed to this method are * consumed. * * For example, to set the values for a row on model with the schema * ("u", "s", "as"): * * GVariantBuilder b; * * g_variant_builder_init (&b, "as"); * g_variant_builder_add (&b, "Hello"); * g_variant_builder_add (&b, "World"); * * dee_model_set (model, iter, 27, "foo", g_variant_builder_end (&b)); * **/ void dee_model_set (DeeModel *self, DeeModelIter *iter, ...) { va_list args; g_return_if_fail (DEE_IS_MODEL (self)); /* Update data */ va_start (args, iter); dee_model_set_valist (self, iter, &args); va_end (args); } /** * dee_model_set_valist: (skip): * @self: a #DeeModel * @iter: a #DeeModelIter * @args: See dee_model_set() * * See dee_model_set(). This version takes a va_list for language bindings. */ static void dee_model_set_valist (DeeModel *self, DeeModelIter *iter, va_list *args) { DeeModelIface *iface; GVariant **row_members; guint num_columns; g_return_if_fail (DEE_IS_MODEL (self)); iface = DEE_MODEL_GET_IFACE (self); num_columns = dee_model_get_n_columns (self); row_members = g_alloca (num_columns * sizeof (gpointer)); dee_model_build_row_valist (self, row_members, args); (* iface->set_row) (self, iter, row_members); } /** * dee_model_set_value: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: column number to set the value * @value: New value for cell. If @value is a floating reference the model * will assume ownership of the variant * * Sets the data in @column for the row @iter points to, to @value. The type * of @value must be convertible to the type of the column. * * When this method call completes the model will emit ::row-changed. You can * edit the model in place without triggering the change signals by calling * dee_model_set_value_silently(). */ void dee_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); CHECK_SCHEMA (self, NULL, return); iface = DEE_MODEL_GET_IFACE (self); return (* iface->set_value) (self, iter, column, value); } /** * dee_model_set_row: * @self: a #DeeModel * @iter: a #DeeModelIter * @row_members: (array): And array of * #GVariants with type signature matching * those from the model schema. If any of the variants have * floating references these will be consumed * * Sets all columns in the row @iter points to, to those found in * @row_members. The variants in @row_members must match the types defined in * the model's schema. */ void dee_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); CHECK_SCHEMA (self, NULL, return); iface = DEE_MODEL_GET_IFACE (self); return (* iface->set_row) (self, iter, row_members); } /** * dee_model_get: * @self: a #DeeModel * @iter: a #DeeModelIter * @VarArgs: a list of return locations matching the types defined in the * column schemas. To ignore the data in a specific column pass * a %NULL on that position * * Gets all the values across the entire row referenced by @iter. The * variable argument list should contain pointers to variables that match * the column schemas of this model. * * For all basic variant types (see g_variant_type_is_basic()) this method * expects pointers to their native C types while for all other types it * expects a pointer to a pointer to a #GVariant. * * For string values you are passed a constant reference which is owned by the * model, but any returned variants must be freed with g_variant_unref (). * * For example, to get all values a model with signature ("u", "s", "as") you * would do: * * guint32 u; * const gchar *s; * GVariant *v; * * dee_model_get (model, iter, &u, &s, &v); * * // do stuff * * g_variant_unref (v); * **/ void dee_model_get (DeeModel *self, DeeModelIter *iter, ...) { va_list args; g_return_if_fail (DEE_IS_MODEL (self)); g_return_if_fail (iter); va_start (args, iter); dee_model_get_valist (self, iter, args); va_end (args); } /** * dee_model_get_valist: (skip): * @self: a #DeeModel * @iter: a #DeeModelIter * @args: a list of column/return location pairs, terminated by -1 * * See #dee_model_get(). This version takes a va_list for language bindings. **/ static void dee_model_get_valist (DeeModel *self, DeeModelIter *iter, va_list args) { GVariant *val; const GVariantType *val_t; guint col, n_cols; gpointer *col_data; g_return_if_fail (DEE_IS_MODEL (self)); g_return_if_fail (iter != NULL); n_cols = dee_model_get_n_columns (self); for (col = 0; col < n_cols; col++) { col_data = va_arg (args, gpointer*); /* Skip past here if this column's data was not request */ if (col_data == NULL) { continue; } val = dee_model_get_value (self, iter, col); val_t = g_variant_get_type (val); /* Basic types are passed back unboxed, and non-basic types are passed * back wrapped in variants. Strings are special because we pass them * back without copying them */ if (g_variant_type_is_basic (val_t)) { if (g_variant_type_equal (val_t, G_VARIANT_TYPE_SIGNATURE) || g_variant_type_equal (val_t, G_VARIANT_TYPE_STRING) || g_variant_type_equal (val_t, G_VARIANT_TYPE_OBJECT_PATH)) { /* We need to cast away the constness */ *col_data = (gpointer) g_variant_get_string (val, NULL); } else g_variant_get (val, dee_model_get_column_schema (self, col), col_data); /* dee_model_get_value() returns a ref we need to free */ g_variant_unref (val); } else { /* For complex types the ref on val is transfered to the caller */ *col_data = val; } } } /** * dee_model_get_row: * @self: A #DeeModel to get a row from * @iter: A #DeeModelIter pointing to the row to get * @out_row_members: (array) (out) (allow-none) (default NULL): * An array of variants with a length bigger than or equal to * the number of columns in @self, or %NULL. If you pass * %NULL here a new array will be allocated for you. The * returned variants will have a non-floating reference * * Returns: (array zero-terminated=1): @out_row_members if it was not %NULL * or a newly allocated array otherwise which you must free * with g_free(). The variants in the array will have a strong * reference and needs to be freed with g_variant_unref(). */ GVariant** dee_model_get_row (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_row) (self, iter, out_row_members); } /** * dee_model_get_value: * @self: The #DeeModel to inspect * @iter: a #DeeModelIter pointing to the row to inspect * @column: column number to retrieve the value from * * Returns: (transfer full): A, guaranteed non-floating, reference to a * #GVariant containing the row data. Free with g_variant_unref(). */ GVariant* dee_model_get_value (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_value) (self, iter, column); } /** * dee_model_get_value_by_name: * @self: The #DeeModel to inspect * @iter: a #DeeModelIter pointing to the row to inspect * @column: column name to retrieve the value from * * Returns: (transfer full): A, guaranteed non-floating, reference to a * #GVariant containing the row data. Free with g_variant_unref(). */ GVariant* dee_model_get_value_by_name (DeeModel *self, DeeModelIter *iter, const gchar *column_name) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_value_by_name) (self, iter, column_name); } /** * dee_model_get_bool: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a boolean from * * Return value: if @iter and @column are valid, the boolean stored at @column. * Otherwise %FALSE */ gboolean dee_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), FALSE); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_bool) (self, iter, column); } /** * dee_model_get_uchar: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a uchar from * * Return value: if @iter and @column are valid, the uchar stored at @column. * Otherwise 0. **/ guchar dee_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), '\0'); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_uchar) (self, iter, column); } /** * dee_model_get_int32: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a int from * * Return value: if @iter and @column are valid, the int stored at @column. * Otherwise 0. **/ gint32 dee_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_int32) (self, iter, column); } /** * dee_model_get_uint32: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a uint from * * Return value: if @iter and @column are valid, the uint stored at @column. * Otherwise 0. **/ guint32 dee_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_uint32) (self, iter, column); } /** * dee_model_get_int64: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a int64 from * * Return value: if @iter and @column are valid, the int64 stored at @column. * Otherwise 0. **/ gint64 dee_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_int64) (self, iter, column); } /** * dee_model_get_uint64: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a uint64 from * * Return value: if @iter and @column are valid, the uint64 stored at @column. * Otherwise 0. **/ guint64 dee_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_uint64) (self, iter, column); } /** * dee_model_get_double: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a double from * * Return value: if @iter and @column are valid, the double stored at @column. * Otherwise 0. **/ gdouble dee_model_get_double (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), 0); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_double) (self, iter, column); } /** * dee_model_get_string: * @self: a #DeeModel * @iter: a #DeeModelIter * @column: the column to retrieve a string from * * Return value: if @iter and @column are valid, the string stored at @column. * Otherwise %NULL. **/ const gchar* dee_model_get_string (DeeModel *self, DeeModelIter *iter, guint column) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_string) (self, iter, column); } /** * dee_model_get_first_iter: * @self: a #DeeModel * * Retrieves a #DeeModelIter representing the first row in @self. * * Return value: (transfer none): A #DeeModelIter (owned by @self, do not * free it) */ DeeModelIter* dee_model_get_first_iter (DeeModel *self) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_first_iter) (self); } /** * dee_model_get_last_iter: * @self: a #DeeModel * * Retrieves a #DeeModelIter pointing right after the * last row in @self. This is refered to also the the * end iter. * * As with other iters the end iter, in particular, is stable over inserts, * changes, or removals. * * Return value: (transfer none): A #DeeModelIter (owned by @self, do not * free it) **/ DeeModelIter* dee_model_get_last_iter (DeeModel *self) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_last_iter) (self); } /** * dee_model_get_iter_at_row: * @self: a #DeeModel * @row: position of the row to retrieve * * Retrieves a #DeeModelIter representing the row at the given index. * * Note that this method does not have any performance guarantees. In particular * it is not guaranteed to be O(1). * * Return value: (transfer none): A new #DeeModelIter, or %NULL if @row * was out of bounds. The returned iter is owned by @self, so do not free it. **/ DeeModelIter* dee_model_get_iter_at_row (DeeModel *self, guint row) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_iter_at_row) (self, row); } /** * dee_model_next: * @self: a #DeeModel * @iter: a #DeeModelIter * * Returns a #DeeModelIter that points to the next position in the model. * * Return value: (transfer none): A #DeeModelIter, pointing to the next row in * the model. The iter is owned by @self, do not free it. **/ DeeModelIter* dee_model_next (DeeModel *self, DeeModelIter *iter) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->next) (self, iter); } /** * dee_model_prev: * @self: a #DeeModel * @iter: a #DeeModelIter * * Returns a #DeeModelIter that points to the previous position in the model. * * Return value: (transfer none): A #DeeModelIter, pointing to the previous * row in the model. The iter is owned by @self, do not free it. **/ DeeModelIter * dee_model_prev (DeeModel *self, DeeModelIter *iter) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->prev) (self, iter); } /** * dee_model_is_first: * @self: a #DeeModel * @iter: a #DeeModelIter * * Checks if @iter is the very first iter @self. * * Return value: #TRUE if @iter is the first iter in the model */ gboolean dee_model_is_first (DeeModel *self, DeeModelIter *iter) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), FALSE); iface = DEE_MODEL_GET_IFACE (self); return (* iface->is_first) (self, iter); } /** * dee_model_is_last: * @self: a #DeeModel * @iter: a #DeeModelIter * * Whether @iter is the end iter of @self. Note that the end iter points * right after the last valid row in @self. * * Return value: #TRUE if @iter is the last iter in the model */ gboolean dee_model_is_last (DeeModel *self, DeeModelIter *iter) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), FALSE); iface = DEE_MODEL_GET_IFACE (self); return (* iface->is_last) (self, iter); } /** * dee_model_get_position: * @self: The model to inspect * @iter: The iter to get the position of * * Get the numeric offset of @iter into @self. Note that this method is * not guaranteed to be O(1). * * Returns: The integer offset of @iter in @self */ guint dee_model_get_position (DeeModel *self, DeeModelIter *iter) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), -1); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_position) (self, iter); } /** * dee_model_register_tag: * @self: The model to register a tag on * @tag_destroy: Function called when a tagged row is removed from the model. * This function will also be called on all tagged rows when the * model is finalized. * * Register a new tag on a #DeeModel. A tag is an extra * value attached to a given row on a model. The tags are invisible to all * that doesn't have the tag handle returned by this method. #DeeModel * implementations must ensure that dee_model_get_tag() is an O(1) operation. * * Tags can be very useful in associating some extra data to a row in a model * and have that automatically synced when the model changes. If you're * writing a tiled view for a model you might want to tag each row with the * tile widget for that row. That way you have very convenient access to the * tile widget given any row in the model. * * The private nature of tags and the fact that you can store arbitrary pointers * and binary data in them also means that they are not serialized if you * utilize a model implementation that exposes the #DeeSerializable interface. * * Return value: (transfer none) (type Dee.ModelTag): A #DeeModelTag handle * that you can use to set and get tags with */ DeeModelTag* dee_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->register_tag) (self, tag_destroy); } /** * dee_model_get_tag: * @self: The model to get a tag from * @iter: A #DeeModelIter pointing to the row to get the tag from * @tag: The tag handle to retrieve the tag value for * * Look up a tag value for a given row in a model. This method is guaranteed * to be O(1). * * Return value: (transfer none): Returns %NULL if @tag is unset otherwise the * value of the tag as it was set with dee_model_set_tag(). */ gpointer dee_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_MODEL (self), NULL); iface = DEE_MODEL_GET_IFACE (self); return (* iface->get_tag) (self, iter, tag); } /** * dee_model_set_tag: * @self: The model to set a tag on * @iter: The row to set the tag on * @tag: The tag handle for the tag as obtained from dee_model_register_tag() * @value: The value to set for @tag. Note that %NULL represents an unset tag * * Set a tag on a row in a model. This function is guaranteed to be O(1). * See also dee_model_register_tag(). * * If @tag is already set on this row the existing tag value will be destroyed * with the #GDestroyNotify passed to the dee_model_register_tag(). */ void dee_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); iface = DEE_MODEL_GET_IFACE (self); (* iface->set_tag) (self, iter, tag, value); } /** * dee_model_clear_tag: * @self: The model to clear a tag on * @iter: The row to clear the tag from * @tag: The tag to clear from @iter * * This method is purely syntactic sugar for calling dee_model_set_tag() with * a @value of %NULL. It's included in order to help developers write more * readable code. */ void dee_model_clear_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag) { DeeModelIface *iface; g_return_if_fail (DEE_IS_MODEL (self)); iface = DEE_MODEL_GET_IFACE (self); (* iface->set_tag) (self, iter, tag, NULL); } dee-1.2.7+15.04.20150304/src/trace-log.h0000644000015300001610000000561112475676210017437 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ /* * This file contains some special GObject-centric debugging macros that * can be compiled completely out out of the final binary */ #ifndef _TRACE_LOG_H #define _TRACE_LOG_H #include "config.h" G_BEGIN_DECLS #ifndef TRACE_LOG_DOMAIN /** * TRACE_LOG_DOMAIN: (skip) * * The log domain of libdee */ #define TRACE_LOG_DOMAIN "Dee" #endif /* TRACE_LOG_DOMAIN */ /* * Make trace() a noop if ENABLE_TRACE_LOG is not defined */ #ifdef ENABLE_TRACE_LOG void trace_object_va (void *obj, const gchar *format, va_list args); void trace_object_real (void *obj, const gchar *format, ...); # ifdef G_HAVE_ISO_VARARGS # define trace(...) g_log (TRACE_LOG_DOMAIN, \ G_LOG_LEVEL_DEBUG, \ __VA_ARGS__) # define trace_object(object, ...) trace_object_real (object, __VA_ARGS__) # elif defined(G_HAVE_GNUC_VARARGS) # define trace(format...) g_log (TRACE_LOG_DOMAIN, \ G_LOG_LEVEL_DEBUG, \ format) # define trace_object(object, format...) trace_object_real (object, format) # else /* no varargs macros */ static void trace (const gchar *format, ...) { va_list args; va_start (args, format); g_logv (TRACE_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args); va_end (args); } static void trace_object (void *obj, const gchar *format, ...) { va_list args; va_start (args, format); trace_object_va (obj, format, args); va_end (args); } # endif /* !__GNUC__ */ #else /* NO TRACE LOGGING OUTPUT */ # ifdef G_HAVE_ISO_VARARGS # define trace(...) G_STMT_START{ (void)0; }G_STMT_END # define trace_object(object, ...) G_STMT_START{ (void)0; }G_STMT_END # elif defined(G_HAVE_GNUC_VARARGS) # define trace(format...) G_STMT_START{ (void)0; }G_STMT_END # define trace_object(object, format...) G_STMT_START{ (void)0; }G_STMT_END # else /* no varargs macros */ static void trace (const gchar *format, ...) { ; } static void trace_object (GObject *obj, const gchar *format, ...) { ; } # endif /* !__GNUC__ */ #endif /* ENABLE_TRACE_LOG */ G_END_DECLS #endif /* _TRACE_LOG_H */ dee-1.2.7+15.04.20150304/src/dee-text-analyzer.h0000644000015300001610000000475612475676210021135 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_TEXT_ANALYZER_H #define _HAVE_DEE_TEXT_ANALYZER_H #include #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_TEXT_ANALYZER (dee_text_analyzer_get_type ()) #define DEE_TEXT_ANALYZER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_TEXT_ANALYZER, DeeTextAnalyzer)) #define DEE_TEXT_ANALYZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_TEXT_ANALYZER, DeeTextAnalyzerClass)) #define DEE_IS_TEXT_ANALYZER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_TEXT_ANALYZER)) #define DEE_IS_TEXT_ANALYZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_TEXT_ANALYZER)) #define DEE_TEXT_ANALYZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DEE_TYPE_TEXT_ANALYZER, DeeTextAnalyzerClass)) typedef struct _DeeTextAnalyzer DeeTextAnalyzer; typedef struct _DeeTextAnalyzerClass DeeTextAnalyzerClass; typedef struct _DeeTextAnalyzerPrivate DeeTextAnalyzerPrivate; /** * DeeTextAnalyzer: * * All fields in the DeeTextAnalyzer structure are private and should never be * accessed directly */ struct _DeeTextAnalyzer { /*< private >*/ DeeAnalyzer parent; DeeTextAnalyzerPrivate *priv; }; struct _DeeTextAnalyzerClass { /*< private >*/ DeeAnalyzerClass parent_class; }; /** * dee_text_analyzer_get_type: * * The GType of #DeeTextAnalyzer * * Return value: the #GType of #DeeTextAnalyzer **/ GType dee_text_analyzer_get_type (void); DeeTextAnalyzer* dee_text_analyzer_new (void); G_END_DECLS #endif /* _HAVE_DEE_TEXT_ANALYZER_H */ dee-1.2.7+15.04.20150304/src/dee-term-list.h0000644000015300001610000000630312475676210020234 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_TERM_LIST_H #define _HAVE_DEE_TERM_LIST_H #include #include G_BEGIN_DECLS #define DEE_TYPE_TERM_LIST (dee_term_list_get_type ()) #define DEE_TERM_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_TERM_LIST, DeeTermList)) #define DEE_TERM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_TERM_LIST, DeeTermListClass)) #define DEE_IS_TERM_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_TERM_LIST)) #define DEE_IS_TERM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_TERM_LIST)) #define DEE_TERM_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_TERM_LIST, DeeTermListClass)) typedef struct _DeeTermListClass DeeTermListClass; typedef struct _DeeTermList DeeTermList; typedef struct _DeeTermListPrivate DeeTermListPrivate; /** * DeeTermList: * * All fields in the DeeTermList structure are private and should never be * accessed directly */ struct _DeeTermList { /*< private >*/ GObject parent; DeeTermListPrivate *priv; }; struct _DeeTermListClass { GObjectClass parent_class; /*< public >*/ const gchar* (* get_term) (DeeTermList *self, guint n); DeeTermList* (* add_term) (DeeTermList *self, const gchar *term); guint (* num_terms) (DeeTermList *self); DeeTermList* (* clear) (DeeTermList *self); DeeTermList* (* clone) (DeeTermList *self); /*< private >*/ void (*_dee_term_list_1) (void); void (*_dee_term_list_2) (void); void (*_dee_term_list_3) (void); void (*_dee_term_list_4) (void); }; GType dee_term_list_get_type (void); const gchar* dee_term_list_get_term (DeeTermList *self, guint n); DeeTermList* dee_term_list_add_term (DeeTermList *self, const gchar *term); guint dee_term_list_num_terms (DeeTermList *self); DeeTermList* dee_term_list_clear (DeeTermList *self); DeeTermList* dee_term_list_clone (DeeTermList *self); G_END_DECLS #endif /* _HAVE_DEE_TERM_LIST_H */ dee-1.2.7+15.04.20150304/src/dee-filter-model.c0000644000015300001610000010477612475676210020707 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-filter-model * @short_description: A #DeeModel that contains a filtered subset of * another #DeeModel * @include: dee.h * * A #DeeFilterModel should be regarded as a view on a specific subset of * of another #DeeModel, filtered according to a given "filtering rule". * * Filter models re-use the #DeeModelIters of the back end model they * filter. This means that any iter from the filter model can be used directly * on the back end model. This is a powerful invariant, but implies the * restriction that a row in the filter model contains the exact same data * as the corresponding row in the back end model (ie. you can not apply * a "transfomation map" to the filtered data). * * The reuse of row iters also minimizes the amount of memory shuffling needed * to set up a filter model. The filtering functions, #DeeFilterMapFunc and * #DeeFilterMapNotify, has also been designed to minimize the amount of work * done to create a filter model. So if the filter functions are written * optimally the resulting filter models should be cheap to construct. * * Another important feature of the filter model is also that the rows * need not be in the same order as the original rows in the back end model. * * There is a suite of filters shipped with Dee which you can browse in the * Filters section. * */ #ifdef HAVE_CONFIG_H #include #endif #include // memcpy() #include "dee-peer.h" #include "dee-model.h" #include "dee-filter.h" #include "dee-proxy-model.h" #include "dee-filter-model.h" #include "dee-serializable-model.h" #include "dee-sequence-model.h" #include "dee-marshal.h" #include "trace-log.h" static void dee_filter_model_model_iface_init (DeeModelIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeFilterModel, dee_filter_model, DEE_TYPE_PROXY_MODEL, G_IMPLEMENT_INTERFACE (DEE_TYPE_MODEL, dee_filter_model_model_iface_init)); #define DEE_FILTER_MODEL_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_FILTER_MODEL, DeeFilterModelPrivate)) /** * DeeFilterModelPrivate: * * Ignore this structure. **/ struct _DeeFilterModelPrivate { DeeFilter *filter; DeeModel *orig_model; /* Map of orig_model iters to our own internal GSequenceIters for iter_list */ GHashTable *iter_map; /* Sequence use to keep track of the sorting of iters from iter_map */ GSequence *iter_list; /* When TRUE signals from orig_model will not be forwarded or checked * via the filter->map_notify function */ gboolean ignore_orig_signals; gulong on_orig_row_added_id; gulong on_orig_row_removed_id; gulong on_orig_row_changed_id; gulong on_orig_changeset_started_id; gulong on_orig_changeset_finished_id; }; enum { PROP_0, PROP_FILTER, }; /* * DeeModel forward declarations */ static void dee_filter_model_set_schema_full (DeeModel *self, const gchar* const *schema, guint n_columns); static guint dee_filter_model_get_n_rows (DeeModel *self); static DeeModelIter* dee_filter_model_append_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_filter_model_prepend_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_filter_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static DeeModelIter* dee_filter_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found); static void dee_filter_model_remove (DeeModel *self, DeeModelIter *iter); static DeeModelIter* dee_filter_model_get_first_iter (DeeModel *self); static DeeModelIter* dee_filter_model_get_iter_at_row (DeeModel *self, guint row); static DeeModelIter* dee_filter_model_next (DeeModel *self, DeeModelIter *iter); static DeeModelIter* dee_filter_model_prev (DeeModel *self, DeeModelIter *iter); static gboolean dee_filter_model_is_first (DeeModel *self, DeeModelIter *iter); static guint dee_filter_model_get_position (DeeModel *self, DeeModelIter *iter); /* Private forward declarations */ static gboolean dee_filter_model_is_empty (DeeModel *self); static void on_orig_model_row_added (DeeFilterModel *self, DeeModelIter *iter); static void on_orig_model_row_removed (DeeFilterModel *self, DeeModelIter *iter); static void on_orig_model_row_changed (DeeFilterModel *self, DeeModelIter *iter); static void on_orig_model_changeset_started (DeeFilterModel *self, DeeModel *iter); static void on_orig_model_changeset_finished (DeeFilterModel *self, DeeModel *iter); /* GObject stuff */ static void dee_filter_model_finalize (GObject *object) { DeeFilterModelPrivate *priv = DEE_FILTER_MODEL (object)->priv; if (priv->filter) { dee_filter_destroy (priv->filter); g_free (priv->filter); priv->filter = NULL; } if (priv->iter_map) { g_hash_table_destroy (priv->iter_map); priv->iter_map = NULL; } if (priv->iter_list) { g_sequence_free (priv->iter_list); priv->iter_list = NULL; } if (priv->on_orig_row_added_id != 0) g_signal_handler_disconnect (priv->orig_model, priv->on_orig_row_added_id); if (priv->on_orig_row_removed_id != 0) g_signal_handler_disconnect (priv->orig_model, priv->on_orig_row_removed_id); if (priv->on_orig_row_changed_id != 0) g_signal_handler_disconnect (priv->orig_model, priv->on_orig_row_changed_id); if (priv->on_orig_changeset_started_id != 0) g_signal_handler_disconnect (priv->orig_model, priv->on_orig_changeset_started_id); if (priv->on_orig_changeset_finished_id != 0) g_signal_handler_disconnect (priv->orig_model, priv->on_orig_changeset_finished_id); priv->on_orig_row_added_id = 0; priv->on_orig_row_removed_id = 0; priv->on_orig_row_changed_id = 0; priv->on_orig_changeset_started_id = 0; priv->on_orig_changeset_finished_id = 0; if (priv->orig_model) { g_object_unref (priv->orig_model); priv->orig_model = NULL; } G_OBJECT_CLASS (dee_filter_model_parent_class)->finalize (object); } static void dee_filter_model_constructed (GObject *object) { DeeFilterModelPrivate *priv = DEE_FILTER_MODEL (object)->priv; if (priv->filter == NULL) { g_critical ("You must set the 'filter' property when " "creating a DeeFilterModel"); return; } /* This will return a new reference on back-end */ g_object_get (object, "back-end", &(priv->orig_model), NULL); /* Map the end iter of the orig_model to the end iter of our iter list */ g_hash_table_insert (priv->iter_map, dee_model_get_last_iter (priv->orig_model), g_sequence_get_end_iter (priv->iter_list)); /* Apply filter to orig_model in order to fill this model */ dee_filter_map (priv->filter, priv->orig_model, DEE_FILTER_MODEL (object)); /* Listen for changes to orig_model */ priv->on_orig_row_added_id = g_signal_connect_swapped (priv->orig_model, "row-added", G_CALLBACK (on_orig_model_row_added), object); priv->on_orig_row_removed_id = g_signal_connect_swapped (priv->orig_model, "row-removed", G_CALLBACK (on_orig_model_row_removed), object); priv->on_orig_row_changed_id = g_signal_connect_swapped (priv->orig_model, "row-changed", G_CALLBACK (on_orig_model_row_changed), object); priv->on_orig_changeset_started_id = g_signal_connect_swapped (priv->orig_model, "changeset-started", G_CALLBACK (on_orig_model_changeset_started), object); priv->on_orig_changeset_finished_id = g_signal_connect_swapped (priv->orig_model, "changeset-finished", G_CALLBACK (on_orig_model_changeset_finished), object); if (G_OBJECT_CLASS (dee_filter_model_parent_class)->constructed) G_OBJECT_CLASS (dee_filter_model_parent_class)->constructed (object); } static void dee_filter_model_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeeFilterModelPrivate *priv = DEE_FILTER_MODEL (object)->priv; switch (id) { case PROP_FILTER: priv->filter = g_new0 (DeeFilter, 1); memcpy (priv->filter, g_value_get_pointer (value), sizeof (DeeFilter)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_filter_model_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { case PROP_FILTER: g_value_set_pointer (value, DEE_FILTER_MODEL (object)->priv->filter); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_filter_model_class_init (DeeFilterModelClass *klass) { GParamSpec *pspec; GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_filter_model_finalize; obj_class->constructed = dee_filter_model_constructed; obj_class->get_property = dee_filter_model_get_property; obj_class->set_property = dee_filter_model_set_property; /** * DeeFilterModel:filter: * * Property holding the #DeeFilter used to filter the model * defined in the #DeeFilterModel:back-end property. * * Type: DeeFilter */ pspec = g_param_spec_pointer ("filter", "Filter", "Filtering rules applied to the original model", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_FILTER, pspec); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeFilterModelPrivate)); } static void dee_filter_model_init (DeeFilterModel *self) { DeeFilterModelPrivate *priv; priv = self->priv = DEE_FILTER_MODEL_GET_PRIVATE (self); priv->iter_map = g_hash_table_new (g_direct_hash, g_direct_equal); priv->iter_list = g_sequence_new (NULL); priv->ignore_orig_signals = FALSE; priv->on_orig_row_added_id = 0; priv->on_orig_row_removed_id = 0; priv->on_orig_row_changed_id = 0; priv->on_orig_changeset_started_id = 0; priv->on_orig_changeset_finished_id = 0; } static void dee_filter_model_model_iface_init (DeeModelIface *iface) { /* Override column spec setters as that would cause a mess... */ iface->set_schema_full = dee_filter_model_set_schema_full; iface->get_n_rows = dee_filter_model_get_n_rows; iface->prepend_row = dee_filter_model_prepend_row; iface->append_row = dee_filter_model_append_row; iface->insert_row_before = dee_filter_model_insert_row_before; iface->find_row_sorted = dee_filter_model_find_row_sorted; iface->remove = dee_filter_model_remove; iface->get_first_iter = dee_filter_model_get_first_iter; iface->get_iter_at_row = dee_filter_model_get_iter_at_row; iface->next = dee_filter_model_next; iface->prev = dee_filter_model_prev; iface->is_first = dee_filter_model_is_first; iface->get_position = dee_filter_model_get_position; } /* * Public API */ /** * dee_filter_model_new: * @filter: Structure containing the logic used to create the filter model. * The filter model will create it's own copy of @filter so unless * @filter is allocated statically or on the stack you need to free it * after calling this method. * @orig_model: The back end model. This will be set as the * #DeeProxyModel:back-end property * * Returns: (transfer full) (type DeeFilterModel): A newly allocated #DeeFilterModel. Free with g_object_unref(). */ DeeModel* dee_filter_model_new (DeeModel *orig_model, DeeFilter *filter) { DeeModel *self; self = DEE_MODEL (g_object_new (DEE_TYPE_FILTER_MODEL, "filter", filter, "back-end", orig_model, "proxy-signals", FALSE, "inherit-seqnums", FALSE, NULL)); return self; } /** * dee_filter_model_contains: * @self: The #DeeFilterModel to check * @iter: (transfer none):The #DeeModelIter to check * * Check if @iter from the back end model is mapped in @self. * * Returns: %TRUE if and only if @iter is contained in @self. */ gboolean dee_filter_model_contains (DeeFilterModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), FALSE); return g_hash_table_lookup (self->priv->iter_map, iter) != NULL; } /** * dee_filter_model_append_iter: * @self: * @iter: * * Includes @iter from the back end model in the filtered model, appending * it to the end of the filtered rows. * * This method is usually called when implementing #DeeFilterMapFunc or * #DeeFilterMapNotify methods. * * Return value: (transfer none): Always returns @iter */ DeeModelIter* dee_filter_model_append_iter (DeeFilterModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); g_return_val_if_fail (!dee_model_is_last ((DeeModel*)self, iter), NULL); priv = self->priv; seq_iter = g_hash_table_lookup (priv->iter_map, iter); if (seq_iter != NULL) { g_critical ("Iter already present in DeeFilterModel"); return NULL; } seq_iter = g_sequence_append (priv->iter_list, iter); g_hash_table_insert (priv->iter_map, iter, seq_iter); dee_serializable_model_inc_seqnum (DEE_MODEL (self)); g_signal_emit_by_name (self, "row-added", iter); return iter; } /** * dee_filter_model_prepend_iter: * @self: * @iter: * * Includes @iter from the back end model in the filtered model, prepending * it to the beginning of the filtered rows. * * This method is usually called when implementing #DeeFilterMapFunc or * #DeeFilterMapNotify methods. * * Return value: (transfer none): Always returns @iter */ DeeModelIter* dee_filter_model_prepend_iter (DeeFilterModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = self->priv; seq_iter = g_hash_table_lookup (priv->iter_map, iter); if (seq_iter != NULL) { g_critical ("Iter already present in DeeFilterModel"); return NULL; } seq_iter = g_sequence_prepend (priv->iter_list, iter); g_hash_table_insert (priv->iter_map, iter, seq_iter); dee_serializable_model_inc_seqnum (DEE_MODEL (self)); g_signal_emit_by_name (self, "row-added", iter); return iter; } /** * dee_filter_model_insert_iter: * @self: * @iter: * @pos: * * Includes @iter from the back end model in the filtered model, inserting it at * @pos pushing other rows down. * * This method is usually called when implementing #DeeFilterMapFunc or * #DeeFilterMapNotify methods. * * Return value: (transfer none): Always returns @iter */ DeeModelIter* dee_filter_model_insert_iter (DeeFilterModel *self, DeeModelIter *iter, guint pos) { DeeModelIter *pos_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); pos_iter = dee_model_get_iter_at_row (DEE_MODEL (self), pos); return dee_filter_model_insert_iter_before (self, iter, pos_iter); } /** * dee_filter_model_insert_iter_before: * @self: * @iter: * @pos: * * Includes @iter from the back end model in the filtered model, inserting it at * the position before @pos pushing other rows down. * * This method is usually called when implementing #DeeFilterMapFunc or * #DeeFilterMapNotify methods. * * Return value: (transfer none): Always returns @iter */ DeeModelIter* dee_filter_model_insert_iter_before (DeeFilterModel *self, DeeModelIter *iter, DeeModelIter *pos) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = self->priv; seq_iter = g_hash_table_lookup (priv->iter_map, iter); if (seq_iter != NULL) { g_critical ("Iter already present in DeeFilterModel"); return NULL; } seq_iter = g_hash_table_lookup (priv->iter_map, pos); if (seq_iter == NULL) { g_critical ("Can not insert iter. Position iter not " "present in DeeFilterModel"); return NULL; } seq_iter = g_sequence_insert_before (seq_iter, iter); g_hash_table_insert (priv->iter_map, iter, seq_iter); dee_serializable_model_inc_seqnum (DEE_MODEL (self)); g_signal_emit_by_name (self, "row-added", iter); return iter; } /** * dee_filter_model_insert_iter_with_original_order: * * @self: A #DeeFilterModel instance * @iter: Iterator * * Inserts @iter in @self in a way that is consistent with the ordering of the * rows in the original #DeeModel behind @self. THis method assumes that @self * is already ordered this way. If that's not the case then this method has * undefined behaviour. * * This method is mainly intended as a helper for #DeeFilterMapNotify functions * of #DeeFilter implementations that creates filter models sorted in * accordance with the original models. * * Return value: (transfer none): Always returns @iter */ DeeModelIter* dee_filter_model_insert_iter_with_original_order (DeeFilterModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; DeeModel *orig_model; DeeModelIter *probe, *end; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); g_return_val_if_fail (iter != NULL, NULL); priv = self->priv; orig_model = priv->orig_model; /* We find the first row *after* iter which is both in orig_model and the * filter model and insert iter *before* that row in the filter model. * This should assure the sorting is consistent between the models * (assuming it already was consistent before we started) */ probe = dee_model_next (orig_model, iter); end = dee_model_get_last_iter (orig_model); while (probe != end) { if (dee_filter_model_contains (self, probe)) { dee_filter_model_insert_iter_before (self, iter, probe); return iter; } probe = dee_model_next (orig_model, probe); } /* We made it to the end without finding a suitable place, * so just append it */ return dee_filter_model_append_iter (self, iter); } /* * Private impl */ static gboolean dee_filter_model_is_empty (DeeModel *self) { DeeFilterModelPrivate *priv; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), FALSE); priv = DEE_FILTER_MODEL (self)->priv; return g_sequence_get_begin_iter (priv->iter_list) == g_sequence_get_end_iter (priv->iter_list); } static void on_orig_model_row_added (DeeFilterModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; priv = self->priv; if (priv->ignore_orig_signals) return; dee_filter_notify (priv->filter, iter, priv->orig_model, self); } static void on_orig_model_row_removed (DeeFilterModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; priv = self->priv; if (priv->ignore_orig_signals) return; seq_iter = g_hash_table_lookup (priv->iter_map, iter); if (seq_iter != NULL) { /* Emit signal before we delete it from our records */ dee_serializable_model_inc_seqnum (DEE_MODEL (self)); g_signal_emit_by_name (self, "row-removed", iter); g_hash_table_remove (priv->iter_map, iter); g_sequence_remove (seq_iter); } } static void on_orig_model_row_changed (DeeFilterModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; priv = self->priv; if (priv->ignore_orig_signals) return; if (dee_filter_model_contains (self, iter)) { dee_serializable_model_inc_seqnum (DEE_MODEL (self)); g_signal_emit_by_name (self, "row-changed", iter); } } static void on_orig_model_changeset_started (DeeFilterModel *self, DeeModel *model) { DeeFilterModelPrivate *priv; priv = self->priv; if (priv->ignore_orig_signals) return; /* this can end up being an empty changeset, but that's ok */ g_signal_emit_by_name (self, "changeset-started"); } static void on_orig_model_changeset_finished (DeeFilterModel *self, DeeModel *model) { DeeFilterModelPrivate *priv; priv = self->priv; if (priv->ignore_orig_signals) return; g_signal_emit_by_name (self, "changeset-finished"); } /* * DeeModel Interface Implementation */ static void dee_filter_model_set_schema_full (DeeModel *self, const gchar* const *schema, guint n_columns) { g_return_if_fail (DEE_IS_FILTER_MODEL (self)); g_critical ("You can not set the schema on a DeeFilterModel. " "It will always inherit the ones on the original model"); return; } /* FALL THROUGH: dee_filter_model_get_schema() */ /* FALL THROUGH: dee_filter_model_get_column_schema() */ /* FALL THROUGH: dee_filter_model_get_n_columns() */ static guint dee_filter_model_get_n_rows (DeeModel *self) { DeeFilterModelPrivate *priv; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), 0); priv = DEE_FILTER_MODEL (self)->priv; return g_hash_table_size (priv->iter_map) - 1; } /* FALL THROUGH: dee_filter_model_get_column_schema() */ /* FALL THROUGH: dee_filter_model_clear() */ static DeeModelIter* dee_filter_model_prepend_row (DeeModel *self, GVariant **row_members) { DeeFilterModelPrivate *priv; DeeModelIter *iter; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = DEE_FILTER_MODEL (self)->priv; /* If the filter model already contains some rows, then insert the new row * in the orig model at the position of the first row in the filtered model. * If it's empty we prepend the row to the orig model */ if (dee_filter_model_is_empty (self)) { priv->ignore_orig_signals = TRUE; iter = dee_model_prepend_row (priv->orig_model, row_members); priv->ignore_orig_signals = FALSE; } else { iter = dee_model_get_first_iter (self); priv->ignore_orig_signals = TRUE; iter = dee_model_insert_row_before (priv->orig_model, iter, row_members); priv->ignore_orig_signals = FALSE; } seq_iter = g_sequence_prepend (priv->iter_list, iter); g_hash_table_insert (priv->iter_map, iter, seq_iter); dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-added", iter); return iter; } static DeeModelIter* dee_filter_model_append_row (DeeModel *self, GVariant **row_members) { DeeFilterModelPrivate *priv; DeeModelIter *iter; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = DEE_FILTER_MODEL (self)->priv; /* If the filter model already contains some rows, then insert the new row * in the orig model at the position of the last row in the filtered model. * If it's empty we append the row to the orig model */ priv->ignore_orig_signals = TRUE; if (dee_filter_model_is_empty (self)) { iter = dee_model_append_row (priv->orig_model, row_members); } else { iter = dee_model_get_last_iter (self); iter = dee_model_insert_row_before (priv->orig_model, iter, row_members); } priv->ignore_orig_signals = FALSE; seq_iter = g_sequence_append (priv->iter_list, iter); g_hash_table_insert (priv->iter_map, iter, seq_iter); dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-added", iter); return iter; } /* FALL THROUGH: dee_filter_model_insert_valist() */ static DeeModelIter* dee_filter_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { DeeFilterModelPrivate *priv; DeeModelIter *new_iter; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = DEE_FILTER_MODEL (self)->priv; seq_iter = g_hash_table_lookup (priv->iter_map, iter); if (seq_iter == NULL) { g_critical ("DeeFilterModel can not insert before unknown iter"); return NULL; } priv->ignore_orig_signals = TRUE; new_iter = dee_model_insert_row_before (priv->orig_model, iter, row_members); priv->ignore_orig_signals = FALSE; seq_iter = g_sequence_insert_before (seq_iter, new_iter); g_hash_table_insert (priv->iter_map, new_iter, seq_iter); dee_serializable_model_inc_seqnum (self); g_signal_emit_by_name (self, "row-added", new_iter); return iter; } typedef struct { DeeCompareRowFunc cmp; gpointer user_data; guint n_cols; GVariant **row_buf; DeeModel *model; } CmpDispatchData; static gint _dispatch_cmp_func (DeeModelIter *iter, GVariant **row_spec, CmpDispatchData *data) { gint result, i; dee_model_get_row (data->model, iter, data->row_buf); result = data->cmp (data->row_buf, row_spec, data->user_data); for (i = 0; i < data->n_cols; i++) g_variant_unref (data->row_buf[i]); return result; } static DeeModelIter* dee_filter_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found) { DeeFilterModelPrivate *priv; GSequenceIter *iter; CmpDispatchData data; guint row_size, n_cols, i; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); g_return_val_if_fail (row_spec != NULL, NULL); g_return_val_if_fail (cmp_func != NULL, NULL); priv = DEE_FILTER_MODEL (self)->priv; if (out_was_found != NULL) *out_was_found = FALSE; n_cols = dee_model_get_n_columns (self); row_size = sizeof (gpointer) * n_cols; data.cmp = cmp_func; data.user_data = user_data; data.n_cols = n_cols; data.row_buf = g_alloca (row_size); data.model = self; iter = g_sequence_search (priv->iter_list, row_spec, (GCompareDataFunc)_dispatch_cmp_func, &data); /* Kinda awkward - if we did find the row then GSequence has placed just * after the row we wanted. If we did not find it, then we're in the right * place */ if (!g_sequence_iter_is_begin (iter)) { GSequenceIter *jter = g_sequence_iter_prev (iter); dee_model_get_row (self, g_sequence_get (jter), data.row_buf); if (cmp_func (data.row_buf, row_spec, user_data) == 0) { if (out_was_found != NULL) *out_was_found = TRUE; iter = jter; } for (i = 0; i < n_cols; i++) g_variant_unref (data.row_buf[i]); } if (g_sequence_iter_is_end (iter)) return dee_model_get_last_iter (self); else return (DeeModelIter *) g_sequence_get (iter); } static void dee_filter_model_remove (DeeModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_if_fail (DEE_IS_FILTER_MODEL (self)); priv = DEE_FILTER_MODEL (self)->priv; seq_iter = g_hash_table_lookup (priv->iter_map, iter); if (seq_iter == NULL) { g_critical ("Can not remove unknown iter from DeeFilterModel"); return; } g_hash_table_remove (priv->iter_map, iter); g_sequence_remove (seq_iter); priv->ignore_orig_signals = TRUE; dee_model_remove (priv->orig_model, iter); priv->ignore_orig_signals = FALSE; } /* FALL THROUGH: dee_filter_model_set_valist() */ /* FALL THROUGH: dee_filter_model_set_value() */ /* FALL THROUGH: dee_filter_model_set_value_silently() */ /* FALL THROUGH: dee_filter_model_get_valist() */ /* FALL THROUGH: dee_filter_model_get_value() */ /* FALL THROUGH: dee_filter_model_get_bool() */ /* FALL THROUGH: dee_filter_model_get_uchar() */ /* FALL THROUGH: dee_filter_model_get_int() */ /* FALL THROUGH: dee_filter_model_get_uint() */ /* FALL THROUGH: dee_filter_model_get_int64() */ /* FALL THROUGH: dee_filter_model_get_uint64() */ /* FALL THROUGH: dee_filter_model_get_double() */ /* FALL THROUGH: dee_filter_model_get_string() */ static DeeModelIter* dee_filter_model_get_first_iter (DeeModel *self) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = DEE_FILTER_MODEL (self)->priv; if (dee_filter_model_is_empty (self)) return dee_model_get_last_iter (priv->orig_model); seq_iter = g_sequence_get_begin_iter (priv->iter_list); return (DeeModelIter*) g_sequence_get (seq_iter); } /* FALL THROUGH: dee_filter_model_get_last_iter() */ static DeeModelIter* dee_filter_model_get_iter_at_row (DeeModel *self, guint row) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); priv = DEE_FILTER_MODEL (self)->priv; seq_iter = g_sequence_get_iter_at_pos (priv->iter_list, row); /* On out of bounds we return the end iter of the orig model */ if (seq_iter == g_sequence_get_end_iter (priv->iter_list)) return dee_model_get_last_iter (priv->orig_model); return (DeeModelIter*) g_sequence_get (seq_iter); } static DeeModelIter* dee_filter_model_next (DeeModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); g_return_val_if_fail (!dee_model_is_last (self, iter), NULL); priv = DEE_FILTER_MODEL (self)->priv; seq_iter = (GSequenceIter*) g_hash_table_lookup (priv->iter_map, iter); if (seq_iter == NULL) { g_critical ("Can not find next iter for unknown iter"); return NULL; } seq_iter = g_sequence_iter_next (seq_iter); if (g_sequence_iter_is_end (seq_iter)) return dee_model_get_last_iter (priv->orig_model); return g_sequence_get (seq_iter); } static DeeModelIter* dee_filter_model_prev (DeeModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), NULL); g_return_val_if_fail (!dee_model_is_first (self, iter), NULL); priv = DEE_FILTER_MODEL (self)->priv; seq_iter = (GSequenceIter*) g_hash_table_lookup (priv->iter_map, iter); if (seq_iter == NULL) { g_critical ("Can not find next iter for unknown iter"); return NULL; } seq_iter = g_sequence_iter_prev (seq_iter); return g_sequence_get (seq_iter); } static gboolean dee_filter_model_is_first (DeeModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), -1); priv = DEE_FILTER_MODEL (self)->priv; if (dee_filter_model_is_empty (self)) return iter == dee_model_get_last_iter (priv->orig_model); seq_iter = g_sequence_get_begin_iter (priv->iter_list); return g_sequence_get (seq_iter) == iter; } /* FALL THROUGH: dee_filter_model_is_last()*/ static guint dee_filter_model_get_position (DeeModel *self, DeeModelIter *iter) { DeeFilterModelPrivate *priv; GSequenceIter *seq_iter; g_return_val_if_fail (DEE_IS_FILTER_MODEL (self), 0); priv = DEE_FILTER_MODEL (self)->priv; seq_iter = (GSequenceIter*) g_hash_table_lookup (priv->iter_map, iter); if (seq_iter == NULL) { g_critical ("Can not find next iter for unknown iter"); return 0; } return (guint) ABS(g_sequence_iter_get_position (seq_iter)); } dee-1.2.7+15.04.20150304/src/dbus/0000755000015300001610000000000012475676370016352 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/src/dbus/com.canonical.Dee.Peer.xml0000644000015300001610000000103712475676210023160 0ustar pbuserpbgroup00000000000000 dee-1.2.7+15.04.20150304/src/dbus/com.canonical.Dee.Model.xml0000644000015300001610000000202012475676210023316 0ustar pbuserpbgroup00000000000000 dee-1.2.7+15.04.20150304/src/dee-shared-model.h0000644000015300001610000001213512475676210020660 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_SHARED_MODEL_H #define _HAVE_DEE_SHARED_MODEL_H #include #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_SHARED_MODEL (dee_shared_model_get_type ()) #define DEE_SHARED_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_SHARED_MODEL, DeeSharedModel)) #define DEE_SHARED_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_SHARED_MODEL, DeeSharedModelClass)) #define DEE_IS_SHARED_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_SHARED_MODEL)) #define DEE_IS_SHARED_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_SHARED_MODEL)) #define DEE_SHARED_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_SHARED_MODEL, DeeSharedModelClass)) typedef struct _DeeSharedModel DeeSharedModel; typedef struct _DeeSharedModelClass DeeSharedModelClass; typedef struct _DeeSharedModelPrivate DeeSharedModelPrivate; /** * DeeSharedModel: * * All fields in the DeeSharedModel structure are private and should never be * accessed directly */ struct _DeeSharedModel { /*< private >*/ DeeProxyModel parent; DeeSharedModelPrivate *priv; }; /** * DEE_SHARED_MODEL_DBUS_IFACE: * * String constant defining the name of the DBus Model interface. */ #define DEE_SHARED_MODEL_DBUS_IFACE "com.canonical.Dee.Model" struct _DeeSharedModelClass { /*< private >*/ DeeProxyModelClass parent_class; /*< private >*/ void (*_dee_shared_model_1) (void); void (*_dee_shared_model_2) (void); void (*_dee_shared_model_3) (void); void (*_dee_shared_model_4) (void); }; typedef enum { DEE_SHARED_MODEL_ERROR_LEADER_INVALIDATED } DeeSharedModelError; #define DEE_TYPE_SHARED_MODEL_ACCESS_MODE \ (dee_shared_model_access_mode_get_type ()) /** * DeeSharedModelAccessMode: * * Enumeration defining behavior of the model with regards to writes from * other peers in the swarm. */ typedef enum { DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE, DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE } DeeSharedModelAccessMode; /** * dee_shared_model_access_mode_get_type: * * The GType of #DeeSharedModelAccessMode * * Return value: the #GType of #DeeSharedModelAccessMode **/ GType dee_shared_model_access_mode_get_type (void); #define DEE_TYPE_SHARED_MODEL_FLUSH_MODE \ (dee_shared_model_flush_mode_get_type ()) /** * DeeSharedModelFlushMode: * * Enumeration defining flushing behavior of a shared model. */ typedef enum { DEE_SHARED_MODEL_FLUSH_MODE_AUTOMATIC, DEE_SHARED_MODEL_FLUSH_MODE_MANUAL } DeeSharedModelFlushMode; /** * dee_shared_model_flush_mode_get_type: * * The GType of #DeeSharedModelFlushMode * * Return value: the #GType of #DeeSharedModelFlushMode **/ GType dee_shared_model_flush_mode_get_type (void); /** * dee_shared_model_get_type: * * The GType of #DeeSharedModel * * Return value: the #GType of #DeeSharedModel **/ GType dee_shared_model_get_type (void); DeeModel* dee_shared_model_new (const gchar *name); DeeModel* dee_shared_model_new_for_peer (DeePeer *peer); DeeModel* dee_shared_model_new_with_back_end (const gchar *name, DeeModel *back_end); const gchar* dee_shared_model_get_swarm_name (DeeSharedModel *self); DeePeer* dee_shared_model_get_peer (DeeSharedModel *self); gboolean dee_shared_model_is_leader (DeeSharedModel *self); gboolean dee_shared_model_is_synchronized (DeeSharedModel *self); guint dee_shared_model_flush_revision_queue (DeeSharedModel *self); guint dee_shared_model_flush_revision_queue_sync (DeeSharedModel *self); void dee_shared_model_set_flush_mode (DeeSharedModel *self, DeeSharedModelFlushMode mode); DeeSharedModelFlushMode dee_shared_model_get_flush_mode (DeeSharedModel *self); G_END_DECLS #endif /* _HAVE_DEE_SHARED_MODEL_H */ dee-1.2.7+15.04.20150304/src/dee-filter.h0000644000015300001610000001333512475676210017604 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_FILTERS_H #define _HAVE_DEE_FILTERS_H #include #include #include G_BEGIN_DECLS /** * DeeFilterMapFunc: * @orig_model: The model containing the original data to filter * @filter_model: The model that will contain the filtered results. The * filter func must iterate over @orig_model and add all relevant * rows to @filter_model. This model is guaranteed to be empty * when the filter func is invoked * @user_data: (closure): User data passed together with the filter func * * Function used to collect the rows from a model that should be included in * a #DeeFilterModel. To add rows to @filter_model use the methods * dee_filter_model_append_iter(), dee_filter_model_prepend_iter(), * dee_filter_model_insert_iter(), and dee_filter_model_insert_iter_before(). * * The iteration over the original model is purposely left to the map func * in order to allow optimized iterations if the the caller has a priori * knowledge of the sorting and grouping of the data in the original model. */ typedef void (*DeeFilterMapFunc) (DeeModel *orig_model, DeeFilterModel *filter_model, gpointer user_data); /** * DeeFilterMapNotify: * @orig_model: The model containing the added row * @orig_iter: A #DeeModelIter pointing to the new row in @orig_model * @filter_model: The model that was also passed to the #DeeModelMapFunc * of the #DeeFilter this functions is a part of * @user_data: (closure): User data for the #DeeFilter * * Callback invoked when a row is added to @orig_model. To add rows to * @filter_model use the methods dee_filter_model_append_iter(), * dee_filter_model_prepend_iter(), dee_filter_model_insert_iter(), * and dee_filter_model_insert_iter_before(). * * Returns: %TRUE if @orig_iter was added to @filter_model */ typedef gboolean (*DeeFilterMapNotify) (DeeModel *orig_model, DeeModelIter *orig_iter, DeeFilterModel *filter_model, gpointer user_data); /** * DeeFilter: * @map_func: (scope notified): The #DeeModelMapFunc used to construct * the initial contents of a #DeeFilterModel * @map_notify: (scope notified): Callback invoked when the original model changes * @destroy: Callback for freeing the @user_data * @userdata (closure): Free form user data associated with the filter. * This pointer will be passed to @map_func and @map_notify * * Structure encapsulating the mapping logic used to construct a #DeeFilterModel */ struct _DeeFilter { DeeFilterMapFunc map_func; DeeFilterMapNotify map_notify; GDestroyNotify destroy; gpointer userdata; /*< private >*/ gpointer _padding_1; gpointer _padding_2; gpointer _padding_3; gpointer _padding_4; }; gboolean dee_filter_notify (DeeFilter *filter, DeeModelIter *orig_iter, DeeModel *orig_model, DeeFilterModel *filter_model); void dee_filter_map (DeeFilter *filter, DeeModel *orig_model, DeeFilterModel *filter_model); void dee_filter_destroy (DeeFilter *filter); void dee_filter_new (DeeFilterMapFunc map_func, DeeFilterMapNotify map_notify, gpointer userdata, GDestroyNotify destroy, DeeFilter *out_filter); void dee_filter_new_sort (DeeCompareRowFunc cmp_row, gpointer cmp_user_data, GDestroyNotify cmp_destroy, DeeFilter *out_filter); void dee_filter_new_collator (guint column, DeeFilter *out_filter); void dee_filter_new_collator_desc (guint column, DeeFilter *out_filter); void dee_filter_new_for_key_column (guint column, const gchar *key, DeeFilter *out_filter); void dee_filter_new_for_any_column (guint column, GVariant *value, DeeFilter *out_filter); void dee_filter_new_regex (guint column, GRegex *regex, DeeFilter *out_filter); G_END_DECLS #endif /* _HAVE_DEE_FILTERS_H */ dee-1.2.7+15.04.20150304/src/dee-client.h0000644000015300001610000000416112475676210017572 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_CLIENT_H #define _HAVE_DEE_CLIENT_H #include #include #include "dee-peer.h" G_BEGIN_DECLS #define DEE_TYPE_CLIENT dee_client_get_type() #define DEE_CLIENT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_CLIENT, DeeClient)) #define DEE_CLIENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), DEE_TYPE_CLIENT, DeeClientClass)) #define DEE_IS_CLIENT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_CLIENT)) #define DEE_IS_CLIENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), DEE_TYPE_CLIENT)) #define DEE_CLIENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), DEE_TYPE_CLIENT, DeeClientClass)) typedef struct _DeeClientPrivate DeeClientPrivate; typedef struct { /*< private >*/ DeePeer parent; DeeClientPrivate *priv; } DeeClient; typedef struct { /*< private >*/ DeePeerClass parent_class; } DeeClientClass; /** * dee_client_get_type: * * The GType of #DeeClient. * * Return value: the #GType of #DeeClient. **/ GType dee_client_get_type (void); DeeClient* dee_client_new (const gchar *swarm_name); DeeClient* dee_client_new_for_address (const gchar* swarm_name, const gchar* bus_address); G_END_DECLS #endif /* _HAVE_DEE_CLIENT_H */ dee-1.2.7+15.04.20150304/src/dee-hash-index.h0000644000015300001610000000465612475676210020355 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_HASH_INDEX_H #define _HAVE_DEE_HASH_INDEX_H #include #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_HASH_INDEX (dee_hash_index_get_type ()) #define DEE_HASH_INDEX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_HASH_INDEX, DeeHashIndex)) #define DEE_HASH_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_HASH_INDEX, DeeHashIndexClass)) #define DEE_IS_HASH_INDEX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_HASH_INDEX)) #define DEE_IS_HASH_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_HASH_INDEX)) #define DEE_HASH_INDEX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_HASH_INDEX, DeeHashIndexClass)) typedef struct _DeeHashIndexClass DeeHashIndexClass; typedef struct _DeeHashIndex DeeHashIndex; typedef struct _DeeHashIndexPrivate DeeHashIndexPrivate; /** * DeeHashIndex: * * All fields in the DeeHashIndex structure are private and should never be * accessed directly */ struct _DeeHashIndex { /*< private >*/ DeeIndex parent; DeeHashIndexPrivate *priv; }; struct _DeeHashIndexClass { DeeIndexClass parent_class; }; GType dee_hash_index_get_type (void); DeeHashIndex* dee_hash_index_new (DeeModel *model, DeeAnalyzer *analyzer, DeeModelReader *reader); G_END_DECLS #endif /* _HAVE_DEE_HASH_INDEX_H */ dee-1.2.7+15.04.20150304/src/dee-tree-index.c0000644000015300001610000005465612475676210020371 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-tree-index * @short_description: A #DeeTreeIndex backed by a balanced binary tree * @include: dee.h * * #DeeTreeIndex is an implementation of #DeeIndex which is backed * by a balanced binary tree. This means that it in addition to * #DEE_TERM_MATCH_EXACT also supports #DEE_TERM_MATCH_PREFIX as a flag in * dee_index_lookup(). * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "dee-tree-index.h" #include "dee-result-set.h" #include "dee-glist-result-set.h" #include "trace-log.h" G_DEFINE_TYPE (DeeTreeIndex, dee_tree_index, DEE_TYPE_INDEX); #define DEE_TREE_INDEX_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_TREE_INDEX, DeeTreeIndexPrivate)) /* * FORWARDS */ typedef struct { /* The term string is owned by the DeeTermList of the index */ const gchar *term; /* Cached collation key for the term string */ const gchar *col_key; /* Maps row iter -> pointer to guint with ref count */ GHashTable *rows; } Term; /* * DeeIndex API forwards */ static DeeResultSet* dee_tree_index_lookup (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags); static void dee_tree_index_foreach (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata); static guint dee_tree_index_get_n_terms (DeeIndex *self); static guint dee_tree_index_get_n_rows (DeeIndex *self); static guint dee_tree_index_get_n_rows_for_term (DeeIndex *self, const gchar *term); static guint dee_tree_index_get_supported_term_match_flags (DeeIndex *self); /* * Private functions */ static void on_row_added (DeeIndex *self, DeeModelIter *iter, DeeModel *model); static void on_row_removed (DeeIndex *self, DeeModelIter *iter, DeeModel *model); static void on_row_changed (DeeIndex *self, DeeModelIter *iter, DeeModel *model); static Term* term_new (const gchar *term, const gchar *col_key); static void term_destroy (Term* term); static void term_ref_row (Term *term, DeeModelIter *iter); static void term_unref_row (Term *term, DeeModelIter *iter); static guint term_n_rows (Term *term); static GList* term_rows (Term *term); static gint term_cmp (Term *term, Term *other, DeeAnalyzer *analyzer); static GSequenceIter* find_term (GSequence *terms, const gchar *term, const gchar *col_key, DeeAnalyzer *analyzer); static GSequenceIter* find_term_real (GSequence *terms, const gchar *term, const gchar *col_key, DeeAnalyzer *analyzer, DeeTermMatchFlag flags); /* * Term impl. term and colkeys are owned by our analyzer */ static Term* term_new (const gchar *term, const gchar *col_key) { Term *self; g_return_val_if_fail (term != NULL, NULL); g_return_val_if_fail (col_key != NULL, NULL); self = g_slice_new (Term); self->term = term; self->col_key = col_key; self->rows = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free); return self; } static void term_destroy (Term* term) { g_hash_table_unref (term->rows); g_slice_free (Term, term); } static void term_ref_row (Term *term, DeeModelIter *iter) { guint *ref_count; ref_count = g_hash_table_lookup (term->rows, iter); if (ref_count == NULL) { ref_count = g_new (guint, 1); *ref_count = 1; g_hash_table_insert (term->rows, iter, ref_count); } else { *ref_count = *ref_count + 1; } } static void term_unref_row (Term *term, DeeModelIter *iter) { guint *ref_count; ref_count = g_hash_table_lookup (term->rows, iter); if (ref_count == NULL) { g_critical ("Trying to unref unknown row %p for term '%s'", iter, term->term); } else { *ref_count = *ref_count - 1; if (*ref_count == 0) { g_hash_table_remove(term->rows, iter); } } } static guint term_n_rows (Term *term) { return g_hash_table_size(term->rows); } static GList* term_rows (Term *term) { return g_hash_table_get_keys (term->rows); } static gint term_cmp (Term *term, Term *other, DeeAnalyzer *analyzer) { return dee_analyzer_collate_cmp (analyzer, term->col_key, other->col_key); } /* Search priv->terms for a string from priv->term_list. * ! Doesn't work for strings not in priv->term_list ! */ static GSequenceIter* find_term (GSequence *terms, const gchar *term, const gchar *col_key, DeeAnalyzer *analyzer) { return find_term_real (terms, term, col_key, analyzer, DEE_TERM_MATCH_EXACT); } static GSequenceIter* find_term_real (GSequence *terms, const gchar *term, const gchar *col_key, DeeAnalyzer *analyzer, DeeTermMatchFlag flags) { Term search_term, *term_result; GSequenceIter *found_iter, *iter, *previous, *begin, *end; begin = g_sequence_get_begin_iter (terms); end = g_sequence_get_end_iter (terms); /* If the index is empty don't bother searching */ if (begin == end) return NULL; search_term.col_key = col_key; if (flags & DEE_TERM_MATCH_EXACT) { // FIXME: do we need to make sure this is the first iter? return g_sequence_lookup (terms, &search_term, (GCompareDataFunc) term_cmp, analyzer); } else if (flags & DEE_TERM_MATCH_PREFIX) { found_iter = g_sequence_search (terms, &search_term, (GCompareDataFunc) term_cmp, analyzer); /* What can happen now is: * 1) found_iter has our prefix * 2) found_iter as well as the found_iter->previous have our prefix * 3) found_iter doesn't have it, but found_iter->previous does * 4) there isn't any nearby iter that has the prefix */ previous = iter = found_iter; /* We might be placed after the iter we want */ while (previous != begin) { previous = g_sequence_iter_prev (previous); term_result = g_sequence_get (previous); if (g_str_has_prefix (term_result->term, term)) iter = previous; else break; } if (iter == found_iter && iter != end) { /* We never checked this one */ term_result = g_sequence_get (iter); if (g_str_has_prefix (term_result->term, term)) return iter; } else return iter; } else { g_critical ("Unexpected term match flags %u", flags); return NULL; } return NULL; } /* * GOBJECT STUFF */ struct _DeeTreeIndexPrivate { /* Holds Term instances as data members */ GSequence *terms; /* Holds map of DeeModelIter -> GPtrArray */ GHashTable *row_terms; /* All terms are stored here */ DeeTermList *term_list; gulong on_row_added_handler; gulong on_row_removed_handler; gulong on_row_changed_handler; }; enum { PROP_0, }; /* GObject stuff */ static void dee_tree_index_finalize (GObject *object) { DeeTreeIndexPrivate *priv = DEE_TREE_INDEX (object)->priv; DeeModel *model = dee_index_get_model (DEE_INDEX (object)); if (priv->on_row_added_handler) g_signal_handler_disconnect(model, priv->on_row_added_handler); if (priv->on_row_removed_handler) g_signal_handler_disconnect(model, priv->on_row_removed_handler); if (priv->on_row_changed_handler) g_signal_handler_disconnect(model, priv->on_row_changed_handler); if (priv->terms) { g_sequence_free (priv->terms); priv->terms = NULL; } if (priv->row_terms) { g_hash_table_unref (priv->row_terms); priv->row_terms = NULL; } if (priv->term_list) { g_object_unref (priv->term_list); priv->term_list = NULL; } G_OBJECT_CLASS (dee_tree_index_parent_class)->finalize (object); } static void dee_tree_index_constructed (GObject *object) { DeeTreeIndexPrivate *priv = DEE_TREE_INDEX (object)->priv; DeeIndex *self = DEE_INDEX (object); DeeModel *model = dee_index_get_model (self); DeeModelIter *iter; /* Listen for changes in the model so we automagically pick those up */ priv->on_row_added_handler = g_signal_connect_swapped (model, "row-added", G_CALLBACK (on_row_added), self); priv->on_row_removed_handler = g_signal_connect_swapped (model, "row-removed", G_CALLBACK (on_row_removed), self); priv->on_row_changed_handler = g_signal_connect_swapped (model, "row-changed", G_CALLBACK (on_row_changed), self); /* Index existing rows in the model */ iter = dee_model_get_first_iter (model); while (!dee_model_is_last (model, iter)) { on_row_added (self, iter, model); iter = dee_model_next (model, iter); } } static void dee_tree_index_class_init (DeeTreeIndexClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); DeeIndexClass *idx_class = DEE_INDEX_CLASS (klass); obj_class->finalize = dee_tree_index_finalize; obj_class->constructed = dee_tree_index_constructed; idx_class->lookup = dee_tree_index_lookup; idx_class->foreach = dee_tree_index_foreach; idx_class->get_n_terms = dee_tree_index_get_n_terms; idx_class->get_n_rows = dee_tree_index_get_n_rows; idx_class->get_n_rows_for_term = dee_tree_index_get_n_rows_for_term; idx_class->get_supported_term_match_flags = dee_tree_index_get_supported_term_match_flags; /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeTreeIndexPrivate)); } static void dee_tree_index_init (DeeTreeIndex *self) { self->priv = DEE_TREE_INDEX_GET_PRIVATE (self); self->priv->terms = g_sequence_new ((GDestroyNotify) term_destroy); self->priv->row_terms = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref); self->priv->term_list = g_object_new (DEE_TYPE_TERM_LIST, NULL); } /* * IMPLEMENTATION */ static DeeResultSet* dee_tree_index_lookup (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags) { DeeTreeIndexPrivate *priv; DeeAnalyzer *analyzer; GSequenceIter *term_iter, *end; Term *term_data; gchar *col_key; g_return_val_if_fail (DEE_IS_TREE_INDEX (self), NULL); g_return_val_if_fail (term != NULL, NULL); priv = DEE_TREE_INDEX (self)->priv; analyzer = dee_index_get_analyzer (self); col_key = dee_analyzer_collate_key (analyzer, term); term_iter = find_term_real (priv->terms, term, col_key, analyzer, flags); g_free (col_key); if (term_iter == NULL || term_iter == g_sequence_get_end_iter (priv->terms)) { return dee_glist_result_set_new (NULL, /* The empty GList */ dee_index_get_model (self), NULL); } if (flags & DEE_TERM_MATCH_EXACT) { term_data = g_sequence_get (term_iter); return dee_glist_result_set_new (term_rows (term_data), dee_index_get_model (self), G_OBJECT (self)); } else if (flags & DEE_TERM_MATCH_PREFIX) { GList *iter; GList *buf = NULL; GHashTable *iter_set = g_hash_table_new (g_direct_hash, g_direct_equal); end = g_sequence_get_end_iter (priv->terms); term_data = g_sequence_get (term_iter); /* We can't use collation keys for prefix matching */ while (g_str_has_prefix (term_data->term, term)) { GList *rows = term_rows (term_data); iter = rows; /* There may be duplicated iters in the result list */ while (iter != NULL) { if (g_hash_table_lookup_extended (iter_set, iter->data, NULL, NULL)) { GList *to_delete = iter; iter = iter->next; rows = g_list_delete_link (rows, to_delete); } else { /* Add to the iter set */ g_hash_table_replace (iter_set, iter->data, iter->data); iter = iter->next; } } buf = g_list_concat (buf, rows); term_iter = g_sequence_iter_next (term_iter); if (term_iter == end) break; term_data = g_sequence_get (term_iter); } g_hash_table_unref (iter_set); /* We use a dummy GObject to bolt ref counting onto the GList */ GObject *buf_owner = g_object_new (G_TYPE_OBJECT, NULL); g_object_set_data_full (buf_owner, "buf", buf, (GDestroyNotify) g_list_free); DeeResultSet *results = dee_glist_result_set_new (buf, dee_index_get_model (self), buf_owner); g_object_unref (buf_owner); return results; } else { g_critical ("Unexpected term match flags %u", flags); return NULL; } } static void dee_tree_index_foreach (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata) { DeeTreeIndexPrivate *priv; DeeModel *model; DeeAnalyzer *analyzer; DeeResultSet *results; GSequenceIter *iter, *end; Term *term_data; gchar *col_key; g_return_if_fail (DEE_IS_TREE_INDEX (self)); g_return_if_fail (func != NULL); priv = DEE_TREE_INDEX (self)->priv; model = dee_index_get_model (self); if (start_term == NULL) iter = g_sequence_get_begin_iter (priv->terms); else { analyzer = dee_index_get_analyzer (self); col_key = dee_analyzer_collate_key (analyzer, start_term); iter = find_term (priv->terms, start_term, col_key, analyzer); g_free (col_key); if (iter == NULL || iter == g_sequence_get_end_iter (priv->terms)) return; } end = g_sequence_get_end_iter (priv->terms); while (iter != end) { term_data = g_sequence_get (iter); results = dee_glist_result_set_new (term_rows (term_data), model, G_OBJECT (self)); func (start_term, results, userdata); g_object_unref (results); iter = g_sequence_iter_next (iter); } } static guint dee_tree_index_get_n_terms (DeeIndex *self) { DeeTreeIndexPrivate *priv; g_return_val_if_fail (DEE_IS_TREE_INDEX (self), 0); priv = DEE_TREE_INDEX (self)->priv; return g_sequence_get_length(priv->terms); } static guint dee_tree_index_get_n_rows (DeeIndex *self) { DeeTreeIndexPrivate *priv; g_return_val_if_fail (DEE_IS_TREE_INDEX (self), 0); priv = DEE_TREE_INDEX (self)->priv; return g_hash_table_size(priv->row_terms); } static guint dee_tree_index_get_n_rows_for_term (DeeIndex *self, const gchar *term) { DeeTreeIndexPrivate *priv; Term *term_data; GSequenceIter *term_iter; DeeAnalyzer *analyzer; gchar *col_key; g_return_val_if_fail (DEE_IS_TREE_INDEX (self), 0); g_return_val_if_fail (term != NULL, 0); priv = DEE_TREE_INDEX (self)->priv; analyzer = dee_index_get_analyzer (self); col_key = dee_analyzer_collate_key (analyzer, term); term_iter = find_term (priv->terms, term, col_key, analyzer); g_free (col_key); if (term_iter == NULL || term_iter == g_sequence_get_end_iter (priv->terms)) return 0; term_data = g_sequence_get (term_iter); return term_n_rows (term_data); } static guint dee_tree_index_get_supported_term_match_flags (DeeIndex *self) { return DEE_TERM_MATCH_EXACT | DEE_TERM_MATCH_PREFIX; } static void on_row_added (DeeIndex *self, DeeModelIter *iter, DeeModel *model) { DeeTreeIndexPrivate *priv; DeeAnalyzer *analyzer; DeeModelReader *reader; DeeTermList *col_keys; guint i, num_terms; const gchar *term, *colkey; gchar *term_stream; GSequenceIter *term_iter; Term *term_data; GPtrArray *row_term_data; priv = DEE_TREE_INDEX (self)->priv; analyzer = dee_index_get_analyzer (self); reader = dee_index_get_reader (self); dee_term_list_clear (priv->term_list); col_keys = dee_term_list_clone (priv->term_list); term_stream = dee_model_reader_read (reader, model, iter); dee_analyzer_analyze (analyzer, term_stream, priv->term_list, col_keys); num_terms = dee_term_list_num_terms (priv->term_list); if (num_terms == 0) { g_free (term_stream); g_object_unref (col_keys); return; } /* Make sure we have row_terms registered for this iter */ row_term_data = (GPtrArray*) g_hash_table_lookup (priv->row_terms, iter); if (row_term_data == NULL) { row_term_data = g_ptr_array_sized_new (num_terms); g_hash_table_insert (priv->row_terms, iter, row_term_data); } for (i = 0; i < num_terms; i++) { /* Important: The following works because @term lives in the scope * of priv->term_list. This makes the 'const gchar*' to 'gpointer' * casts valid. Yes, they even survive term_list.clear(). */ colkey = dee_term_list_get_term (col_keys, i); term = dee_term_list_get_term (priv->term_list, i); /* Update priv->terms */ term_iter = find_term (priv->terms, term, colkey, analyzer); if (term_iter == NULL || term_iter == g_sequence_get_end_iter (priv->terms)) { term_data = term_new (term, colkey); g_sequence_insert_sorted (priv->terms, term_data, (GCompareDataFunc) term_cmp, analyzer); } else term_data = g_sequence_get (term_iter); /* Register the row for the term */ term_ref_row (term_data, iter); /* Update reverse map row -> Terms */ g_ptr_array_add(row_term_data, term_data); } g_object_unref (col_keys); } static void on_row_removed (DeeIndex *self, DeeModelIter *iter, DeeModel *model) { DeeTreeIndexPrivate *priv; DeeAnalyzer *analyzer; Term *term_data; GPtrArray *row_term_data; gint i; GSequenceIter *term_iter; priv = DEE_TREE_INDEX (self)->priv; analyzer = dee_index_get_analyzer (self); row_term_data = (GPtrArray*) g_hash_table_lookup (priv->row_terms, iter); /* We have no terms for this row */ if (row_term_data == NULL) return; /* Iterate over all terms for this row and remove the row from those terms */ for (i = 0; i < row_term_data->len; i++) { term_data = g_ptr_array_index (row_term_data, i); if (term_data == NULL) continue; term_unref_row (term_data, iter); /* If there are no more rows for this term * we can remove the term from the index altogether */ if (term_n_rows (term_data) == 0) { /* Removing the term from the sequence also frees it */ term_iter = find_term (priv->terms, term_data->term, term_data->col_key, analyzer); g_sequence_remove (term_iter); } } /* Remove the row from the reverse map row -> terms */ g_hash_table_remove (priv->row_terms, iter); } static void on_row_changed (DeeIndex *self, DeeModelIter *iter, DeeModel *model) { on_row_removed (self, iter, model); on_row_added (self, iter, model); } /* * API */ /** * dee_tree_index_new: * @model: The model to index * @analyzer: The #DeeAnalyzer used to tokenize and filter the terms extracted * by @reader * @reader: The #DeeModelReader used to extract terms from the model * * Create a new tree index. * * Returns: A newly allocated tree index. Free with g_object_unref(). */ DeeTreeIndex* dee_tree_index_new (DeeModel *model, DeeAnalyzer *analyzer, DeeModelReader *reader) { DeeTreeIndex *self; g_return_val_if_fail (DEE_IS_MODEL (model), NULL); g_return_val_if_fail (DEE_IS_ANALYZER (analyzer), NULL); g_return_val_if_fail (reader != NULL, NULL); self = (DeeTreeIndex*) g_object_new (DEE_TYPE_TREE_INDEX, "model", model, "analyzer", analyzer, "reader", reader, NULL); return self; } dee-1.2.7+15.04.20150304/src/dee-proxy-model.c0000644000015300001610000010700312475676210020565 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-proxy-model * @short_description: A model that wraps another underlying #DeeModel * @include: dee.h * * #DeeProxyModel wraps another #DeeModel instance and use it as a back end * by proxuing all method calls down to the back end. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "dee-model.h" #include "dee-proxy-model.h" #include "dee-serializable-model.h" #include "dee-marshal.h" #include "trace-log.h" static void dee_proxy_model_model_iface_init (DeeModelIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeProxyModel, dee_proxy_model, DEE_TYPE_SERIALIZABLE_MODEL, G_IMPLEMENT_INTERFACE (DEE_TYPE_MODEL, dee_proxy_model_model_iface_init)); #define DEE_PROXY_MODEL_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_PROXY_MODEL, DeeProxyModelPrivate)) enum { PROP_0, PROP_BACK_END, PROP_PROXY_SIGNALS, PROP_INHERIT_SEQNUMS }; /** * DeeProxyModelPrivate: * * Ignore this structure. */ struct _DeeProxyModelPrivate { /* The backend model holding the actual data */ DeeModel *back_end; /* Whether to use the seqnums of the backend model (if it's versioned), * or to use our own seqnums */ gboolean inherit_seqnums; /* Whether or not to automatically forward signals from the back end */ gboolean proxy_signals; /* Signals handlers for relaying signals from the back end */ gulong row_added_handler; gulong row_removed_handler; gulong row_changed_handler; gulong changeset_started_handler; gulong changeset_finished_handler; }; #define DEE_PROXY_MODEL_BACK_END(model) (DEE_PROXY_MODEL(model)->priv->back_end) #define SUPER_CLASS DEE_SERIALIZABLE_MODEL_CLASS (dee_proxy_model_parent_class) /* * DeeModel forward declarations */ static void dee_proxy_model_set_schema_full (DeeModel *self, const gchar* const *schema, guint num_columns); static const gchar* const* dee_proxy_model_get_schema (DeeModel *self, guint *num_columns); static const gchar* dee_proxy_model_get_column_schema (DeeModel *self, guint column); static const gchar* dee_proxy_model_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column); static gint dee_proxy_model_get_column_index (DeeModel *self, const gchar *column_name); static void dee_proxy_model_set_column_names (DeeModel *self, const gchar **column_names, guint num_columns); static const gchar** dee_proxy_model_get_column_names (DeeModel *self, guint *num_columns); static void dee_proxy_model_register_vardict_schema (DeeModel *self, guint column, GHashTable *schema); static GHashTable* dee_proxy_model_get_vardict_schema (DeeModel *self, guint column); static guint dee_proxy_model_get_n_columns (DeeModel *self); static guint dee_proxy_model_get_n_rows (DeeModel *self); static void dee_proxy_model_clear (DeeModel *self); static DeeModelIter* dee_proxy_model_append_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_proxy_model_prepend_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_proxy_model_insert_row (DeeModel *self, guint pos, GVariant **row_members); static DeeModelIter* dee_proxy_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static DeeModelIter* dee_proxy_model_insert_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data); static DeeModelIter* dee_proxy_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found); static void dee_proxy_model_remove (DeeModel *self, DeeModelIter *iter); static void dee_proxy_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value); static void dee_proxy_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static GVariant* dee_proxy_model_get_value (DeeModel *self, DeeModelIter *iter, guint column); static DeeModelIter* dee_proxy_model_get_first_iter (DeeModel *self); static DeeModelIter* dee_proxy_model_get_last_iter (DeeModel *self); static DeeModelIter* dee_proxy_model_get_iter_at_row (DeeModel *self, guint row); static gboolean dee_proxy_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column); static guchar dee_proxy_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column); static gint32 dee_proxy_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column); static guint32 dee_proxy_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column); static gint64 dee_proxy_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column); static guint64 dee_proxy_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column); static gdouble dee_proxy_model_get_double (DeeModel *self, DeeModelIter *iter, guint column); static const gchar* dee_proxy_model_get_string (DeeModel *self, DeeModelIter *iter, guint column); static DeeModelIter* dee_proxy_model_next (DeeModel *self, DeeModelIter *iter); static DeeModelIter* dee_proxy_model_prev (DeeModel *self, DeeModelIter *iter); static gboolean dee_proxy_model_is_first (DeeModel *self, DeeModelIter *iter); static gboolean dee_proxy_model_is_last (DeeModel *self, DeeModelIter *iter); static guint dee_proxy_model_get_position (DeeModel *self, DeeModelIter *iter); static DeeModelTag* dee_proxy_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy); static gpointer dee_proxy_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag); static void dee_proxy_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value); static void dee_proxy_model_begin_changeset (DeeModel *self); static void dee_proxy_model_end_changeset (DeeModel *self); /* * Callbacks for relaying signals from the back end model */ static void on_back_end_row_added (DeeProxyModel *self, DeeModelIter *iter); static void on_back_end_row_removed (DeeProxyModel *self, DeeModelIter *iter); static void on_back_end_row_changed (DeeProxyModel *self, DeeModelIter *iter); static void on_back_end_changeset_started (DeeProxyModel *self, DeeModel *model); static void on_back_end_changeset_finished (DeeProxyModel *self, DeeModel *model); /* * Overrides for DeeSerializableModel */ static guint64 dee_proxy_model_get_seqnum (DeeModel *self); static void dee_proxy_model_set_seqnum (DeeModel *self, guint64 seqnum); static guint64 dee_proxy_model_inc_seqnum (DeeModel *self); /* GObject Init */ static void dee_proxy_model_finalize (GObject *object) { DeeProxyModelPrivate *priv = DEE_PROXY_MODEL (object)->priv; if (priv->back_end) { if (priv->row_added_handler != 0) g_signal_handler_disconnect (priv->back_end, priv->row_added_handler); if (priv->row_removed_handler != 0) g_signal_handler_disconnect (priv->back_end, priv->row_removed_handler); if (priv->row_changed_handler != 0) g_signal_handler_disconnect (priv->back_end, priv->row_changed_handler); if (priv->changeset_started_handler != 0) g_signal_handler_disconnect (priv->back_end, priv->changeset_started_handler); if (priv->changeset_finished_handler != 0) g_signal_handler_disconnect (priv->back_end, priv->changeset_finished_handler); g_object_unref (priv->back_end); } G_OBJECT_CLASS (dee_proxy_model_parent_class)->finalize (object); } /* GObject Post-Init. Properties has been set */ static void dee_proxy_model_constructed (GObject *object) { DeeProxyModelPrivate *priv = DEE_PROXY_MODEL (object)->priv; if (priv->back_end == NULL) { g_critical ("You must set the 'back-end' property of " "the DeeProxyModel upon creation."); return; } /* Connect to signals on the back-end model so we can relay them */ if (priv->proxy_signals) { priv->row_added_handler = g_signal_connect_swapped (priv->back_end, "row-added", G_CALLBACK (on_back_end_row_added), object); priv->row_removed_handler = g_signal_connect_swapped (priv->back_end, "row-removed", G_CALLBACK (on_back_end_row_removed), object); priv->row_changed_handler = g_signal_connect_swapped (priv->back_end, "row-changed", G_CALLBACK (on_back_end_row_changed), object); priv->changeset_started_handler = g_signal_connect_swapped (priv->back_end, "changeset-started", G_CALLBACK (on_back_end_changeset_started), object); priv->changeset_finished_handler = g_signal_connect_swapped (priv->back_end, "changeset-finished", G_CALLBACK (on_back_end_changeset_finished), object); } /* GObjectClass has NULL 'constructed' member, but we add this check for * future robustness if we ever move to another base class */ if (G_OBJECT_CLASS (dee_proxy_model_parent_class)->constructed != NULL) G_OBJECT_CLASS (dee_proxy_model_parent_class)->constructed (object); } static void dee_proxy_model_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeeProxyModelPrivate *priv = DEE_PROXY_MODEL (object)->priv; switch (id) { case PROP_BACK_END: priv->back_end = g_value_dup_object (value); break; case PROP_PROXY_SIGNALS: priv->proxy_signals = g_value_get_boolean (value); break; case PROP_INHERIT_SEQNUMS: priv->inherit_seqnums = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_proxy_model_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { case PROP_BACK_END: g_value_set_object (value, DEE_PROXY_MODEL (object)->priv->back_end); break; case PROP_PROXY_SIGNALS: g_value_set_boolean (value, DEE_PROXY_MODEL (object)->priv->proxy_signals); break; case PROP_INHERIT_SEQNUMS: g_value_set_boolean (value, DEE_PROXY_MODEL (object)->priv->inherit_seqnums); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_proxy_model_class_init (DeeProxyModelClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); DeeSerializableModelClass *dvm_class = DEE_SERIALIZABLE_MODEL_CLASS (klass); GParamSpec *pspec; obj_class->finalize = dee_proxy_model_finalize; obj_class->constructed = dee_proxy_model_constructed; obj_class->set_property = dee_proxy_model_set_property; obj_class->get_property = dee_proxy_model_get_property; dvm_class->get_seqnum = dee_proxy_model_get_seqnum; dvm_class->set_seqnum = dee_proxy_model_set_seqnum; dvm_class->inc_seqnum = dee_proxy_model_inc_seqnum; /** * DeeProxyModel:back-end: * * The backend model used by this proxy model. **/ pspec = g_param_spec_object ("back-end", "Back end", "Back end model", DEE_TYPE_MODEL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_BACK_END, pspec); /** * DeeProxyModel:proxy-signals: * * Boolean property defining whether or not to automatically forward signals * from the back end model. This is especially useful for sub classes wishing * to do their own more advanced signal forwarding. **/ pspec = g_param_spec_boolean ("proxy-signals", "Proxy signals", "Whether or not to automatically forward signals from the back end", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_PROXY_SIGNALS, pspec); /** * DeeProxyModel:inherit-seqnums: * * Boolean property defining whether sequence numbers will be inherited * from the back end model. * You will most likely want to set this property to false * if the implementation manipulates with the rows in the model and keep * track of seqnums yourself. **/ pspec = g_param_spec_boolean ("inherit-seqnums", "Inherit seqnums", "Whether or not to inherit seqnums", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_INHERIT_SEQNUMS, pspec); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeProxyModelPrivate)); } static void dee_proxy_model_model_iface_init (DeeModelIface *iface) { iface->set_schema_full = dee_proxy_model_set_schema_full; iface->get_schema = dee_proxy_model_get_schema; iface->get_column_schema = dee_proxy_model_get_column_schema; iface->get_column_index = dee_proxy_model_get_column_index; iface->set_column_names_full = dee_proxy_model_set_column_names; iface->get_column_names = dee_proxy_model_get_column_names; iface->get_field_schema = dee_proxy_model_get_field_schema; iface->get_n_columns = dee_proxy_model_get_n_columns; iface->get_n_rows = dee_proxy_model_get_n_rows; iface->clear = dee_proxy_model_clear; iface->prepend_row = dee_proxy_model_prepend_row; iface->append_row = dee_proxy_model_append_row; iface->insert_row = dee_proxy_model_insert_row; iface->insert_row_before = dee_proxy_model_insert_row_before; iface->insert_row_sorted = dee_proxy_model_insert_row_sorted; iface->find_row_sorted = dee_proxy_model_find_row_sorted; iface->remove = dee_proxy_model_remove; iface->set_value = dee_proxy_model_set_value; iface->set_row = dee_proxy_model_set_row; iface->get_value = dee_proxy_model_get_value; iface->get_first_iter = dee_proxy_model_get_first_iter; iface->get_last_iter = dee_proxy_model_get_last_iter; iface->get_iter_at_row = dee_proxy_model_get_iter_at_row; iface->get_bool = dee_proxy_model_get_bool; iface->get_uchar = dee_proxy_model_get_uchar; iface->get_int32 = dee_proxy_model_get_int32; iface->get_uint32 = dee_proxy_model_get_uint32; iface->get_int64 = dee_proxy_model_get_int64; iface->get_uint64 = dee_proxy_model_get_uint64; iface->get_double = dee_proxy_model_get_double; iface->get_string = dee_proxy_model_get_string; iface->next = dee_proxy_model_next; iface->prev = dee_proxy_model_prev; iface->is_first = dee_proxy_model_is_first; iface->is_last = dee_proxy_model_is_last; iface->get_position = dee_proxy_model_get_position; iface->register_tag = dee_proxy_model_register_tag; iface->get_tag = dee_proxy_model_get_tag; iface->set_tag = dee_proxy_model_set_tag; iface->begin_changeset = dee_proxy_model_begin_changeset; iface->end_changeset = dee_proxy_model_end_changeset; iface->register_vardict_schema = dee_proxy_model_register_vardict_schema; iface->get_vardict_schema = dee_proxy_model_get_vardict_schema; } static void dee_proxy_model_init (DeeProxyModel *model) { DeeProxyModelPrivate *priv; priv = model->priv = DEE_PROXY_MODEL_GET_PRIVATE (model); priv->back_end = NULL; priv->inherit_seqnums = TRUE; priv->row_added_handler = 0; priv->row_removed_handler = 0; priv->row_changed_handler = 0; priv->changeset_started_handler = 0; priv->changeset_finished_handler = 0; } /* * DeeModel Interface Implementation */ static void dee_proxy_model_set_schema_full (DeeModel *self, const gchar* const *schema, guint num_columns) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_set_schema_full (DEE_PROXY_MODEL_BACK_END (self), schema, num_columns); } static const gchar* const* dee_proxy_model_get_schema (DeeModel *self, guint *num_columns) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_schema (DEE_PROXY_MODEL_BACK_END (self), num_columns); } static const gchar* dee_proxy_model_get_column_schema (DeeModel *self, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_column_schema (DEE_PROXY_MODEL_BACK_END (self), column); } static const gchar* dee_proxy_model_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_field_schema (DEE_PROXY_MODEL_BACK_END (self), field_name, out_column); } static gint dee_proxy_model_get_column_index (DeeModel *self, const gchar *column_name) { DeeModelIface *iface; g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), -1); iface = DEE_MODEL_GET_IFACE (DEE_PROXY_MODEL_BACK_END (self)); return (* iface->get_column_index) (DEE_PROXY_MODEL_BACK_END (self), column_name); } static void dee_proxy_model_set_column_names (DeeModel *self, const gchar **column_names, guint num_columns) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_set_column_names_full (DEE_PROXY_MODEL_BACK_END (self), column_names, num_columns); } static const gchar** dee_proxy_model_get_column_names (DeeModel *self, guint *num_columns) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_column_names (DEE_PROXY_MODEL_BACK_END (self), num_columns); } static void dee_proxy_model_register_vardict_schema (DeeModel *self, guint column, GHashTable *schema) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_register_vardict_schema (DEE_PROXY_MODEL_BACK_END (self), column, schema); } static GHashTable* dee_proxy_model_get_vardict_schema (DeeModel *self, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_vardict_schema (DEE_PROXY_MODEL_BACK_END (self), column); } static guint dee_proxy_model_get_n_columns (DeeModel *self) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_n_columns (DEE_PROXY_MODEL_BACK_END (self)); } static guint dee_proxy_model_get_n_rows (DeeModel *self) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_n_rows (DEE_PROXY_MODEL_BACK_END (self)); } static void dee_proxy_model_clear (DeeModel *self) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_clear (DEE_PROXY_MODEL_BACK_END (self)); } static DeeModelIter* dee_proxy_model_prepend_row (DeeModel *self, GVariant **row_members) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_prepend_row (DEE_PROXY_MODEL_BACK_END (self), row_members); } static DeeModelIter* dee_proxy_model_append_row (DeeModel *self, GVariant **row_members) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_append_row (DEE_PROXY_MODEL_BACK_END (self), row_members); } static DeeModelIter* dee_proxy_model_insert_row (DeeModel *self, guint pos, GVariant **row_members) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_insert_row (DEE_PROXY_MODEL_BACK_END (self), pos, row_members); } static DeeModelIter* dee_proxy_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_insert_row_before (DEE_PROXY_MODEL_BACK_END (self), iter, row_members); } static DeeModelIter* dee_proxy_model_insert_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_insert_row_sorted (DEE_PROXY_MODEL_BACK_END (self), row_spec, cmp_func, user_data); } static DeeModelIter* dee_proxy_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_find_row_sorted (DEE_PROXY_MODEL_BACK_END (self), row_spec, cmp_func, user_data, out_was_found); } static void dee_proxy_model_remove (DeeModel *self, DeeModelIter *iter) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_remove (DEE_PROXY_MODEL_BACK_END (self), iter); } static void dee_proxy_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_set_row (DEE_PROXY_MODEL_BACK_END (self), iter, row_members); } static void dee_proxy_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_set_value (DEE_PROXY_MODEL_BACK_END (self), iter, column, value); } static GVariant* dee_proxy_model_get_value (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_value (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static gboolean dee_proxy_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), FALSE); return dee_model_get_bool (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static guchar dee_proxy_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_uchar (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static gint32 dee_proxy_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_int32 (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static guint32 dee_proxy_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_uint32 (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static gint64 dee_proxy_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_int64 (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static guint64 dee_proxy_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_uint64 (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static gdouble dee_proxy_model_get_double (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_double (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static const gchar* dee_proxy_model_get_string (DeeModel *self, DeeModelIter *iter, guint column) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_string (DEE_PROXY_MODEL_BACK_END (self), iter, column); } static DeeModelIter* dee_proxy_model_get_first_iter (DeeModel *self) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_first_iter (DEE_PROXY_MODEL_BACK_END (self)); } static DeeModelIter* dee_proxy_model_get_last_iter (DeeModel *self) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_last_iter (DEE_PROXY_MODEL_BACK_END (self)); } static DeeModelIter* dee_proxy_model_get_iter_at_row (DeeModel *self, guint row) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_iter_at_row (DEE_PROXY_MODEL_BACK_END (self), row); } static DeeModelIter* dee_proxy_model_next (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_next (DEE_PROXY_MODEL_BACK_END (self), iter); } static DeeModelIter* dee_proxy_model_prev (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_prev (DEE_PROXY_MODEL_BACK_END (self), iter); } static gboolean dee_proxy_model_is_first (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), FALSE); return dee_model_is_first (DEE_PROXY_MODEL_BACK_END (self), iter); } static gboolean dee_proxy_model_is_last (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), FALSE); return dee_model_is_last (DEE_PROXY_MODEL_BACK_END (self), iter); } static guint dee_proxy_model_get_position (DeeModel *self, DeeModelIter *iter) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); return dee_model_get_position (DEE_PROXY_MODEL_BACK_END (self), iter); } static DeeModelTag* dee_proxy_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_register_tag (DEE_PROXY_MODEL_BACK_END (self), tag_destroy); } static gpointer dee_proxy_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag) { g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), NULL); return dee_model_get_tag (DEE_PROXY_MODEL_BACK_END (self), iter, tag); } static void dee_proxy_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); return dee_model_set_tag (DEE_PROXY_MODEL_BACK_END (self), iter, tag, value); } static void dee_proxy_model_begin_changeset (DeeModel *self) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_begin_changeset (DEE_PROXY_MODEL_BACK_END (self)); } static void dee_proxy_model_end_changeset (DeeModel *self) { g_return_if_fail (DEE_IS_PROXY_MODEL (self)); dee_model_end_changeset (DEE_PROXY_MODEL_BACK_END (self)); } /* * Relay signals from back end */ static void on_back_end_row_added (DeeProxyModel *self, DeeModelIter *iter) { g_signal_emit_by_name (self, "row-added", iter); } static void on_back_end_row_removed (DeeProxyModel *self, DeeModelIter *iter) { g_signal_emit_by_name (self, "row-removed", iter); } static void on_back_end_row_changed (DeeProxyModel *self, DeeModelIter *iter) { g_signal_emit_by_name (self, "row-changed", iter); } static void on_back_end_changeset_started (DeeProxyModel *self, DeeModel *model) { g_signal_emit_by_name (self, "changeset-started"); } static void on_back_end_changeset_finished (DeeProxyModel *self, DeeModel *model) { g_signal_emit_by_name (self, "changeset-finished"); } /* * Overrides for DeeSerializableModel */ static guint64 dee_proxy_model_get_seqnum (DeeModel *self) { DeeProxyModelPrivate *priv; g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); priv = DEE_PROXY_MODEL (self)->priv; if (priv->inherit_seqnums) return dee_serializable_model_get_seqnum (priv->back_end); else return SUPER_CLASS->get_seqnum (self); } static void dee_proxy_model_set_seqnum (DeeModel *self, guint64 seqnum) { DeeProxyModelPrivate *priv; g_return_if_fail (DEE_IS_PROXY_MODEL (self)); priv = DEE_PROXY_MODEL (self)->priv; if (priv->inherit_seqnums) dee_serializable_model_set_seqnum (priv->back_end, seqnum); else return SUPER_CLASS->set_seqnum (self, seqnum); } static guint64 dee_proxy_model_inc_seqnum (DeeModel *self) { DeeProxyModelPrivate *priv; g_return_val_if_fail (DEE_IS_PROXY_MODEL (self), 0); priv = DEE_PROXY_MODEL (self)->priv; if (priv->inherit_seqnums) return dee_serializable_model_inc_seqnum (priv->back_end); else return SUPER_CLASS->inc_seqnum (self); } dee-1.2.7+15.04.20150304/src/dee-model.h0000644000015300001610000005424612475676210017425 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_MODEL_H #define _HAVE_DEE_MODEL_H #include #include #include G_BEGIN_DECLS #define DEE_TYPE_MODEL_ITER (dee_model_iter_get_type ()) #define DEE_TYPE_MODEL (dee_model_get_type ()) #define DEE_MODEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_MODEL, DeeModel)) #define DEE_IS_MODEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_MODEL)) #define DEE_MODEL_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE(obj, dee_model_get_type (), DeeModelIface)) typedef struct _DeeModelIface DeeModelIface; typedef struct _DeeModel DeeModel; /** * DeeModelIter: * * The DeeModelIter structure is private and should only be used with the * provided #DeeModel API. It is owned by DeeModel and should not be freed. **/ typedef struct _DeeModelIter DeeModelIter; /** * DeeModelTag: * * The DeeModelTag structure is private and should only be used with the * provided #DeeModel API. It is owned by DeeModel and should not be freed. **/ typedef struct _DeeModelTag DeeModelTag; /** * DeeCompareRowFunc: * @row1: (array): The model being indexed * @row2: (array): The row to extract terms for * @user_data: (closure): User data to pass to comparison function * * Compares @row1 and @row2. Mainly used with dee_model_insert_sorted() and * dee_model_find_sorted(). * * Returns: -1, 0, or 1 if @row1 is respectively less than, equal, or greater * than @row2. */ typedef gint (*DeeCompareRowFunc) (GVariant** row1, GVariant** row2, gpointer user_data); /** * DeeCompareRowSizedFunc: * @row1: (array length=row1_length): Row data * @row1_length: The number of elements in row1 array * @row2: (array length=row2_length): Row data to compare with * @row2_length: The number of elements in row2 array * @user_data: (closure): User data passed to comparison function * * Compares @row1 and @row2. Mainly used with * dee_model_insert_row_sorted_with_sizes() and * dee_model_find_row_sorted_with_sizes(). * * Returns: -1, 0, or 1 if @row1 is respectively less than, equal, or greater * than @row2. */ typedef gint (*DeeCompareRowSizedFunc) (GVariant** row1, guint row1_length, GVariant** row2, guint row2_length, gpointer user_data); struct _DeeModelIface { GTypeInterface g_iface; /* Signals */ void (*row_added) (DeeModel *self, DeeModelIter *iter); void (*row_removed) (DeeModel *self, DeeModelIter *iter); void (*row_changed) (DeeModel *self, DeeModelIter *iter); /*< public >*/ void (*set_schema_full) (DeeModel *self, const char* const *column_schemas, guint num_columns); const gchar* const* (*get_schema) (DeeModel *self, guint *num_columns); const gchar* (*get_column_schema) (DeeModel *self, guint column); const gchar* (*get_field_schema) (DeeModel *self, const gchar *field_name, guint *out_column); gint (*get_column_index) (DeeModel *self, const gchar *column_name); void (*set_column_names_full) (DeeModel *self, const gchar **column_names, guint num_columns); const gchar** (*get_column_names) (DeeModel *self, guint *num_columns); void (*register_vardict_schema) (DeeModel *self, guint num_column, GHashTable *schemas); GHashTable* (*get_vardict_schema) (DeeModel *self, guint num_column); guint (*get_n_columns) (DeeModel *self); guint (*get_n_rows) (DeeModel *self); DeeModelIter* (*append_row) (DeeModel *self, GVariant **row_members); DeeModelIter* (*prepend_row) (DeeModel *self, GVariant **row_members); DeeModelIter* (*insert_row) (DeeModel *self, guint pos, GVariant **row_members); DeeModelIter* (*insert_row_before) (DeeModel *self, DeeModelIter *iter, GVariant **row_members); DeeModelIter* (*insert_row_sorted) (DeeModel *self, GVariant **row_members, DeeCompareRowFunc cmp_func, gpointer user_data); DeeModelIter* (*find_row_sorted) (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found); void (*remove) (DeeModel *self, DeeModelIter *iter); void (*clear) (DeeModel *self); void (*set_value) (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value); void (*set_row) (DeeModel *self, DeeModelIter *iter, GVariant **row_members); GVariant* (*get_value) (DeeModel *self, DeeModelIter *iter, guint column); GVariant* (*get_value_by_name) (DeeModel *self, DeeModelIter *iter, const gchar *column_name); DeeModelIter* (*get_first_iter) (DeeModel *self); DeeModelIter* (*get_last_iter) (DeeModel *self); DeeModelIter* (*get_iter_at_row) (DeeModel *self, guint row); gboolean (*get_bool) (DeeModel *self, DeeModelIter *iter, guint column); guchar (*get_uchar) (DeeModel *self, DeeModelIter *iter, guint column); gint32 (*get_int32) (DeeModel *self, DeeModelIter *iter, guint column); guint32 (*get_uint32) (DeeModel *self, DeeModelIter *iter, guint column); gint64 (*get_int64) (DeeModel *self, DeeModelIter *iter, guint column); guint64 (*get_uint64) (DeeModel *self, DeeModelIter *iter, guint column); gdouble (*get_double) (DeeModel *self, DeeModelIter *iter, guint column); const gchar* (*get_string) (DeeModel *self, DeeModelIter *iter, guint column); DeeModelIter* (*next) (DeeModel *self, DeeModelIter *iter); DeeModelIter* (*prev) (DeeModel *self, DeeModelIter *iter); gboolean (*is_first) (DeeModel *self, DeeModelIter *iter); gboolean (*is_last) (DeeModel *self, DeeModelIter *iter); guint (*get_position) (DeeModel *self, DeeModelIter *iter); DeeModelTag* (*register_tag) (DeeModel *self, GDestroyNotify tag_destroy); gpointer (*get_tag) (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag); void (*set_tag) (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value); GVariant** (*get_row) (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members); void (*begin_changeset) (DeeModel *self); void (*end_changeset) (DeeModel *self); void (*changeset_started) (DeeModel *self); void (*changeset_finished) (DeeModel *self); /*< private >*/ void (*_dee_model_1) (void); void (*_dee_model_2) (void); void (*_dee_model_3) (void); }; GType dee_model_iter_get_type (void); /** * dee_model_get_type: * * The GType of #DeeModel * * Return value: the #GType of #DeeModel **/ GType dee_model_get_type (void); void dee_model_set_schema (DeeModel *self, ...) G_GNUC_NULL_TERMINATED; void dee_model_set_schema_full (DeeModel *self, const gchar* const *column_schemas, guint num_columns); const gchar* const* dee_model_get_schema (DeeModel *self, guint *num_columns); const gchar* dee_model_get_column_schema (DeeModel *self, guint column); const gchar* dee_model_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column); gint dee_model_get_column_index (DeeModel *self, const gchar *column_name); void dee_model_set_column_names (DeeModel *self, const gchar *first_column_name, ...) G_GNUC_NULL_TERMINATED; void dee_model_set_column_names_full (DeeModel *self, const gchar **column_names, guint num_columns); const gchar** dee_model_get_column_names (DeeModel *self, guint *num_columns); void dee_model_register_vardict_schema (DeeModel *self, guint column, GHashTable *schemas); GHashTable* dee_model_get_vardict_schema (DeeModel *self, guint column); guint dee_model_get_n_columns (DeeModel *self); guint dee_model_get_n_rows (DeeModel *self); DeeModelIter* dee_model_append (DeeModel *self, ...); DeeModelIter* dee_model_append_row (DeeModel *self, GVariant **row_members); DeeModelIter* dee_model_prepend (DeeModel *self, ...); DeeModelIter* dee_model_prepend_row (DeeModel *self, GVariant **row_members); DeeModelIter* dee_model_insert (DeeModel *self, guint pos, ...); DeeModelIter* dee_model_insert_row (DeeModel *self, guint pos, GVariant **row_members); DeeModelIter* dee_model_insert_before (DeeModel *self, DeeModelIter *iter, ...); DeeModelIter* dee_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members); DeeModelIter* dee_model_insert_row_sorted (DeeModel *self, GVariant **row_members, DeeCompareRowFunc cmp_func, gpointer user_data); DeeModelIter* dee_model_insert_row_sorted_with_sizes (DeeModel *self, GVariant **row_members, DeeCompareRowSizedFunc cmp_func, gpointer user_data); DeeModelIter* dee_model_insert_sorted (DeeModel *self, DeeCompareRowFunc cmp_func, gpointer user_data, ...); DeeModelIter* dee_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found); DeeModelIter* dee_model_find_row_sorted_with_sizes (DeeModel *self, GVariant **row_spec, DeeCompareRowSizedFunc cmp_func, gpointer user_data, gboolean *out_was_found); DeeModelIter* dee_model_find_sorted (DeeModel *self, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found, ...); void dee_model_remove (DeeModel *self, DeeModelIter *iter); void dee_model_clear (DeeModel *self); void dee_model_set (DeeModel *self, DeeModelIter *iter, ...); void dee_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value); void dee_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members); void dee_model_get (DeeModel *self, DeeModelIter *iter, ...); GVariant** dee_model_get_row (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members); GVariant* dee_model_get_value (DeeModel *self, DeeModelIter *iter, guint column); GVariant* dee_model_get_value_by_name (DeeModel *self, DeeModelIter *iter, const gchar *column_name); DeeModelIter* dee_model_get_first_iter (DeeModel *self); DeeModelIter* dee_model_get_last_iter (DeeModel *self); DeeModelIter* dee_model_get_iter_at_row (DeeModel *self, guint row); gboolean dee_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column); guchar dee_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column); gint32 dee_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column); guint32 dee_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column); gint64 dee_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column); guint64 dee_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column); gdouble dee_model_get_double (DeeModel *self, DeeModelIter *iter, guint column); const gchar * dee_model_get_string (DeeModel *self, DeeModelIter *iter, guint column); DeeModelIter * dee_model_next (DeeModel *self, DeeModelIter *iter); DeeModelIter * dee_model_prev (DeeModel *self, DeeModelIter *iter); gboolean dee_model_is_first (DeeModel *self, DeeModelIter *iter); gboolean dee_model_is_last (DeeModel *self, DeeModelIter *iter); guint dee_model_get_position (DeeModel *self, DeeModelIter *iter); DeeModelTag* dee_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy); gpointer dee_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag); void dee_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value); void dee_model_clear_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag); void dee_model_begin_changeset (DeeModel *self); void dee_model_end_changeset (DeeModel *self); GVariant** dee_model_build_row (DeeModel *self, GVariant **out_row_members, ...); GVariant** dee_model_build_named_row (DeeModel *self, GVariant **out_row_members, const gchar *first_column_name, ...) G_GNUC_NULL_TERMINATED; GVariant** dee_model_build_named_row_valist (DeeModel *self, GVariant **out_row_members, const gchar *first_column_name, va_list *args); GVariant** dee_model_build_named_row_sunk (DeeModel *self, GVariant **out_row_members, guint *out_array_length, const gchar *first_column_name, ...) G_GNUC_NULL_TERMINATED; G_END_DECLS #endif /* _HAVE_DEE_MODEL_H */ dee-1.2.7+15.04.20150304/src/dee-file-resource-manager.c0000644000015300001610000003322012475676210022461 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-file-resource-manager * @short_description: A resource manager backed by memory mapped files * * This is an implementation of the #DeeResourceManager interface. * It uses atomic operations to write resources to files and memory maps * the resource files when you load them. * * Unless you have very specific circumstances you should normally not * create resource managers yourself, but get the default one for your * platform by calling dee_resource_manager_get_default(). * */ #ifdef HAVE_CONFIG_H #include #endif #include // for chmod codes for g_mkdir_with_parents() #include "dee-file-resource-manager.h" #include "trace-log.h" static void dee_file_resource_manager_resource_manager_iface_init (DeeResourceManagerIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeFileResourceManager, dee_file_resource_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (DEE_TYPE_RESOURCE_MANAGER, dee_file_resource_manager_resource_manager_iface_init)) #define DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_FILE_RESOURCE_MANAGER, DeeFileResourceManagerPrivate)) enum { PROP_0, PROP_PRIMARY_PATH, }; typedef struct { /* Directories to search for resources. The primary-path is stored * as element 0 in this list */ GSList *resource_dirs; /* Resource monitor ids -> GFileMonitors */ GHashTable *monitors_by_id; } DeeFileResourceManagerPrivate; /* GObject Init */ static void dee_file_resource_manager_finalize (GObject *object) { DeeFileResourceManagerPrivate *priv; priv = DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE (object); g_slist_free_full (priv->resource_dirs, g_free); priv->resource_dirs = NULL; if (priv->monitors_by_id) { g_hash_table_unref (priv->monitors_by_id); priv->monitors_by_id = NULL; } G_OBJECT_CLASS (dee_file_resource_manager_parent_class)->finalize (object); } static void dee_file_resource_manager_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeeResourceManager *self = DEE_RESOURCE_MANAGER (object); gchar *path; switch (id) { case PROP_PRIMARY_PATH: /* Assume ownership of filter */ path = g_value_dup_string(value); if (path == NULL) { path = g_build_filename (g_get_user_data_dir (), "resources", NULL); } dee_file_resource_manager_add_search_path (self, path); g_free (path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_file_resource_manager_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { DeeFileResourceManagerPrivate *priv; priv = DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE (object); switch (id) { case PROP_PRIMARY_PATH: g_value_set_string (value, priv->resource_dirs != NULL ? priv->resource_dirs->data : NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_file_resource_manager_class_init (DeeFileResourceManagerClass *klass) { GParamSpec *pspec; GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_file_resource_manager_finalize; obj_class->get_property = dee_file_resource_manager_get_property; obj_class->set_property = dee_file_resource_manager_set_property; /** * DeeFileResourceManager:primary-path: * * Property holding the primary path used to store and load resources */ pspec = g_param_spec_string("primary-path", "Primary path", "The primary path to to store and load resources from", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_PRIMARY_PATH, pspec); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeFileResourceManagerPrivate)); } static void dee_file_resource_manager_init (DeeFileResourceManager *self) { DeeFileResourceManagerPrivate *priv; priv = DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE (self); priv->resource_dirs = NULL; priv->monitors_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); } /** * dee_file_resource_manager_new: * @primary_path: The primary path used to store and load resources. * If you pass %NULL the manager will use a default path. * * Create a new #DeeFileResourceManager with its primary store- and load * path set to @primary_path. * * You can manually add fallback search paths by calling * dee_file_resource_manager_add_search_path(). * * You normally don't need to create you own resource managers. Instead * you should call dee_resource_manager_get_default(). * * Return value: (transfer full) (type DeeFileResourceManager): A newly allocated #DeeFileResourceManager. * Free with g_object_unref(). */ DeeResourceManager* dee_file_resource_manager_new (const gchar *primary_path) { DeeResourceManager *self; self = DEE_RESOURCE_MANAGER (g_object_new (DEE_TYPE_FILE_RESOURCE_MANAGER, "primary-path", primary_path, NULL)); return self; } /** * dee_file_resource_manager_add_search_path: * @self: (type DeeFileResourceManager): The resource manager to add a search * path to * @path: The path to add to the set of searched paths * * Add a path to the set of paths searched for resources. The manager will * first search the primary path as specified in the constructor and then * search paths in the order they where added. */ void dee_file_resource_manager_add_search_path (DeeResourceManager *self, const gchar *path) { DeeFileResourceManagerPrivate *priv; g_return_if_fail (DEE_IS_FILE_RESOURCE_MANAGER (self)); g_return_if_fail (path != NULL); priv = DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE (self); priv->resource_dirs = g_slist_append (priv->resource_dirs, g_strdup (path)); } /** * dee_file_resource_manager_get_primary_path: * @self: (type DeeFileResourceManager): The resource manager to inspect * * Helper method to access the :primary-path property. * * Return value: The value of the :primary-path property */ const gchar* dee_file_resource_manager_get_primary_path (DeeResourceManager *self) { DeeFileResourceManagerPrivate *priv; g_return_val_if_fail (DEE_IS_FILE_RESOURCE_MANAGER (self), NULL); priv = DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE (self); return (const gchar *) priv->resource_dirs->data; } static gboolean dee_file_resource_manager_store (DeeResourceManager *self, DeeSerializable *resource, const gchar *resource_name, GError **error) { GVariant *external; gsize size; gchar *buf, *path; const gchar *primary_path; gboolean stack_allocated_buffer, result, did_retry = FALSE; GError *local_error; g_return_val_if_fail (DEE_IS_RESOURCE_MANAGER (self), FALSE); g_return_val_if_fail (DEE_IS_SERIALIZABLE (resource), FALSE); g_return_val_if_fail (resource_name != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); external = dee_serializable_externalize (resource); if (external == NULL) { /* This is a programming error, not a runtime error, we don't * set the error value */ g_critical ("When writing DeeSerializable %s@%p to the file %s " "externalize() returned NULL", G_OBJECT_TYPE_NAME (resource), resource, resource_name); return FALSE; } size = g_variant_get_size (external); /* For less than ½MB structures we use a stack-allocated buffer, * for bigger sizes we use a heap allocated buffer to avoid * stack overflows */ if (size < 524288) { buf = g_alloca (size); stack_allocated_buffer = TRUE; } else { buf = g_malloc (size); stack_allocated_buffer = FALSE; } /* Note: Since we're using mmap() in read_from_file() we need atomic * IO operations. Hence g_file_set_contents(). */ g_variant_store (external, buf); primary_path = dee_file_resource_manager_get_primary_path (self); path = g_build_filename (primary_path, resource_name, NULL); store: local_error = NULL; result = g_file_set_contents(path, buf, size, &local_error); if (local_error) { /* The resource directory might not exist - try to create it, * and then try over */ if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT && !did_retry) { trace_object (self, "Failed to write resource %s, " "parent directory %s does not exist. " "Trying to create it", resource_name, primary_path); g_error_free (local_error); g_mkdir_with_parents (primary_path, S_IRUSR | S_IWUSR | S_IXUSR); // read+write+exec user only permissions did_retry = TRUE; goto store; } else { g_propagate_error (error, local_error); goto out; } } out: if (result) trace_object (self, "Successfully stored %s", path); else trace_object (self, "Failed to store %s", path); g_free (path); if (!stack_allocated_buffer) g_free (buf); g_variant_unref (external); return result; } static GObject* _load_resource_from_file (const gchar *filename, GError **error) { GMappedFile *map; gsize map_size; gchar *contents; GVariant *external; GObject *object; GError *local_error = NULL; g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); map = g_mapped_file_new (filename, FALSE, &local_error); if (local_error) { g_propagate_error (error, local_error); return NULL; } contents = g_mapped_file_get_contents (map); map_size = g_mapped_file_get_length (map); external = g_variant_new_from_data (G_VARIANT_TYPE ("(ua{sv}v)"), contents, map_size, FALSE, (GDestroyNotify) g_mapped_file_unref, map); object = dee_serializable_parse_external (external); return object; } static GObject* dee_file_resource_manager_load (DeeResourceManager *self, const gchar *resource_name, GError **error) { DeeFileResourceManagerPrivate *priv; gchar *resource_path; GSList *iter; GError *local_error; GObject *object = NULL; g_return_val_if_fail (DEE_IS_FILE_RESOURCE_MANAGER (self), NULL); g_return_val_if_fail (resource_name != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); priv = DEE_FILE_RESOURCE_MANAGER_GET_PRIVATE (self); for (iter = priv->resource_dirs; iter != NULL; iter = iter->next) { resource_path = g_build_filename (iter->data, resource_name, NULL); trace_object (self, "Looking up resource %s in %s", resource_name, iter->data); local_error = NULL; object = _load_resource_from_file (resource_path, &local_error); g_free (resource_path); /* If we get any error except no-such-file-or-directory we bail out */ if (local_error != NULL) { if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT) { continue; } else { g_propagate_error (error, local_error); break; } } if (object != NULL) break; } return object; } static void dee_file_resource_manager_resource_manager_iface_init (DeeResourceManagerIface *iface) { iface->store = dee_file_resource_manager_store; iface->load = dee_file_resource_manager_load; } dee-1.2.7+15.04.20150304/src/dee-term-list.c0000644000015300001610000002602412475676210020231 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-term-list * @short_description: A simple collection type representing a list of indexed terms for a row in a #DeeIndex * @include: dee.h * * #DeeTermList is a simple list type containing the indexed terms of a row * in a #DeeModel as recorded in a #DeeIndex. The terms are extracted from the * model by using a #DeeAnalyzer. * * The default implementation of #DeeTermList stores all terms in a string pool * and reuses terms from that string pool for the entire lifetime of the * term list. That is, even if you call dee_term_list_clear() on it. This * behaviour will save a lot of reallocations and g_strdup()s provided * there is reuse in the terms over time. */ #ifdef HAVE_CONFIG_H #include #endif #include /* memcpy() */ #include "dee-term-list.h" #include "trace-log.h" G_DEFINE_TYPE (DeeTermList, dee_term_list, G_TYPE_OBJECT); #define DEE_TERM_LIST_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_TERM_LIST, DeeTermListPrivate)) /* * FORWARDS */ static const gchar* dee_term_list_get_term_real (DeeTermList *self, guint n); static DeeTermList* dee_term_list_add_term_real (DeeTermList *self, const gchar *term); static guint dee_term_list_num_terms_real (DeeTermList *self); static DeeTermList* dee_term_list_clear_real (DeeTermList *self); static DeeTermList* dee_term_list_clone_real (DeeTermList *self); /* * GOBJECT VOODOO */ /** * DeeTermListPrivate: * * Ignore this structure. **/ struct _DeeTermListPrivate { /* String pool - reused to minimize strdup()ing. Allocated lazily to make * clone more efficient. Chunk are shared with clones and since GStringChunk * is not ref counted we use a GObject instance to help with that */ GStringChunk *chunk; /* Dummy helper object to implement ref counting on our string chunk */ GObject *chunk_counter; /* The actual terms, in order. The strings are stored in priv->chunk * so they shouldn't be freed by this instance. Allocated lazily to make * cloning more efficient */ GPtrArray *terms; }; enum { PROP_0, }; #define CHECK_LAZY_SETUP(term_list) \ if (G_UNLIKELY(term_list->priv->chunk == NULL)) \ { \ /* The number 64 is chosen as a an estimate of the length of URIs to files * under the user's home dir somewhere */ \ term_list->priv->chunk = g_string_chunk_new (64); \ \ /* Create dummy ref count on chunk_counter */ \ term_list->priv->chunk_counter = g_object_new (G_TYPE_OBJECT, NULL); \ g_object_set_data_full (term_list->priv->chunk_counter, \ "chunk", \ term_list->priv->chunk, \ (GDestroyNotify) g_string_chunk_free);\ \ /* Making room for 10 terms by default is probably sane */ \ term_list->priv->terms = g_ptr_array_sized_new (10); \ } /* GObject stuff */ static void dee_term_list_finalize (GObject *object) { DeeTermListPrivate *priv = DEE_TERM_LIST (object)->priv; if (priv->chunk_counter) { g_object_unref (priv->chunk_counter); priv->chunk = NULL; priv->chunk_counter = NULL; } if (priv->terms) { g_ptr_array_unref (priv->terms); priv->terms = NULL; } G_OBJECT_CLASS (dee_term_list_parent_class)->finalize (object); } static void dee_term_list_class_init (DeeTermListClass *klass) { // GParamSpec *pspec; GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_term_list_finalize; klass->get_term = dee_term_list_get_term_real; klass->add_term = dee_term_list_add_term_real; klass->num_terms = dee_term_list_num_terms_real; klass->clear = dee_term_list_clear_real; klass->clone = dee_term_list_clone_real; /* pspec = g_param_spec_pointer ("filter", "Filter", "Filtering rules applied to the original model", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_FILTER, pspec); */ /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeTermListPrivate)); } static void dee_term_list_init (DeeTermList *self) { DeeTermListPrivate *priv; priv = self->priv = DEE_TERM_LIST_GET_PRIVATE (self); /* The chunk and terms are allocated lazily, to make clone() work more * eficiently */ priv->chunk = NULL; priv->terms = NULL; } /* * API */ /** * dee_term_list_get_term: * @self: The term list to get the @nth term from * @n: The (zero based) offset into the term list * * Get the n'th term in the list. * * Note that in the default implementation it is guaranteed that the returned * string is valid for the entire lifetime of the #DeeTermList. * * Returns: The @nth string held in the term list */ const gchar* dee_term_list_get_term (DeeTermList *self, guint n) { DeeTermListClass *klass; g_return_val_if_fail (DEE_IS_TERM_LIST (self), NULL); klass = DEE_TERM_LIST_GET_CLASS (self); return (* klass->get_term) (self, n); } /** * dee_term_list_add_term: * @self: The term list to add a term to * @term: The term to add * * Add a term to the termlist. Note that it is possible to add a term multiple * times. The effect of this is determined by the #DeeModelIndex consuming the * #DeeTermList. * * Returns: (transfer none): Always returns @self */ DeeTermList* dee_term_list_add_term (DeeTermList *self, const gchar *term) { DeeTermListClass *klass; g_return_val_if_fail (DEE_IS_TERM_LIST (self), NULL); g_return_val_if_fail (term != NULL, NULL); klass = DEE_TERM_LIST_GET_CLASS (self); return (* klass->add_term) (self, term); } /** * dee_term_list_num_terms: * @self: The term list to check the number of terms in * * Returns: The number of terms in the term list */ guint dee_term_list_num_terms (DeeTermList *self) { DeeTermListClass *klass; g_return_val_if_fail (DEE_IS_TERM_LIST (self), 0); klass = DEE_TERM_LIST_GET_CLASS (self); return (* klass->num_terms) (self); } /** * dee_term_list_clear: * @self: The term list to clear * * Remove all terms from a term list making it ready for reuse. Note that * term list implementations will often have optimized memory allocation * schemes so reuse is often more efficient than allocating a new term list * each time you need it. * * Returns: (transfer none): Always returns @self */ DeeTermList* dee_term_list_clear (DeeTermList *self) { DeeTermListClass *klass; g_return_val_if_fail (DEE_IS_TERM_LIST (self), NULL); klass = DEE_TERM_LIST_GET_CLASS (self); return (* klass->clear) (self); } /** * dee_term_list_clone: * @self: The term list to clone * * Create a copy of @self that shares the underlying string pool and containing * a list of terms as currently set in @self. * * Subsequently freeing the original and keeping the clone around is not a * problem. The clone works as a standalone term list. The only gotcha may be * threading issues because of concurrent access to the shared string pool. * * Creating a clone very efficient since only very little memory allocation * is required. It's advised that you use a clone instead a new instance * whenever you work over a common corpus of strings. * * It is also worth noting that terms obtained from the original term list * and a clone can be compared directly as pointers (fx. with g_direct_equal()). * This is because they share the underlying string pool. * * Returns: (transfer full): A newly allocated term list. * Free with g_object_unref(). */ DeeTermList* dee_term_list_clone (DeeTermList *self) { DeeTermListClass *klass; g_return_val_if_fail (DEE_IS_TERM_LIST (self), NULL); klass = DEE_TERM_LIST_GET_CLASS (self); return (* klass->clone) (self); } /* * IMPLEMENTATION */ static const gchar* dee_term_list_get_term_real (DeeTermList *self, guint n) { DeeTermListPrivate *priv; g_return_val_if_fail (DEE_IS_TERM_LIST(self), NULL); CHECK_LAZY_SETUP (self); priv = self->priv; g_return_val_if_fail (n < priv->terms->len, NULL); return g_ptr_array_index (priv->terms, n); } static DeeTermList* dee_term_list_add_term_real (DeeTermList *self, const gchar *term) { DeeTermListPrivate *priv; gchar *cterm; g_return_val_if_fail (DEE_IS_TERM_LIST(self), NULL); g_return_val_if_fail (term != NULL, NULL); CHECK_LAZY_SETUP (self); priv = self->priv; cterm = g_string_chunk_insert_const (priv->chunk, term); g_ptr_array_add (priv->terms, cterm); return self; } static guint dee_term_list_num_terms_real (DeeTermList *self) { DeeTermListPrivate *priv; g_return_val_if_fail (DEE_IS_TERM_LIST(self), 0); CHECK_LAZY_SETUP (self); priv = self->priv; return priv->terms->len; } static DeeTermList* dee_term_list_clear_real (DeeTermList *self) { /* * The idea here is that we clear only the term list (priv->terms), * but not the string pool (priv->chunk). This will work well assuming that * there is a fair deal of reuse in the term. */ DeeTermListPrivate *priv; guint i; g_return_val_if_fail (DEE_IS_TERM_LIST(self), NULL); CHECK_LAZY_SETUP (self); priv = self->priv; if (priv->terms->len == 0) return self; /* Clear the term list backwards to avoid memory shuffling */ for (i = priv->terms->len; i > 0; i--) g_ptr_array_remove_index_fast (priv->terms, i - 1); return self; } static DeeTermList* dee_term_list_clone_real (DeeTermList *self) { DeeTermListPrivate *priv, *clone_priv; DeeTermList *clone; g_return_val_if_fail (DEE_IS_TERM_LIST(self), NULL); CHECK_LAZY_SETUP (self); priv = self->priv; clone = (DeeTermList*) g_object_new (DEE_TYPE_TERM_LIST, NULL); clone_priv = clone->priv; clone_priv->chunk = priv->chunk; clone_priv->chunk_counter = g_object_ref (priv->chunk_counter); clone_priv->terms = g_ptr_array_sized_new (priv->terms->len); memcpy (clone_priv->terms->pdata, priv->terms->pdata, sizeof (gpointer) * priv->terms->len); clone_priv->terms->len = priv->terms->len; return clone; } dee-1.2.7+15.04.20150304/src/dee-peer.h0000644000015300001610000000634212475676210017252 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_PEER_H #define _HAVE_DEE_PEER_H #include #include #include G_BEGIN_DECLS #define DEE_TYPE_PEER (dee_peer_get_type ()) #define DEE_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_PEER, DeePeer)) #define DEE_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_PEER, DeePeerClass)) #define DEE_IS_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_PEER)) #define DEE_IS_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_PEER)) #define DEE_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DEE_TYPE_PEER, DeePeerClass)) typedef struct _DeePeer DeePeer; typedef struct _DeePeerClass DeePeerClass; typedef struct _DeePeerPrivate DeePeerPrivate; #define DEE_PEER_DBUS_IFACE "com.canonical.Dee.Peer" /** * DeePeer: * * All fields in the DeePeer structure are private and should never be * accessed directly */ struct _DeePeer { /*< private >*/ GObject parent; DeePeerPrivate *priv; }; struct _DeePeerClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ /*< signals >*/ void (*peer_found) (DeePeer *self, const gchar *name); void (*peer_lost) (DeePeer *self, const gchar *name); void (*connection_acquired) (DeePeer *self, GDBusConnection *connection); void (*connection_closed) (DeePeer *self, GDBusConnection *connection); /*< vtable >*/ const gchar* (*get_swarm_leader) (DeePeer *self); gboolean (*is_swarm_leader) (DeePeer *self); GSList* (*get_connections) (DeePeer *self); gchar** (*list_peers) (DeePeer *self); /*< private >*/ void (*_dee_peer_1) (void); void (*_dee_peer_2) (void); void (*_dee_peer_3) (void); }; /** * dee_peer_get_type: * * The GType of #DeePeer * * Return value: the #GType of #DeePeer **/ GType dee_peer_get_type (void); DeePeer* dee_peer_new (const gchar* swarm_name); gboolean dee_peer_is_swarm_leader (DeePeer *self); const gchar* dee_peer_get_swarm_leader (DeePeer *self); const gchar* dee_peer_get_swarm_name (DeePeer *self); GSList* dee_peer_get_connections (DeePeer *self); gchar** dee_peer_list_peers (DeePeer *self); gboolean dee_peer_is_swarm_owner (DeePeer *self); G_END_DECLS #endif /* _HAVE_DEE_PEER_H */ dee-1.2.7+15.04.20150304/src/dee-model-reader.h0000644000015300001610000000635112475676210020657 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_MODEL_READER_H #define _HAVE_DEE_MODEL_READER_H #include #include #include G_BEGIN_DECLS /** * DeeModelReaderFunc: * @model: The model being indexed * @iter: The row to extract terms for * @userdata: (closure):The data set when registering the reader * * Extracts a string from a row in a model. * * Returns: A newly allocated string with the row data to be indexed. * Free with g_free(). */ typedef gchar* (*DeeModelReaderFunc) (DeeModel *model, DeeModelIter *iter, gpointer userdata); /** * DeeModelReader: * @reader_func: (scope notified): The #DeeModelReaderFunc used to extract * string from a model * @userdata: (closure): user data to pass to @reader_func * @destroy: Called when the reader is destroyed * * Structure encapsulating the information needed to read strings from a * model. Used for example by #DeeIndex. */ typedef struct { DeeModelReaderFunc reader_func; gpointer userdata; GDestroyNotify destroy; /*< private >*/ gpointer _padding1; gpointer _padding2; gpointer _padding3; gpointer _padding4; gpointer _padding5; } DeeModelReader; gchar* dee_model_reader_read (DeeModelReader *self, DeeModel *model, DeeModelIter *iter); void dee_model_reader_destroy (DeeModelReader *reader); void dee_model_reader_new (DeeModelReaderFunc reader_func, gpointer userdata, GDestroyNotify destroy, DeeModelReader *out_reader); void dee_model_reader_new_for_string_column (guint column, DeeModelReader *out_reader); void dee_model_reader_new_for_int32_column (guint column, DeeModelReader *out_reader); void dee_model_reader_new_for_uint32_column (guint column, DeeModelReader *out_reader); G_END_DECLS #endif /* _HAVE_DEE_MODEL_READER_H */ dee-1.2.7+15.04.20150304/src/dee-text-analyzer.c0000644000015300001610000001160612475676210021120 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-text-analyzer * @short_description: Analyze UTF8 text * @include: dee.h * * A #DeeTextAnalyzer is a #DeeAnalyzer that tokenizes UTF-8 text into words, * lower cases it, and does rudimentary normalization. Collation keys for the * current locale are generated. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "dee-text-analyzer.h" G_DEFINE_TYPE (DeeTextAnalyzer, dee_text_analyzer, DEE_TYPE_ANALYZER); #define DEE_TEXT_ANALYZER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_TEXT_ANALYZER, DeeTextAnalyzerPrivate)) /** * DeeAnalyzerPrivate: * * Ignore this structure. **/ struct _DeeTextAnalyzerPrivate { int foo; }; enum { PROP_0, }; /* * DeeAnalyzer forward declarations */ static void dee_text_analyzer_tokenize_real (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out); static gchar* dee_text_analyzer_collate_key_real (DeeAnalyzer *self, const gchar *data); /* GObject stuff */ static void dee_text_analyzer_finalize (GObject *object) { G_OBJECT_CLASS (dee_text_analyzer_parent_class)->finalize (object); } static void dee_text_analyzer_class_init (DeeTextAnalyzerClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); DeeAnalyzerClass *a_class = DEE_ANALYZER_CLASS (klass); obj_class->finalize = dee_text_analyzer_finalize; a_class->tokenize = dee_text_analyzer_tokenize_real; a_class->collate_key = dee_text_analyzer_collate_key_real; /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeTextAnalyzerPrivate)); } static void dee_text_analyzer_init (DeeTextAnalyzer *self) { self->priv = DEE_TEXT_ANALYZER_GET_PRIVATE (self); } /* * Implementations */ /* Default tokenization is a no-op */ static void dee_text_analyzer_tokenize_real (DeeAnalyzer *self, const gchar *data, DeeTermList *terms_out) { GPtrArray *term_array; const gchar *p, *last_term, *end; gchar *term, *_term; gunichar chr; gint term_len_bytes, i; g_return_if_fail (DEE_IS_TEXT_ANALYZER (self)); g_return_if_fail (data != NULL); g_return_if_fail (DEE_IS_TERM_LIST (terms_out)); if (!g_utf8_validate (data, -1, &end)) { g_warning ("Unable to analyze invalid UTF-8: %s", data); return; } term_array = g_ptr_array_new (); g_ptr_array_set_free_func (term_array, (GDestroyNotify) g_free); /* Split on non-alphanumeric characters * Watch out: "Clever" pointer arithmetic ahead... :-) */ p = data; last_term = data; while (p != end) { chr = g_utf8_get_char (p); if (!g_unichar_isalnum(chr) || p == end) { term_len_bytes = p - last_term; term = g_strndup (last_term, term_len_bytes); g_ptr_array_add (term_array, term); while (!g_unichar_isalnum(chr) && p != end) { p = g_utf8_next_char (p); chr = g_utf8_get_char (p); } last_term = p; continue; } p = g_utf8_next_char (p); } if (last_term != p) { term_len_bytes = p - last_term; term = g_strndup (last_term, term_len_bytes); g_ptr_array_add (term_array, term); } /* Normalize terms , lowercase them, and add them to the term list */ for (i = 0; i < term_array->len; i++) { term = g_ptr_array_index (term_array, i); term = g_utf8_normalize (term, -1, G_NORMALIZE_ALL_COMPOSE); _term = g_utf8_strdown (term, -1); dee_term_list_add_term (terms_out, _term); g_free (term); g_free (_term); } g_ptr_array_unref (term_array); } static gchar* dee_text_analyzer_collate_key_real (DeeAnalyzer *self, const gchar *data) { return g_utf8_collate_key (data, -1); } DeeTextAnalyzer* dee_text_analyzer_new (void) { return (DeeTextAnalyzer*) g_object_new (DEE_TYPE_TEXT_ANALYZER, NULL); } dee-1.2.7+15.04.20150304/src/dee-serializable-model.c0000644000015300001610000015405712475676210022065 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-serializable-model * @short_description: Abstract base class for easing implementations of * #DeeModels providing a unique version number * for each row * @include: dee.h * * #DeeSerializableModel is an abstract base class to ease implementation of * #DeeModels providing rows versioned by a * sequence number. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "dee-model.h" #include "dee-serializable-model.h" #include "dee-serializable.h" #include "dee-marshal.h" #include "trace-log.h" static void dee_serializable_model_model_iface_init (DeeModelIface *iface); static void dee_serializable_model_serializable_iface_init (DeeSerializableIface *iface); static GObject* dee_serializable_model_parse_serialized (GVariant *data); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (DeeSerializableModel, dee_serializable_model, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (DEE_TYPE_MODEL, dee_serializable_model_model_iface_init) G_IMPLEMENT_INTERFACE (DEE_TYPE_SERIALIZABLE, dee_serializable_model_serializable_iface_init)); #define DEE_SERIALIZABLE_MODEL_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_SERIALIZABLE_MODEL, DeeSerializableModelPrivate)) #define MODEL_VARIANT_TYPE_1_0 G_VARIANT_TYPE ("(asaav(tt))") #define MODEL_VARIANT_TYPE G_VARIANT_TYPE ("(asaav(tt)a{sv})") /** * DeeSerializableModelPrivate: * * Ignore this structure. */ struct _DeeSerializableModelPrivate { /* Seqnum tracking */ guint64 seqnum; /* Column type info */ guint n_columns; gchar **column_schemas; // NULL terminated gchar **column_names; // NULL terminated guint32 *column_name_hashes; GHashTable *field_schemas; gboolean inside_changeset; }; typedef struct _FieldSchemaInfo FieldSchemaInfo; struct _FieldSchemaInfo { gint ref_count; // thread unsafe refcount gchar *schema; guint column; }; /* * Public overridable DeeSerializableModel methods */ static guint64 dee_serializable_model_get_seqnum_real (DeeModel *self); static void dee_serializable_model_set_seqnum_real (DeeModel *self, guint64 seqnum); static guint64 dee_serializable_model_inc_seqnum_real (DeeModel *self); /* * DeeModel forward declarations */ static const gchar* const* dee_serializable_model_get_schema (DeeModel *self, guint *num_columns); static const gchar* dee_serializable_model_get_column_schema (DeeModel *self, guint column); static const gchar* dee_serializable_model_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column); static gint dee_serializable_model_get_column_index (DeeModel *self, const gchar *column_name); static void dee_serializable_model_set_schema_full (DeeModel *self, const gchar* const *column_schemas, guint num_columns); static void dee_serializable_model_set_column_names_full (DeeModel *self, const gchar **column_names, guint num_columns); static const gchar** dee_serializable_model_get_column_names (DeeModel *self, guint *num_columns); static void dee_serializable_model_register_vardict_schema (DeeModel *self, guint column, GHashTable *schema); static GHashTable* dee_serializable_model_get_vardict_schema (DeeModel *self, guint column); static guint dee_serializable_model_get_n_columns (DeeModel *self); static guint dee_serializable_model_get_n_rows (DeeModel *self); static void dee_serializable_model_clear (DeeModel *self); static DeeModelIter* dee_serializable_model_append_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_serializable_model_prepend_row (DeeModel *self, GVariant **row_members); static DeeModelIter* dee_serializable_model_insert_row (DeeModel *self, guint pos, GVariant **row_members); static DeeModelIter* dee_serializable_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static DeeModelIter* dee_serializable_model_insert_row_sorted (DeeModel *self, GVariant **row_members, DeeCompareRowFunc cmp_func, gpointer user_data); static DeeModelIter* dee_serializable_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found); static void dee_serializable_model_remove (DeeModel *self, DeeModelIter *iter); static void dee_serializable_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value); static void dee_serializable_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members); static GVariant* dee_serializable_model_get_value (DeeModel *self, DeeModelIter *iter, guint column); static GVariant* dee_serializable_model_get_value_by_name (DeeModel *self, DeeModelIter *iter, const gchar *column_name); static GVariant** dee_serializable_model_get_row (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members); static DeeModelIter* dee_serializable_model_get_first_iter (DeeModel *self); static DeeModelIter* dee_serializable_model_get_last_iter (DeeModel *self); static DeeModelIter* dee_serializable_model_get_iter_at_row (DeeModel *self, guint row); static gboolean dee_serializable_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column); static guchar dee_serializable_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column); static gint32 dee_serializable_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column); static guint32 dee_serializable_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column); static gint64 dee_serializable_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column); static guint64 dee_serializable_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column); static gdouble dee_serializable_model_get_double (DeeModel *self, DeeModelIter *iter, guint column); static const gchar* dee_serializable_model_get_string (DeeModel *self, DeeModelIter *iter, guint column); static DeeModelIter* dee_serializable_model_next (DeeModel *self, DeeModelIter *iter); static DeeModelIter* dee_serializable_model_prev (DeeModel *self, DeeModelIter *iter); static gboolean dee_serializable_model_is_first (DeeModel *self, DeeModelIter *iter); static gboolean dee_serializable_model_is_last (DeeModel *self, DeeModelIter *iter); static void dee_serializable_model_begin_changeset (DeeModel *self); static void dee_serializable_model_end_changeset (DeeModel *self); static guint sigid_changeset_started = 0; static guint sigid_changeset_finished = 0; /* FieldSchemaInfo methods */ static FieldSchemaInfo* field_schema_info_new (const gchar *schema, guint column) { FieldSchemaInfo *info; info = g_slice_new (FieldSchemaInfo); info->ref_count = 1; info->schema = g_strdup (schema); info->column = column; return info; } static FieldSchemaInfo* field_schema_info_ref (FieldSchemaInfo *info) { g_return_val_if_fail (info, NULL); info->ref_count++; return info; } static void field_schema_info_unref (FieldSchemaInfo *info) { g_return_if_fail (info); g_return_if_fail (info->ref_count > 0); if (--info->ref_count <= 0) { g_free (info->schema); g_slice_free (FieldSchemaInfo, info); } } /* GObject Init */ static void dee_serializable_model_finalize (GObject *object) { DeeSerializableModelPrivate *priv = DEE_SERIALIZABLE_MODEL (object)->priv; priv->n_columns = 0; priv->seqnum = 0; if (priv->column_schemas != NULL) { g_strfreev (priv->column_schemas); priv->column_schemas = NULL; } if (priv->column_names != NULL) { g_strfreev (priv->column_names); priv->column_names = NULL; } if (priv->column_name_hashes != NULL) { g_free (priv->column_name_hashes); priv->column_name_hashes = NULL; } if (priv->field_schemas != NULL) { g_hash_table_unref (priv->field_schemas); priv->field_schemas = NULL; } G_OBJECT_CLASS (dee_serializable_model_parent_class)->finalize (object); } static void dee_serializable_model_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { switch (id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_serializable_model_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { switch (id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_serializable_model_class_init (DeeSerializableModelClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_serializable_model_finalize; obj_class->set_property = dee_serializable_model_set_property; obj_class->get_property = dee_serializable_model_get_property; klass->get_seqnum = dee_serializable_model_get_seqnum_real; klass->set_seqnum = dee_serializable_model_set_seqnum_real; klass->inc_seqnum = dee_serializable_model_inc_seqnum_real; sigid_changeset_started = g_signal_lookup ("changeset-started", DEE_TYPE_MODEL); sigid_changeset_finished = g_signal_lookup ("changeset-finished", DEE_TYPE_MODEL); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeSerializableModelPrivate)); } static void dee_serializable_model_init (DeeSerializableModel *model) { DeeSerializableModelPrivate *priv; priv = model->priv = DEE_SERIALIZABLE_MODEL_GET_PRIVATE (model); priv->seqnum = 0; priv->n_columns = 0; priv->column_schemas = NULL; } static guint64 dee_serializable_model_get_seqnum_real (DeeModel *self) { g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); return DEE_SERIALIZABLE_MODEL (self)->priv->seqnum; } /** * dee_serializable_model_get_seqnum: * * @self: (type DeeSerializableModel): A #DeeSerializableModel instance * * Return value: Sequence number of this #DeeSerializableModel. */ guint64 dee_serializable_model_get_seqnum (DeeModel *self) { g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); return DEE_SERIALIZABLE_MODEL_GET_CLASS (self)->get_seqnum (self); } static void dee_serializable_model_set_seqnum_real (DeeModel *self, guint64 seqnum) { g_return_if_fail (DEE_IS_SERIALIZABLE_MODEL (self)); DEE_SERIALIZABLE_MODEL (self)->priv->seqnum = seqnum; } /** * dee_serializable_model_set_seqnum: * * @self: (type DeeSerializableModel): A #DeeSerializableModel instance * @seqnum: Sequence number * * Sets sequence number of this #DeeSerializableModel. */ void dee_serializable_model_set_seqnum (DeeModel *self, guint64 seqnum) { g_return_if_fail (DEE_IS_SERIALIZABLE_MODEL (self)); DEE_SERIALIZABLE_MODEL_GET_CLASS (self)->set_seqnum (self, seqnum); } static guint64 dee_serializable_model_inc_seqnum_real (DeeModel *self) { g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); return ++DEE_SERIALIZABLE_MODEL (self)->priv->seqnum; } /** * dee_serializable_model_inc_seqnum: * * @self: (type DeeSerializableModel): A #DeeSerializableModel instance * * Increments sequence number of this #DeeSerializableModel. */ guint64 dee_serializable_model_inc_seqnum (DeeModel *self) { g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); return DEE_SERIALIZABLE_MODEL_GET_CLASS (self)->inc_seqnum (self); } /* * DeeModel API */ static void dee_serializable_model_set_schema_full (DeeModel *self, const gchar* const *column_schemas, guint num_columns) { DeeSerializableModelPrivate *priv; gchar **column_schemas_copy; guint i; g_return_if_fail (DEE_IS_SERIALIZABLE_MODEL (self)); g_return_if_fail (column_schemas != NULL); priv = DEE_SERIALIZABLE_MODEL (self)->priv; if (priv->column_schemas != NULL) { g_critical ("The DeeModel %p already has a schema", self); return; } /* Allocate space to store the column schemas. We NULL terminate it * in order to play well with g_strfreev() */ column_schemas_copy = g_new0 (gchar*, num_columns + 1); /* Validate the type strings and copy the schema for our selves */ for (i = 0; i < num_columns; i++) { if (!g_variant_type_string_is_valid (column_schemas[i])) { g_critical ("When setting schema for DeeModel %p: '%s' is not a " "valid type string", self, column_schemas[i]); return; } column_schemas_copy[i] = g_strdup (column_schemas[i]); } /* Register the validated types as our column types */ priv->column_schemas = column_schemas_copy; // steal priv->n_columns = num_columns; #ifdef ENABLE_TRACE_LOG gchar* schema = g_strjoinv (", ", priv->column_schemas); trace_object (self, "Set schema: (%s)", schema); g_free (schema); #endif } static void dee_serializable_model_set_column_names_full (DeeModel *self, const gchar **column_names, guint num_columns) { DeeSerializableModelPrivate *priv; guint i, j; gboolean any_name_null; g_return_if_fail (DEE_IS_SERIALIZABLE_MODEL (self)); priv = DEE_SERIALIZABLE_MODEL (self)->priv; any_name_null = FALSE; for (i = 0; i < num_columns; i++) any_name_null |= column_names[i] == NULL; if (num_columns < priv->n_columns || any_name_null) { g_critical ("All column names have to be set!"); return; } if (priv->column_names) g_strfreev (priv->column_names); if (priv->column_name_hashes) g_free (priv->column_name_hashes); priv->column_names = g_new0 (gchar*, priv->n_columns + 1); priv->column_name_hashes = g_new0 (guint32, priv->n_columns); for (i = 0; i < num_columns; i++) { priv->column_names[i] = g_strdup (column_names[i]); // hash for fast compares priv->column_name_hashes[i] = column_names[i] != NULL ? g_str_hash (column_names[i]) : 0; } // check for uniqueness for (i = 0; i < num_columns; i++) { for (j = i+1; j < num_columns; j++) { if (g_strcmp0 (priv->column_names[i], priv->column_names[j]) == 0) { g_warning ("Column names for columns %u and %u are the same!", i, j); } } } } static const gchar** dee_serializable_model_get_column_names (DeeModel *self, guint *n_columns) { DeeSerializableModelPrivate *priv; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); priv = ((DeeSerializableModel*) self)->priv; if (n_columns != NULL) *n_columns = priv->n_columns; return (const gchar**) priv->column_names; } static void dee_serializable_model_register_vardict_schema (DeeModel *self, guint column, GHashTable *schema) { DeeSerializableModelPrivate *priv; GHashTableIter iter; gpointer key, value; g_return_if_fail (DEE_IS_SERIALIZABLE_MODEL (self)); g_return_if_fail (schema); priv = ((DeeSerializableModel*) self)->priv; g_return_if_fail (priv->column_schemas); g_return_if_fail (column < priv->n_columns); g_return_if_fail (g_variant_type_is_subtype_of (G_VARIANT_TYPE (priv->column_schemas[column]), G_VARIANT_TYPE_VARDICT)); if (priv->column_names == NULL || priv->column_names[column] == NULL) { g_critical ("Column name for column %u has to be set before calling " "dee_model_register_vardict_schema", column); return; } if (priv->field_schemas == NULL) priv->field_schemas = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) field_schema_info_unref); g_hash_table_iter_init (&iter, schema); while (g_hash_table_iter_next (&iter, &key, &value)) { FieldSchemaInfo *info; const gchar *field_schema; gchar *full_name; guint registered_column; field_schema = dee_model_get_field_schema (self, key, ®istered_column); if (field_schema && registered_column != column) { g_warning ("Field '%s' is already registered for column %u! Please " "use fully qualified names to refer to it ('%s::%s' and " "'%s::%s')", (gchar*) key, registered_column, priv->column_names[registered_column], (gchar*) key, priv->column_names[column], (gchar*) key); } else if (field_schema && !g_str_equal (field_schema, value)) { g_warning ("Field '%s' was already registered with schema '%s'! " "Overwriting with schema '%s'", (gchar*) key, field_schema, (gchar*) value); } info = field_schema_info_new (value, column); g_hash_table_insert (priv->field_schemas, g_strdup (key), info); full_name = g_strdup_printf ("%s::%s", priv->column_names[column], (gchar*) key); /* transfering ownership of full_name to hashtable */ g_hash_table_insert (priv->field_schemas, full_name, field_schema_info_ref (info)); } } static GHashTable* dee_serializable_model_get_vardict_schema (DeeModel *self, guint column) { DeeSerializableModelPrivate *priv; GHashTable *result; GHashTableIter iter; gpointer key, value; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); priv = ((DeeSerializableModel*) self)->priv; g_return_val_if_fail (priv->column_schemas, NULL); g_return_val_if_fail (column < priv->n_columns, NULL); g_return_val_if_fail (g_variant_type_is_subtype_of (G_VARIANT_TYPE (priv->column_schemas[column]), G_VARIANT_TYPE_VARDICT), NULL); if (priv->field_schemas == NULL) return NULL; result = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_iter_init (&iter, priv->field_schemas); while (g_hash_table_iter_next (&iter, &key, &value)) { gchar *field, *field_name; FieldSchemaInfo *info; field = (gchar*) key; info = (FieldSchemaInfo*) value; if (info->column != column) continue; field_name = strstr (field, "::"); field_name = field_name != NULL ? field_name + 2 : field; g_hash_table_insert (result, field_name, info->schema); } return result; } static const gchar* const* dee_serializable_model_get_schema (DeeModel *self, guint *n_columns) { DeeSerializableModelPrivate *priv; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); priv = ((DeeSerializableModel*) self)->priv; if (n_columns != NULL) *n_columns = priv->n_columns; return (const gchar**) priv->column_schemas; } static const gchar* dee_serializable_model_get_column_schema (DeeModel *self, guint column) { DeeSerializableModelPrivate *priv; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); priv = ((DeeSerializableModel*) self)->priv; g_return_val_if_fail (column < priv->n_columns, NULL); return priv->column_schemas[column]; } static const gchar* dee_serializable_model_get_field_schema (DeeModel *self, const gchar *field_name, guint *out_column) { DeeSerializableModelPrivate *priv; FieldSchemaInfo *info; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); g_return_val_if_fail (field_name, NULL); priv = ((DeeSerializableModel*) self)->priv; if (priv->field_schemas == NULL) return NULL; info = g_hash_table_lookup (priv->field_schemas, field_name); if (info == NULL) return NULL; if (out_column) *out_column = info->column; return info->schema; } static gint dee_serializable_model_get_column_index (DeeModel *self, const gchar *column_name) { gint i; guint hash; DeeSerializableModelPrivate *priv; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), -1); priv = ((DeeSerializableModel*) self)->priv; if (priv->column_names == NULL || column_name == NULL) return -1; hash = g_str_hash (column_name); // FIXME: use a hashtable instead? for (i = 0; i < priv->n_columns; i++) { if (priv->column_name_hashes[i] == hash && g_str_equal (priv->column_names[i], column_name)) return i; } return -1; } static guint dee_serializable_model_get_n_columns (DeeModel *self) { g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); return ((DeeSerializableModel*) self)->priv->n_columns; } static guint dee_serializable_model_get_n_rows (DeeModel *self) { DeeModelIter *iter, *end; guint count; count = 0; end = dee_model_get_last_iter (self); iter = dee_model_get_first_iter (self); while (iter != end) { iter = dee_model_next (self, iter); count++; } return count; } static void dee_serializable_model_clear (DeeModel *self) { DeeModelIter *iter, *end; g_return_if_fail (DEE_IS_SERIALIZABLE_MODEL (self)); iter = dee_model_get_first_iter (self); end = dee_model_get_last_iter (self); while (iter != end) { dee_model_remove (self, iter); iter = dee_model_get_first_iter (self); } } static DeeModelIter* dee_serializable_model_prepend_row (DeeModel *self, GVariant **row_members) { DeeModelIter *iter; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); iter = dee_model_get_first_iter (self); return dee_model_insert_row_before (self, iter, row_members); } static DeeModelIter* dee_serializable_model_append_row (DeeModel *self, GVariant **row_members) { DeeModelIter *iter; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); iter = dee_model_get_last_iter (self); return dee_model_insert_row_before (self, iter, row_members); } static DeeModelIter* dee_serializable_model_insert_row (DeeModel *self, guint pos, GVariant **row_members) { DeeModelIter *iter; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); if (pos > 0) { iter = dee_model_get_iter_at_row (self, pos); return dee_model_insert_row_before (self, iter, row_members); } else if (pos == 0) return dee_model_prepend_row (self, row_members); else return dee_model_append_row (self, row_members); } static DeeModelIter* dee_serializable_model_insert_row_before (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static DeeModelIter* dee_serializable_model_insert_row_sorted (DeeModel *self, GVariant **row_members, DeeCompareRowFunc cmp_func, gpointer user_data) { DeeModelIter *iter; gboolean was_found; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); g_return_val_if_fail (row_members != NULL, NULL); g_return_val_if_fail (cmp_func != NULL, NULL); iter = dee_model_find_row_sorted (self, row_members, cmp_func, user_data, &was_found); if (was_found) iter = dee_model_next (self, iter); return dee_model_insert_row_before (self, iter, row_members); } static DeeModelIter* dee_serializable_model_find_row_sorted (DeeModel *self, GVariant **row_spec, DeeCompareRowFunc cmp_func, gpointer user_data, gboolean *out_was_found) { DeeModelIter *iter, *end, *last_matching; GVariant **row_buf; gint cmp_result; guint n_cols, i; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); g_return_val_if_fail (row_spec != NULL, NULL); g_return_val_if_fail (cmp_func != NULL, NULL); last_matching = NULL; if (out_was_found != NULL) *out_was_found = FALSE; n_cols = dee_model_get_n_columns (self); /* Stack allocate the buffer for speed, and so we don't have to free */ row_buf = g_alloca (n_cols * sizeof (gpointer)); iter = dee_model_get_first_iter (self); end = dee_model_get_last_iter (self); while (iter != end) { dee_model_get_row (self, iter, row_buf); cmp_result = cmp_func (row_buf, row_spec, user_data); /* we're returning last matching row to make ordering * of insert_row_sorted stable and fast */ while (cmp_result == 0) { last_matching = iter; iter = dee_model_next (self, iter); if (iter == end) { iter = last_matching; break; } for (i = 0; i < n_cols; i++) g_variant_unref (row_buf[i]); dee_model_get_row (self, iter, row_buf); cmp_result = cmp_func (row_buf, row_spec, user_data); } for (i = 0; i < n_cols; i++) g_variant_unref (row_buf[i]); /* if we're past the matching row, the loop can be quit */ if (cmp_result >= 0) break; iter = dee_model_next (self, iter); } if (out_was_found != NULL && last_matching != NULL) *out_was_found = TRUE; return last_matching ? last_matching : iter; } static void dee_serializable_model_remove (DeeModel *self, DeeModelIter *iter_) { g_critical ("%s not implemented", G_STRFUNC); } static void dee_serializable_model_set_value (DeeModel *self, DeeModelIter *iter, guint column, GVariant *value) { g_critical ("%s not implemented", G_STRFUNC); } static void dee_serializable_model_set_row (DeeModel *self, DeeModelIter *iter, GVariant **row_members) { g_critical ("%s not implemented", G_STRFUNC); } static GVariant* dee_serializable_model_get_value (DeeModel *self, DeeModelIter *iter, guint column) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static GVariant* dee_serializable_model_get_value_by_name (DeeModel *self, DeeModelIter *iter, const gchar *column_name) { gint col_index; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); col_index = dee_model_get_column_index (self, column_name); if (col_index >= 0) { return dee_model_get_value (self, iter, col_index); } else if (dee_model_get_field_schema (self, column_name, (guint*) &col_index)) { GVariant *dict, *result; const gchar *key_name; dict = dee_model_get_value (self, iter, col_index); // handle full "column::field" name key_name = strstr(column_name, "::"); key_name = key_name != NULL ? key_name + 2 : column_name; result = g_variant_lookup_value (dict, key_name, NULL); g_variant_unref (dict); return result; } return NULL; } static GVariant** dee_serializable_model_get_row (DeeModel *self, DeeModelIter *iter, GVariant **out_row_members) { guint col, n_cols; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); n_cols = dee_model_get_n_columns (self); if (out_row_members == NULL) out_row_members = g_new0 (GVariant*, n_cols + 1); for (col = 0; col < n_cols; col++) out_row_members[col] = dee_model_get_value (self, iter, col); return out_row_members; } static gboolean dee_serializable_model_get_bool (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; gboolean b; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), FALSE); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve bool from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return FALSE; } b = g_variant_get_boolean (value); g_variant_unref (value); return b; } static guchar dee_serializable_model_get_uchar (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; guchar u; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), '\0'); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve uchar from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return '\0'; } u = g_variant_get_byte(value); g_variant_unref (value); return u; } static gint32 dee_serializable_model_get_int32 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; gint32 i; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve int64 from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return 0; } i = g_variant_get_int32 (value); g_variant_unref (value); return i; } static guint32 dee_serializable_model_get_uint32 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; guint32 u; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve uint32 from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return 0; } u = g_variant_get_uint32 (value); g_variant_unref (value); return u; } static gint64 dee_serializable_model_get_int64 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; gint64 i; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), G_GINT64_CONSTANT (0)); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve int64 from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return G_GINT64_CONSTANT (0); } i = g_variant_get_int64 (value); g_variant_unref (value); return i; } static guint64 dee_serializable_model_get_uint64 (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; guint64 u; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), G_GUINT64_CONSTANT (0)); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve uint64 from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return G_GUINT64_CONSTANT (0); } u = g_variant_get_uint64 (value); g_variant_unref (value); return u; } static gdouble dee_serializable_model_get_double (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; gdouble d; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve double from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return 0; } d = g_variant_get_double (value); g_variant_unref (value); return d; } static const gchar* dee_serializable_model_get_string (DeeModel *self, DeeModelIter *iter, guint column) { GVariant *value; const gchar *s; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); value = dee_model_get_value (self, iter, column); if (G_UNLIKELY (value == NULL)) { g_critical ("Failed to retrieve string from row %u column %u in %s@%p", dee_model_get_position (self, iter), column, G_OBJECT_TYPE_NAME (self), self); return NULL; } s = g_variant_get_string (value, NULL); g_variant_unref (value); return s; } static DeeModelIter* dee_serializable_model_get_first_iter (DeeModel *self) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static DeeModelIter* dee_serializable_model_get_last_iter (DeeModel *self) { DeeModelIter *iter; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); iter = dee_model_get_first_iter (self); while (!dee_model_is_last (self, iter)) iter = dee_model_next (self, iter); return iter; } static DeeModelIter* dee_serializable_model_get_iter_at_row (DeeModel *self, guint row) { DeeModelIter *iter; guint pos; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), NULL); pos = 0; iter = dee_model_get_first_iter (self); while (!dee_model_is_last (self, iter) && pos < row) { iter = dee_model_next (self, iter); pos++; } if (dee_model_is_last (self, iter)) { g_critical ("Index %u is out of bounds in model of size %u", row, pos); } return iter; } static DeeModelIter* dee_serializable_model_next (DeeModel *self, DeeModelIter *iter) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static DeeModelIter* dee_serializable_model_prev (DeeModel *self, DeeModelIter *iter) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static gboolean dee_serializable_model_is_first (DeeModel *self, DeeModelIter *iter) { DeeModelIter *first; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), FALSE); first = dee_model_get_first_iter (self); return first == iter; } static gboolean dee_serializable_model_is_last (DeeModel *self, DeeModelIter *iter) { DeeModelIter *last; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), FALSE); last = dee_model_get_last_iter (self); return last == iter; } static guint dee_serializable_model_get_position (DeeModel *self, DeeModelIter *iter) { DeeModelIter *_iter; guint pos; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), 0); pos = 0; _iter = dee_model_get_first_iter (self); while (!dee_model_is_last (self, iter) && iter != _iter) _iter = dee_model_next (self, _iter); pos++; if (iter == _iter) return pos; else { g_critical ("Can not find position of unknown iter %p", iter); return -1; } } static void dee_serializable_model_begin_changeset (DeeModel *self) { DeeSerializableModelPrivate *priv; priv = DEE_SERIALIZABLE_MODEL (self)->priv; if (!priv->inside_changeset) { priv->inside_changeset = TRUE; g_signal_emit (self, sigid_changeset_started, 0); } else { g_warning ("Ignored call to dee_model_begin_changeset, finish " "the current changeset using dee_model_end_changeset first"); } } static void dee_serializable_model_end_changeset (DeeModel *self) { DeeSerializableModelPrivate *priv; priv = DEE_SERIALIZABLE_MODEL (self)->priv; if (priv->inside_changeset) { priv->inside_changeset = FALSE; g_signal_emit (self, sigid_changeset_finished, 0); } else { g_warning ("Ignored call to dee_model_end_changeset, " "dee_model_begin_changeset has to be called first"); } } static DeeModelTag* dee_serializable_model_register_tag (DeeModel *self, GDestroyNotify tag_destroy) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static gpointer dee_serializable_model_get_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag) { g_critical ("%s not implemented", G_STRFUNC); return NULL; } static void dee_serializable_model_set_tag (DeeModel *self, DeeModelIter *iter, DeeModelTag *tag, gpointer value) { g_critical ("%s not implemented", G_STRFUNC); } /* Build a '(sasaavauay(tt))' suitable for sending in a Clone response */ static GVariant* dee_serializable_model_serialize (DeeSerializable *self) { DeeModel *_self; GVariantBuilder aav, clone, fields, vardict; GVariant *val, *tt, *schema, *col_names; DeeModelIter *iter; guint i, j, n_rows, n_columns; guint64 last_seqnum; const gchar* const *column_schemas; const gchar **column_names; g_return_val_if_fail (DEE_IS_SERIALIZABLE_MODEL (self), FALSE); trace_object (self, "Building clone"); _self = DEE_MODEL (self); n_columns = dee_model_get_n_columns (_self); g_variant_builder_init (&aav, G_VARIANT_TYPE ("aav")); /* Clone the rows */ i = 0; iter = dee_model_get_first_iter (_self); while (!dee_model_is_last (_self, iter)) { g_variant_builder_open (&aav, G_VARIANT_TYPE ("av")); for (j = 0; j < n_columns; j++) { val = dee_model_get_value (_self, iter, j); g_variant_builder_add_value (&aav, g_variant_new_variant (val)); g_variant_unref (val); } g_variant_builder_close (&aav); iter = dee_model_next (_self, iter); i++; } n_rows = i; /* Collect the schema */ column_schemas = dee_model_get_schema(_self, NULL); schema = g_variant_new_strv (column_schemas, -1); /* Collect the column names */ column_names = dee_model_get_column_names (_self, NULL); col_names = g_variant_new_strv (column_names, column_names != NULL ? n_columns : 0); g_variant_builder_init (&fields, G_VARIANT_TYPE ("a(uss)")); for (i = 0; i < n_columns; i++) { GHashTable *field_schemas; GHashTableIter ht_iter; gpointer key, value; if (!g_variant_type_is_subtype_of (G_VARIANT_TYPE (column_schemas[i]), G_VARIANT_TYPE_VARDICT)) continue; field_schemas = dee_model_get_vardict_schema (_self, i); if (field_schemas == NULL) continue; g_hash_table_iter_init (&ht_iter, field_schemas); while (g_hash_table_iter_next (&ht_iter, &key, &value)) { g_variant_builder_add (&fields, "(uss)", i, key, value); } g_hash_table_unref (field_schemas); } /* Collect the seqnum */ last_seqnum = dee_serializable_model_get_seqnum (_self); tt = g_variant_new ("(tt)", last_seqnum - n_rows, last_seqnum); /* Put all extra properties in a vardict */ g_variant_builder_init (&vardict, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&vardict, "{sv}", "column-names", col_names); g_variant_builder_add (&vardict, "{sv}", "fields", g_variant_builder_end (&fields)); /* Build the final clone */ g_variant_builder_init (&clone, MODEL_VARIANT_TYPE); g_variant_builder_add_value (&clone, schema); g_variant_builder_add_value (&clone, g_variant_builder_end (&aav)); g_variant_builder_add_value (&clone, tt); g_variant_builder_add_value (&clone, g_variant_builder_end (&vardict)); trace_object (self, "Serialized with %i rows", dee_model_get_n_rows (_self)); return g_variant_builder_end (&clone); } static GObject* dee_serializable_model_parse_serialized (GVariant *data) { DeeModel *model; GVariant *seqnumsv, *col, *vardict; GVariantIter *row_iter, *col_iter, *vardict_schema_iter; GVariant **row; const gchar **schemas; const gchar **column_names; gsize n_cols, i, j, tuple_items; guint64 seqnum_start, seqnum_end; static GType default_model_type = G_TYPE_INVALID; if (default_model_type == G_TYPE_INVALID) { default_model_type = g_type_from_name ("DeeSequenceModel"); if (default_model_type == 0) { g_critical ("Unable to look up default DeeModel type, DeeSequenceModel, for deserialization"); return NULL; } } tuple_items = g_variant_n_children (data); /* We support schema used by both dee 1.0 and 1.2 */ if (tuple_items == 3) /* "(asaav(tt))" */ { g_variant_get (data, "(^a&saav@(tt))", &schemas, &row_iter, &seqnumsv); vardict = NULL; } else if (tuple_items == 4) /* "(asaav(tt)a{sv})" */ { g_variant_get (data, "(^a&saav@(tt)@a{sv})", &schemas, &row_iter, &seqnumsv, &vardict); if (!g_variant_lookup (vardict, "column-names", "^a&s", &column_names)) column_names = NULL; if (!g_variant_lookup (vardict, "fields", "a(uss)", &vardict_schema_iter)) vardict_schema_iter = NULL; } else { g_critical ("Unable to deserialize model: Unrecognized schema"); return NULL; } n_cols = g_strv_length ((gchar**) schemas); g_variant_get (seqnumsv, "(tt)", &seqnum_start, &seqnum_end); model = DEE_MODEL (g_object_new (default_model_type, NULL)); dee_model_set_schema_full (model, schemas, n_cols); dee_serializable_model_set_seqnum (model, seqnum_start); if (vardict) { if (column_names && g_strv_length ((gchar**) column_names) == n_cols) { dee_model_set_column_names_full (model, column_names, n_cols); } if (vardict_schema_iter != NULL) { GHashTable **vardict_schemas; gchar *field_name, *field_schema; guint column_index; vardict_schemas = g_alloca (n_cols * sizeof (GHashTable*)); memset (vardict_schemas, 0, n_cols * sizeof (GHashTable*)); while (g_variant_iter_next (vardict_schema_iter, "(uss)", &column_index, &field_name, &field_schema)) { if (vardict_schemas[column_index] == NULL) { vardict_schemas[column_index] = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, g_free); } // using g_variant_iter_next, so we own field_name & schema g_hash_table_insert (vardict_schemas[column_index], field_name, field_schema); } for (column_index = 0; column_index < n_cols; column_index++) { if (vardict_schemas[column_index] == NULL) continue; dee_model_register_vardict_schema (model, column_index, vardict_schemas[column_index]); g_hash_table_unref (vardict_schemas[column_index]); } g_variant_iter_free (vardict_schema_iter); } g_free (column_names); g_variant_unref (vardict); } /* Note: The 'row' variable is stack allocated. No need to free it */ row = g_alloca (n_cols * sizeof (GVariant *)); i = 0; while (g_variant_iter_next (row_iter, "av", &col_iter)) { if (g_variant_iter_n_children (col_iter) != n_cols) { g_warning ("Row %"G_GSIZE_FORMAT" of serialized DeeSerializableModel " "data has illegal length %"G_GSIZE_FORMAT". Expected %" G_GSIZE_FORMAT, i, g_variant_iter_n_children (col_iter), n_cols); /* Just skip this row - parsers for DeeSerializable should * generally never return NULL */ continue; } j = 0; while (g_variant_iter_next (col_iter, "v", &col)) { row[j] = col; // transfering variant reference to row[j] j++; } dee_model_append_row (model, row); for (j = 0; j < n_cols; j++) { g_variant_unref (row[j]); } i++; g_variant_iter_free (col_iter); } g_variant_iter_free (row_iter); g_free (schemas); g_variant_unref (seqnumsv); return (GObject *) model; } static void dee_serializable_model_model_iface_init (DeeModelIface *iface) { iface->set_schema_full = dee_serializable_model_set_schema_full; iface->get_schema = dee_serializable_model_get_schema; iface->get_column_schema = dee_serializable_model_get_column_schema; iface->get_field_schema = dee_serializable_model_get_field_schema; iface->get_column_index = dee_serializable_model_get_column_index; iface->set_column_names_full = dee_serializable_model_set_column_names_full; iface->get_column_names = dee_serializable_model_get_column_names; iface->get_n_columns = dee_serializable_model_get_n_columns; iface->get_n_rows = dee_serializable_model_get_n_rows; iface->append_row = dee_serializable_model_append_row; iface->prepend_row = dee_serializable_model_prepend_row; iface->insert_row = dee_serializable_model_insert_row; iface->insert_row_before = dee_serializable_model_insert_row_before; iface->insert_row_sorted = dee_serializable_model_insert_row_sorted; iface->find_row_sorted = dee_serializable_model_find_row_sorted; iface->remove = dee_serializable_model_remove; iface->clear = dee_serializable_model_clear; iface->set_value = dee_serializable_model_set_value; iface->set_row = dee_serializable_model_set_row; iface->get_value = dee_serializable_model_get_value; iface->get_value_by_name = dee_serializable_model_get_value_by_name; iface->get_row = dee_serializable_model_get_row; iface->get_first_iter = dee_serializable_model_get_first_iter; iface->get_last_iter = dee_serializable_model_get_last_iter; iface->get_iter_at_row = dee_serializable_model_get_iter_at_row; iface->get_bool = dee_serializable_model_get_bool; iface->get_uchar = dee_serializable_model_get_uchar; iface->get_int32 = dee_serializable_model_get_int32; iface->get_uint32 = dee_serializable_model_get_uint32; iface->get_int64 = dee_serializable_model_get_int64; iface->get_uint64 = dee_serializable_model_get_uint64; iface->get_double = dee_serializable_model_get_double; iface->get_string = dee_serializable_model_get_string; iface->next = dee_serializable_model_next; iface->prev = dee_serializable_model_prev; iface->is_first = dee_serializable_model_is_first; iface->is_last = dee_serializable_model_is_last; iface->get_position = dee_serializable_model_get_position; iface->register_tag = dee_serializable_model_register_tag; iface->get_tag = dee_serializable_model_get_tag; iface->set_tag = dee_serializable_model_set_tag; iface->register_vardict_schema = dee_serializable_model_register_vardict_schema; iface->get_vardict_schema = dee_serializable_model_get_vardict_schema; iface->begin_changeset = dee_serializable_model_begin_changeset; iface->end_changeset = dee_serializable_model_end_changeset; } static void dee_serializable_model_serializable_iface_init (DeeSerializableIface *iface) { iface->serialize = dee_serializable_model_serialize; dee_serializable_register_parser (DEE_TYPE_SERIALIZABLE_MODEL, MODEL_VARIANT_TYPE_1_0, dee_serializable_model_parse_serialized); dee_serializable_register_parser (DEE_TYPE_SERIALIZABLE_MODEL, MODEL_VARIANT_TYPE, dee_serializable_model_parse_serialized); } dee-1.2.7+15.04.20150304/src/dee-resource-manager.h0000644000015300001610000000636512475676210021563 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _DEE_RESOURCE_MANAGER_H_ #define _DEE_RESOURCE_MANAGER_H_ #include #include #include G_BEGIN_DECLS #define DEE_TYPE_RESOURCE_MANAGER (dee_resource_manager_get_type ()) #define DEE_RESOURCE_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_RESOURCE_MANAGER, DeeResourceManager)) #define DEE_IS_RESOURCE_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_RESOURCE_MANAGER)) #define DEE_RESOURCE_MANAGER_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE(obj, dee_resource_manager_get_type (), DeeResourceManagerIface)) typedef struct _DeeResourceManagerIface DeeResourceManagerIface; typedef struct _DeeResourceManager DeeResourceManager; struct _DeeResourceManagerIface { GTypeInterface g_iface; /*< public >*/ gboolean (*store) (DeeResourceManager *self, DeeSerializable *resource, const gchar *resource_name, GError **error); GObject* (*load) (DeeResourceManager *self, const gchar *resource_name, GError **error); /*< private >*/ void (*_dee_resource_manager_1) (void); void (*_dee_resource_manager_2) (void); void (*_dee_resource_manager_3) (void); void (*_dee_resource_manager_4) (void); void (*_dee_resource_manager_5) (void); void (*_dee_resource_manager_6) (void); void (*_dee_resource_manager_7) (void); void (*_dee_resource_manager_8) (void); }; GType dee_resource_manager_get_type (void); gboolean dee_resource_manager_store (DeeResourceManager *self, DeeSerializable *resource, const gchar *resource_name, GError **error); GObject* dee_resource_manager_load (DeeResourceManager *self, const gchar *resource_name, GError **error); DeeResourceManager* dee_resource_manager_get_default (void); G_END_DECLS #endif /* _HAVE_DEE_RESOURCE_MANAGER_H */ dee-1.2.7+15.04.20150304/src/dee-index.h0000644000015300001610000001371512475676210017430 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010-2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_INDEX_H #define _HAVE_DEE_INDEX_H #include #include #include #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_INDEX (dee_index_get_type ()) #define DEE_INDEX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_INDEX, DeeIndex)) #define DEE_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_INDEX, DeeIndexClass)) #define DEE_IS_INDEX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_INDEX)) #define DEE_IS_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_INDEX)) #define DEE_INDEX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_INDEX, DeeIndexClass)) typedef struct _DeeIndexClass DeeIndexClass; typedef struct _DeeIndex DeeIndex; typedef struct _DeeIndexPrivate DeeIndexPrivate; /** * DeeIndexIterFunc: * @key: A key in the index being traversed * @rows: A #DeeResultSet. Do not free or modify. * @userdata: (closure): The pointer passed to dee_index_foreach() * * The signature of the function passed to dee_index_foreach(). * * Be cautious if you plan on modifying the rows in the model via the * DeeModelIters you find. Your code may have to be reentrant since * the index may change in reaction to the changes in the model. It's not * impossible to do this in a non-broken manner, but it may likely require * you calling dee_model_freeze_signals() and dee_model_thaw_signals() at * strategic points. * * Returns: %FALSE if iteration should stop, %TRUE if it should continue */ typedef gboolean (*DeeIndexIterFunc) (const gchar *key, DeeResultSet *rows, gpointer userdata); /** * DeeTermMatchFlag: * @DEE_TERM_MATCH_EXACT: Match terms byte for byte as specified in the * query string * @DEE_TERM_MATCH_PREFIX: Match if the indexed term begins with the byte string * being queried by. This is also sometimes known as * truncated- or wildcard queries * * Flags passed to dee_index_lookup() to control how matching is done. * Note that it is not required that index backends support more than just * #DEE_TERM_MATCH_EXACT. * * You can query for the supported flags with * dee_index_get_supported_term_match_flags(). */ typedef enum { DEE_TERM_MATCH_EXACT = 1 << 0, DEE_TERM_MATCH_PREFIX = 1 << 1 } DeeTermMatchFlag; /** * DeeIndex: * * All fields in the DeeIndex structure are private and should never be * accessed directly */ struct _DeeIndex { /*< private >*/ GObject parent; DeeIndexPrivate *priv; }; struct _DeeIndexClass { GObjectClass parent_class; /*< public >*/ DeeResultSet* (* lookup) (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags); void (* foreach) (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata); guint (* get_n_terms) (DeeIndex *self); guint (* get_n_rows) (DeeIndex *self); guint (* get_n_rows_for_term)(DeeIndex *self, const gchar *term); guint (*get_supported_term_match_flags) (DeeIndex *self); /*< private >*/ void (*_dee_index_1) (void); void (*_dee_index_2) (void); void (*_dee_index_3) (void); void (*_dee_index_4) (void); void (*_dee_index_5) (void); }; GType dee_index_get_type (void); DeeResultSet* dee_index_lookup (DeeIndex *self, const gchar *term, DeeTermMatchFlag flags); DeeModelIter* dee_index_lookup_one (DeeIndex *self, const gchar *term); void dee_index_foreach (DeeIndex *self, const gchar *start_term, DeeIndexIterFunc func, gpointer userdata); DeeModel* dee_index_get_model (DeeIndex *self); DeeAnalyzer* dee_index_get_analyzer (DeeIndex *self); DeeModelReader* dee_index_get_reader (DeeIndex *self); guint dee_index_get_n_terms (DeeIndex *self); guint dee_index_get_n_rows (DeeIndex *self); guint dee_index_get_n_rows_for_term (DeeIndex *self, const gchar *term); guint dee_index_get_supported_term_match_flags (DeeIndex *self); G_END_DECLS #endif /* _HAVE_DEE_INDEX_H */ dee-1.2.7+15.04.20150304/src/dee-resource-manager.c0000644000015300001610000001246112475676210021550 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-resource-manager * @short_description: Store and load #DeeSerializables by name * @include: dee.h * * The #DeeResourceManager API provides a simple API for storing and loading * DeeSerializables from some persistent storage. The resources * are stored in a flat structure identified by names that should be chosen * similarly to DBus names. That is reverse domain names ala * net.launchpad.Example.MyData. */ #ifdef HAVE_CONFIG_H #include #endif #include "dee-serializable.h" #include "dee-resource-manager.h" #include "dee-file-resource-manager.h" #include "trace-log.h" typedef DeeResourceManagerIface DeeResourceManagerInterface; G_DEFINE_INTERFACE (DeeResourceManager, dee_resource_manager, G_TYPE_OBJECT) static DeeResourceManager *_default_resource_manager = NULL; static void dee_resource_manager_default_init (DeeResourceManagerInterface *klass) { } /** * dee_resource_manager_store: * @self: The resource manager to invoke * @resource: (transfer none):A #DeeSerializable to store under @resource_name * @resource_name: The name to store the resource under. Will overwrite any * existing resource with the same name * @error: A return location for a #GError pointer. %NULL to ignore errors * * Store a resource under a given name. The resource manager must guarantee * that the stored data survives system reboots and that you can recreate a * copy of @resource by calling dee_resource_manager_load() using the * same @resource_name. * * Important note: This call may do blocking IO. The resource manager must * guarantee that this call is reasonably fast, like writing the externalized * resource to a file, but not blocking IO over a network socket. * * Return value: %TRUE on success and %FALSE otherwise. In case of a runtime * error the @error pointer will point to a #GError in the * #DeeResourceError domain. */ gboolean dee_resource_manager_store (DeeResourceManager *self, DeeSerializable *resource, const gchar *resource_name, GError **error) { DeeResourceManagerIface *iface; g_return_val_if_fail (DEE_IS_RESOURCE_MANAGER (self), FALSE); g_return_val_if_fail (DEE_IS_SERIALIZABLE(resource), FALSE); g_return_val_if_fail (resource_name != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); iface = DEE_RESOURCE_MANAGER_GET_IFACE (self); return (* iface->store) (self, resource, resource_name, error); } /** * dee_resource_manager_load: * @self: The resource manager to invoke * @resource_name: The name of the resource to retrieve * @error: A return location for a #GError pointer. %NULL to ignore errors * * Load a resource from persistent storage. The loaded resource will be of the * same GType as when it was stored (provided that the same serialization and * parse functions are registered). * * In case of an error the error will be in the #GFileError domain. Specifically * if there is no resource with the name @resource_name the error code will * be #G_FILE_ERROR_NOENT. * * Important note: This call may do blocking IO. The resource manager must * guarantee that this call is reasonably fast, like writing the externalized * resource to a file, but not blocking IO over a network socket. * * Return value: (transfer full): A newly allocated #GObject in case of success * and %NULL otherwise. In case of a runtime error the @error * pointer will be set. */ GObject* dee_resource_manager_load (DeeResourceManager *self, const gchar *resource_name, GError **error) { DeeResourceManagerIface *iface; g_return_val_if_fail (DEE_IS_RESOURCE_MANAGER (self), NULL); g_return_val_if_fail (resource_name != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); iface = DEE_RESOURCE_MANAGER_GET_IFACE (self); return (* iface->load) (self, resource_name, error); } /** * dee_resource_manager_get_default: * * Get a pointer to the platform default #DeeResourceManager. * * Return value: (transfer none): The default resource manager for the platform. * Do not unreference. If you need to keep the instance around * you must manually reference it. */ DeeResourceManager* dee_resource_manager_get_default (void) { if (_default_resource_manager == NULL) { _default_resource_manager = dee_file_resource_manager_new (NULL); } return _default_resource_manager; } dee-1.2.7+15.04.20150304/src/dee-result-set.c0000644000015300001610000001176712475676210020430 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen */ /** * SECTION:dee-result-set * @short_description: Cursor-like interface for results sets * @include: dee.h * * Interface for results returned by dee_index_lookup(). * * This interface utilizes a cursor-like metaphor. You advance the cursor * by calling dee_result_set_next() or adjust it manually by calling * dee_result_set_seek(). * * Calling dee_result_set_next() will also return the row at the * current cursor position. You may retrieve the current row without advancing * the cursor by calling dee_result_set_peek(). * */ #ifdef HAVE_CONFIG_H #include #endif #include "dee-result-set.h" typedef DeeResultSetIface DeeResultSetInterface; G_DEFINE_INTERFACE (DeeResultSet, dee_result_set, G_TYPE_OBJECT) enum { /* Public signals */ DEE_RESULT_SET_LAST_SIGNAL }; static void dee_result_set_default_init (DeeResultSetInterface *klass) { } /** * dee_result_set_get_n_rows: * @self: The #DeeResultSet to get the size of * * Get the number of #DeeModelIters held in a #DeeResultSet. * * Returns: The number of rows held in the result set */ guint dee_result_set_get_n_rows (DeeResultSet *self) { DeeResultSetIface *iface; g_return_val_if_fail (DEE_IS_RESULT_SET (self), 0); iface = DEE_RESULT_SET_GET_IFACE (self); return (* iface->get_n_rows) (self); } /** * dee_result_set_next: * @self: The #DeeResultSet to get a row from * * Get the current row from the result set and advance the cursor. * To ensure that calls to this method will succeed you can call * dee_result_set_has_next(). * * To retrieve the current row without advancing the cursor call * dee_result_set_peek() in stead of this method. * * Returns: (transfer none):The #DeeModelIter at the current cursor position */ DeeModelIter* dee_result_set_next (DeeResultSet *self) { DeeResultSetIface *iface; g_return_val_if_fail (DEE_IS_RESULT_SET (self), NULL); iface = DEE_RESULT_SET_GET_IFACE (self); return (* iface->next) (self); } /** * dee_result_set_has_next: * @self: The #DeeResultSet to check * * Check if a call to dee_result_set_next() will succeed. * * Returns: %TRUE if and only if more rows can be retrieved by calling * dee_result_set_next() */ gboolean dee_result_set_has_next (DeeResultSet *self) { DeeResultSetIface *iface; g_return_val_if_fail (DEE_IS_RESULT_SET (self), FALSE); iface = DEE_RESULT_SET_GET_IFACE (self); return (* iface->has_next) (self); } /** * dee_result_set_peek: * @self: The #DeeResultSet to get a row from * * Get the row at the current cursor position. * * To retrieve the current row and advance the cursor position call * dee_result_set_next() in stead of this method. * * Returns: (transfer none):The #DeeModelIter at the current cursor position */ DeeModelIter* dee_result_set_peek (DeeResultSet *self) { DeeResultSetIface *iface; g_return_val_if_fail (DEE_IS_RESULT_SET (self), NULL); iface = DEE_RESULT_SET_GET_IFACE (self); return (* iface->peek) (self); } /** * dee_result_set_seek: * @self: The #DeeResultSet to seek in * @pos: The position to seek to * * Set the cursor position. Following calls to dee_result_set_peek() * or dee_result_set_next() will read the row at position @pos. */ void dee_result_set_seek (DeeResultSet *self, guint pos) { DeeResultSetIface *iface; g_return_if_fail (DEE_IS_RESULT_SET (self)); iface = DEE_RESULT_SET_GET_IFACE (self); (* iface->seek) (self, pos); } /** * dee_result_set_tell: * @self: The #DeeResultSet to check the cursor position for * * Get the current position of the cursor. * * Returns: The current position of the cursor */ guint dee_result_set_tell (DeeResultSet *self) { DeeResultSetIface *iface; g_return_val_if_fail (DEE_IS_RESULT_SET (self), 0); iface = DEE_RESULT_SET_GET_IFACE (self); return (* iface->tell) (self); } /** * dee_result_set_get_model: * @self: The #DeeResultSet to get the mode for * * Get the model associated with a result set * * Returns: (transfer none): The model that the rows point into */ DeeModel* dee_result_set_get_model (DeeResultSet *self) { DeeResultSetIface *iface; g_return_val_if_fail (DEE_IS_RESULT_SET (self), NULL); iface = DEE_RESULT_SET_GET_IFACE (self); return (* iface->get_model) (self); } dee-1.2.7+15.04.20150304/src/dee-proxy-model.h0000644000015300001610000000511712475676210020575 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) #error "Only can be included directly." #endif #ifndef _HAVE_DEE_PROXY_MODEL_H #define _HAVE_DEE_PROXY_MODEL_H #include #include #include #include G_BEGIN_DECLS #define DEE_TYPE_PROXY_MODEL (dee_proxy_model_get_type ()) #define DEE_PROXY_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ DEE_TYPE_PROXY_MODEL, DeeProxyModel)) #define DEE_PROXY_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ DEE_TYPE_PROXY_MODEL, DeeProxyModelClass)) #define DEE_IS_PROXY_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ DEE_TYPE_PROXY_MODEL)) #define DEE_IS_PROXY_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ DEE_TYPE_PROXY_MODEL)) #define DEE_PROXY_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ DBUS_TYPE_SEQUENCE_MODEL, DeeProxyModelClass)) typedef struct _DeeProxyModel DeeProxyModel; typedef struct _DeeProxyModelClass DeeProxyModelClass; typedef struct _DeeProxyModelPrivate DeeProxyModelPrivate; /** * DeeProxyModel: * * All fields in the DeeProxyModel structure are private and should never be * accessed directly */ struct _DeeProxyModel { /*< private >*/ DeeSerializableModel parent; DeeProxyModelPrivate *priv; }; struct _DeeProxyModelClass { /*< private >*/ DeeSerializableModelClass parent_class; /*< private >*/ void (*_dee_proxy_model_1) (void); void (*_dee_proxy_model_2) (void); void (*_dee_proxy_model_3) (void); void (*_dee_proxy_model_4) (void); }; /** * dee_proxy_model_get_type: * * The GType of #DeeProxyModel * * Return value: the #GType of #DeeProxyModel **/ GType dee_proxy_model_get_type (void); G_END_DECLS #endif /* _HAVE_DEE_PROXY_MODEL_H */ dee-1.2.7+15.04.20150304/src/dee-shared-model.c0000644000015300001610000023632112475676210020660 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by: * Mikkel Kamstrup Erlandsen * Neil Jagdish Patel * Michal Hruby */ /** * SECTION:dee-shared-model * @short_description: A #DeeModel that can synchronize with other * #DeeSharedModel objects across D-Bus. * @include: dee.h * * #DeeSharedModel is created with a name (usually namespaced and unique to * your program(s)) which is used to locate other #DeeSharedModels created * with the same name through D-Bus, and will keep synchronized with them. * * This allows to you build MVC programs with a sane model API, but have the * controller (or multiple views) in a separate process. * * Before you modify the contents of the shared model it is important that * you wait for the model to synchronize with its peers. The normal way to do * this is to wait for the "notify::synchronized" signal. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "dee-peer.h" #include "dee-model.h" #include "dee-proxy-model.h" #include "dee-sequence-model.h" #include "dee-shared-model.h" #include "dee-serializable-model.h" #include "dee-serializable.h" #include "dee-marshal.h" #include "trace-log.h" #include "com.canonical.Dee.Model-xml.h" static void dee_shared_model_serializable_iface_init (DeeSerializableIface *iface); static void dee_shared_model_model_iface_init (DeeModelIface *iface); G_DEFINE_TYPE_WITH_CODE (DeeSharedModel, dee_shared_model, DEE_TYPE_PROXY_MODEL, G_IMPLEMENT_INTERFACE (DEE_TYPE_SERIALIZABLE, dee_shared_model_serializable_iface_init) G_IMPLEMENT_INTERFACE (DEE_TYPE_MODEL, dee_shared_model_model_iface_init)); #define DEE_SHARED_MODEL_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE(obj, DEE_TYPE_SHARED_MODEL, DeeSharedModelPrivate)) #define COMMIT_VARIANT_TYPE G_VARIANT_TYPE("(sasaavauay(tt))") #define COMMIT_TUPLE_ITEMS 6 #define CLONE_VARIANT_TYPE G_VARIANT_TYPE("(sasaavauay(tt)a{sv})") #define CLONE_TUPLE_ITEMS 7 /** * DeeSharedModelPrivate: * * Ignore this structure. **/ struct _DeeSharedModelPrivate { DeePeer *swarm; GSList *connections; gchar *model_path; guint64 last_committed_seqnum; /* Buffer of DeeSharedModelRevisions that we keep in order to batch * our DBus signals. The invariant is that all buffered revisions * are of the same type */ GSList *revision_queue; guint revision_queue_timeout_id; guint acquisition_timer_id; gulong swarm_leader_handler; gulong connection_acquired_handler; gulong connection_closed_handler; GArray *connection_infos; gboolean synchronized; gboolean found_first_peer; gboolean suppress_remote_signals; gboolean clone_in_progress; DeeSharedModelAccessMode access_mode; DeeSharedModelFlushMode flush_mode; }; typedef struct { /* The revision type is: ROWS_ADDED, ROWS_REMOVED, or ROWS_CHANGED */ guchar change_type; guint32 pos; guint64 seqnum; GVariant **row; DeeModel *model; } DeeSharedModelRevision; typedef struct { GDBusConnection *connection; guint signal_subscription_id; guint registration_id; } DeeConnectionInfo; /* Globals */ static GQuark dee_shared_model_error_quark = 0; enum { PROP_0, PROP_PEER, PROP_SYNCHRONIZED, PROP_DISABLE_REMOTE_WRITES, PROP_ACCESS_MODE, PROP_FLUSH_MODE, }; typedef enum { CHANGE_TYPE_ADD = '\x00', CHANGE_TYPE_REMOVE = '\x01', CHANGE_TYPE_CHANGE = '\x02', CHANGE_TYPE_CLEAR = '\x03', } ChangeType; enum { /* Public signal */ BEGIN_TRANSACTION, END_TRANSACTION, LAST_SIGNAL }; static guint32 _signals[LAST_SIGNAL] = { 0 }; /* Forwards */ static void on_connection_acquired (DeeSharedModel *self, GDBusConnection *connection, DeePeer *peer); static void on_connection_closed (DeeSharedModel *self, GDBusConnection *connection, DeePeer *peer); static void commit_transaction (DeeSharedModel *self, const gchar *sender_name, GVariant *transaction); static void on_clone_received (GObject *source_object, GAsyncResult *res, gpointer user_data); static void clone_leader (DeeSharedModel *self); static void on_dbus_signal_received (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data); static void on_leader_changed (DeeSharedModel *self); static DeeSharedModelRevision* dee_shared_model_revision_new (ChangeType type, guint32 pos, guint64 seqnum, GVariant **row, DeeModel *model); static void dee_shared_model_revision_free (DeeSharedModelRevision *rev); static gboolean flush_revision_queue_timeout_cb (DeeModel *self); static guint flush_revision_queue (DeeModel *self); static void enqueue_revision (DeeModel *self, ChangeType type, guint32 pos, guint64 seqnum, GVariant **row); static void dee_shared_model_parse_vardict_schemas (DeeModel *model, GVariantIter *iter, guint n_cols); static void on_self_row_added (DeeModel *self, DeeModelIter *iter); static void on_self_row_removed (DeeModel *self, DeeModelIter *iter); static void on_self_row_changed (DeeModel *self, DeeModelIter *iter); static void reset_model (DeeModel *self); static void invalidate_peer (DeeSharedModel *self, const gchar *sender_name, GDBusConnection *except); static gboolean on_invalidate (DeeSharedModel *self); /* Create a new revision. The revision will own @row */ static DeeSharedModelRevision* dee_shared_model_revision_new (ChangeType type, guint32 pos, guint64 seqnum, GVariant **row, DeeModel *model) { DeeSharedModelRevision *rev; g_return_val_if_fail (type != CHANGE_TYPE_REMOVE && type != CHANGE_TYPE_CLEAR ? row != NULL : TRUE, NULL); rev = g_slice_new (DeeSharedModelRevision); rev->change_type = (guchar) type; rev->pos = pos; rev->seqnum = seqnum; rev->row = row; rev->model = model; return rev; } /* Free all resources owned by a revision, and the revision itself */ static void dee_shared_model_revision_free (DeeSharedModelRevision *rev) { guint n_cols, i; gsize row_slice_size; g_return_if_fail (rev != NULL); n_cols = dee_model_get_n_columns (rev->model); row_slice_size = n_cols * sizeof(gpointer); for (i = 0; i < n_cols && rev->row != NULL; i++) g_variant_unref (rev->row[i]); g_slice_free1 (row_slice_size, rev->row); g_slice_free (DeeSharedModelRevision, rev); } static gboolean flush_revision_queue_timeout_cb (DeeModel *self) { DeeSharedModelPrivate *priv; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), FALSE); priv = DEE_SHARED_MODEL (self)->priv; priv->revision_queue_timeout_id = 0; flush_revision_queue (self); return FALSE; } /* Emit all queued revisions in one signal on the bus. * Clears the revision_queue_timeout if there is one set. * Returns the number of flushed revisions */ static guint flush_revision_queue (DeeModel *self) { DeeSharedModelPrivate *priv; DeeSharedModelRevision *rev; GError *error; GSList *iter; GSList *connection_iter; GVariant *schema; GVariant *transaction_variant; GVariantBuilder aav, au, ay, transaction; guint64 seqnum_begin = 0, seqnum_end = 0; guint n_cols, i; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), 0); priv = DEE_SHARED_MODEL (self)->priv; /* If we are not connected yet, this should be a no-op. * There are two cases to consider: * 1) We are building a model before we are even connected. * This only makes sense if we are sure to become leaders, * we'll assume the programmer knows this * 2) We are resetting the model - no problem */ if (priv->connections == NULL) { trace_object (self, "Flushing revision queue, without a connection. " "This will blow up unless you are the leader model"); g_slist_foreach (priv->revision_queue, (GFunc) dee_shared_model_revision_free, NULL); g_slist_free (priv->revision_queue); priv->revision_queue = NULL; } /* Clear the current timeout if we have one running */ if (priv->revision_queue_timeout_id != 0) { g_source_remove (priv->revision_queue_timeout_id); priv->revision_queue_timeout_id = 0; } /* If we don't have anything queued up, just return. It's assumed beyond * this point that it is non-empty */ if (priv->revision_queue == NULL) { priv->last_committed_seqnum = dee_serializable_model_get_seqnum (self); return 0; } /* Since we always prepend to the queue we need to reverse it */ priv->revision_queue = g_slist_reverse (priv->revision_queue); n_cols = dee_model_get_n_columns (self); /* We know that the revision_queue is non-empty at this point. We peek the * first element and assume that the last seqnum before this transaction * started was the seqnum in the first revision - 1. */ seqnum_end = ((DeeSharedModelRevision *) priv->revision_queue->data)->seqnum - 1; seqnum_begin = priv->last_committed_seqnum; g_variant_builder_init (&aav, G_VARIANT_TYPE ("aav")); g_variant_builder_init (&au, G_VARIANT_TYPE ("au")); g_variant_builder_init (&ay, G_VARIANT_TYPE ("ay")); for (iter = priv->revision_queue; iter; iter = iter->next) { gboolean is_remove; gboolean sequential_revnum; rev = (DeeSharedModelRevision*) iter->data; is_remove = rev->change_type == CHANGE_TYPE_REMOVE || rev->change_type == CHANGE_TYPE_CLEAR; /* Clears are "compressed" so they don't require sequential revnums */ sequential_revnum = rev->change_type != CHANGE_TYPE_CLEAR; /* Sanity check our seqnums */ if (sequential_revnum && rev->seqnum != seqnum_end + 1) { g_critical ("Internal accounting error of DeeSharedModel@%p. Seqnums " "not sequential: " "%"G_GUINT64_FORMAT" != %"G_GUINT64_FORMAT" + 1", self, rev->seqnum, seqnum_end); return 0; } seqnum_end = rev->seqnum; if ((is_remove) != (rev->row == NULL)) { g_critical ("Internal accounting error is DeeSharedModel@%p. " "Transaction row payload must be empty iff the change" "type is is a removal", self); } /* Build the variants for this change */ g_variant_builder_open (&aav, G_VARIANT_TYPE ("av")); for (i = 0; i < n_cols && !is_remove; i++) { g_variant_builder_add_value (&aav, g_variant_new_variant (rev->row[i])); } g_variant_builder_close (&aav); g_variant_builder_add (&au, "u", rev->pos); g_variant_builder_add (&ay, "y", (guchar) rev->change_type); /* Free the revisions while we are traversing the linked list anyway */ dee_shared_model_revision_free (rev); } /* Collect the schema */ schema = g_variant_new_strv (dee_model_get_schema(self, NULL), -1); /* Build the Commit message */ g_variant_builder_init (&transaction, COMMIT_VARIANT_TYPE); g_variant_builder_add (&transaction, "s", dee_peer_get_swarm_name (priv->swarm)); g_variant_builder_add_value (&transaction, schema); g_variant_builder_add_value (&transaction, g_variant_builder_end (&aav)); g_variant_builder_add_value (&transaction, g_variant_builder_end (&au)); g_variant_builder_add_value (&transaction, g_variant_builder_end (&ay)); g_variant_builder_add_value (&transaction, g_variant_new ("(tt)", seqnum_begin, seqnum_end)); transaction_variant = g_variant_builder_end (&transaction); /* Throw a Commit signal */ for (connection_iter = priv->connections; connection_iter != NULL; connection_iter = connection_iter->next) { error = NULL; g_dbus_connection_emit_signal((GDBusConnection*) connection_iter->data, NULL, priv->model_path, "com.canonical.Dee.Model", "Commit", transaction_variant, &error); if (error != NULL) { g_critical ("Failed to emit DBus signal " "com.canonical.Dee.Model.Commit: %s", error->message); g_error_free (error); } } trace_object (self, "Flushed %"G_GUINT64_FORMAT" revisions. " "Seqnum range %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT, seqnum_end - seqnum_begin, seqnum_begin, seqnum_end); /* Free and reset the queue. Note that we freed the individual revisions while * we constructed the Commit message */ g_slist_free (priv->revision_queue); priv->revision_queue = NULL; priv->last_committed_seqnum = seqnum_end; return seqnum_end - seqnum_begin; // Very theoretical overflow possible here... } /* Prepare a revision to be emitted as a signal on the bus. The revisions * are queued up so that we can emit them in batches. Steals the ref on the * row array and assumes the refs on the variants as well */ static void enqueue_revision (DeeModel *self, ChangeType type, guint32 pos, guint64 seqnum, GVariant **row) { DeeSharedModelPrivate *priv; DeeSharedModelRevision *rev; g_return_if_fail (DEE_IS_SHARED_MODEL (self)); priv = DEE_SHARED_MODEL (self)->priv; rev = dee_shared_model_revision_new (type, pos, seqnum, row, self); priv->revision_queue = g_slist_prepend (priv->revision_queue, rev); /* Flush the revision queue once in idle */ if (priv->revision_queue_timeout_id == 0 && priv->flush_mode == DEE_SHARED_MODEL_FLUSH_MODE_AUTOMATIC) { priv->revision_queue_timeout_id = g_idle_add ((GSourceFunc)flush_revision_queue_timeout_cb, self); } } /* GObject stuff */ static void dee_shared_model_finalize (GObject *object) { guint i; DeeSharedModelPrivate *priv = DEE_SHARED_MODEL (object)->priv; /* Flush any pending revisions */ if (priv->revision_queue != NULL) { flush_revision_queue (DEE_MODEL(object)); priv->revision_queue = NULL; } if (priv->acquisition_timer_id != 0) { g_source_remove (priv->acquisition_timer_id); priv->acquisition_timer_id = 0; } if (priv->connection_acquired_handler) { g_signal_handler_disconnect (priv->swarm, priv->connection_acquired_handler); priv->connection_acquired_handler = 0; } if (priv->connection_closed_handler) { g_signal_handler_disconnect (priv->swarm, priv->connection_closed_handler); priv->connection_closed_handler = 0; } if (priv->connection_infos != NULL) { for (i = 0; i < priv->connection_infos->len; i++) { DeeConnectionInfo *info; info = &g_array_index (priv->connection_infos, DeeConnectionInfo, i); g_dbus_connection_unregister_object (info->connection, info->registration_id); g_dbus_connection_signal_unsubscribe (info->connection, info->signal_subscription_id); } g_array_unref (priv->connection_infos); priv->connection_infos = NULL; } if (priv->swarm_leader_handler != 0) { g_signal_handler_disconnect (priv->swarm, priv->swarm_leader_handler); priv->swarm_leader_handler = 0; } if (priv->model_path) { g_free (priv->model_path); } if (priv->connections) { g_slist_free (priv->connections); priv->connections = NULL; } if (priv->swarm) { g_object_unref (priv->swarm); priv->swarm = NULL; } G_OBJECT_CLASS (dee_shared_model_parent_class)->finalize (object); } static void dee_shared_model_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec) { DeeSharedModelPrivate *priv; priv = DEE_SHARED_MODEL (object)->priv; switch (id) { case PROP_PEER: if (priv->swarm != NULL) g_object_unref (priv->swarm); priv->swarm = g_value_dup_object (value); break; case PROP_SYNCHRONIZED: g_critical ("Trying to set read only property DeeSharedModel:synchronized"); break; case PROP_ACCESS_MODE: priv->access_mode = g_value_get_enum (value); break; case PROP_FLUSH_MODE: priv->flush_mode = g_value_get_enum (value); if (priv->flush_mode != DEE_SHARED_MODEL_FLUSH_MODE_AUTOMATIC && priv->revision_queue_timeout_id != 0) { g_source_remove (priv->revision_queue_timeout_id); priv->revision_queue_timeout_id = 0; } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static void dee_shared_model_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec) { DeeSharedModelPrivate *priv; priv = DEE_SHARED_MODEL (object)->priv; switch (id) { case PROP_PEER: g_value_set_object (value, priv->swarm); break; case PROP_SYNCHRONIZED: g_value_set_boolean (value, priv->synchronized); break; case PROP_ACCESS_MODE: g_value_set_enum (value, priv->access_mode); break; case PROP_FLUSH_MODE: g_value_set_enum (value, priv->flush_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); break; } } static gboolean iterate_connections (DeeSharedModel *self) { DeeSharedModelPrivate *priv; GSList *connections_list, *iter; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), FALSE); priv = self->priv; /* unblock the handlers */ g_signal_handler_unblock (priv->swarm, priv->connection_acquired_handler); g_signal_handler_unblock (priv->swarm, priv->connection_closed_handler); connections_list = dee_peer_get_connections (priv->swarm); for (iter = connections_list; iter != NULL; iter = iter->next) { on_connection_acquired (self, (GDBusConnection*) iter->data, priv->swarm); } g_slist_free (connections_list); priv->acquisition_timer_id = 0; return FALSE; } static void dee_shared_model_constructed (GObject *object) { DeeSharedModel *self; DeeSharedModelPrivate *priv; gchar *dummy; GSList *connections_list; /* GObjectClass has NULL 'constructed' member, but we add this check for * future robustness if we ever move to another base class */ if (G_OBJECT_CLASS (dee_shared_model_parent_class)->constructed != NULL) G_OBJECT_CLASS (dee_shared_model_parent_class)->constructed (object); self = DEE_SHARED_MODEL (object); priv = self->priv; if (priv->swarm == NULL) { g_critical ("You must create a DeeSharedModel with a DeePeer " "in the 'peer' property"); return; } /* Create a canonical object path from the well known swarm name */ dummy = g_strdup (dee_peer_get_swarm_name (priv->swarm)); priv->model_path = g_strconcat ("/com/canonical/dee/model/", g_strdelimit (dummy, ".", '/'), NULL); g_free (dummy); priv->swarm_leader_handler = g_signal_connect_swapped (priv->swarm, "notify::swarm-leader", G_CALLBACK (on_leader_changed), self); priv->connection_acquired_handler = g_signal_connect_swapped (priv->swarm, "connection-acquired", G_CALLBACK (on_connection_acquired), self); priv->connection_closed_handler = g_signal_connect_swapped (priv->swarm, "connection-closed", G_CALLBACK (on_connection_closed), self); /* we don't want to invoke on_connection_acquired from here, it would mean * emitting important signal when inside g_object_new, so block the handlers * and call on_connection_acquired in idle callback */ connections_list = dee_peer_get_connections (priv->swarm); if (g_slist_length (connections_list) > 0) { g_signal_handler_block (priv->swarm, priv->connection_acquired_handler); g_signal_handler_block (priv->swarm, priv->connection_closed_handler); priv->acquisition_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) iterate_connections, self, NULL); } g_slist_free (connections_list); } static void dee_shared_model_class_init (DeeSharedModelClass *klass) { GParamSpec *pspec; GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = dee_shared_model_finalize; obj_class->set_property = dee_shared_model_set_property; obj_class->get_property = dee_shared_model_get_property; obj_class->constructed = dee_shared_model_constructed; /** * DeeSharedModel:peer: * * The #DeePeer that this model uses to connect to the swarm */ pspec = g_param_spec_object ("peer", "Peer", "The peer object that monitors the swarm", DEE_TYPE_PEER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_PEER, pspec); /** * DeeSharedModel:synchronized: * * Boolean property defining whether or not the model has synchronized with * its peers (if any) yet. * * You should not modify a #DeeSharedModel that is not synchronized. Before * modifying the model in any way (except calling dee_model_set_schema()) * you should wait for it to become synchronized. */ pspec = g_param_spec_boolean("synchronized", "Synchronized", "Whether the model is synchronized with its peers", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_SYNCHRONIZED, pspec); /** * DeeSharedModel:access-mode: * * Enumeration defining behavior of this model when trying to write to it. * * Setting this to #DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE is useful * when one process is considered an "owner" of a model and all the other * peers are supposed to only synchronize it for reading. * * See also DeePeer:swarm-owner property to ensure ownership of a swarm. */ pspec = g_param_spec_enum ("access-mode", "Access Mode", "Access mode used by this shared model", DEE_TYPE_SHARED_MODEL_ACCESS_MODE, DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_ACCESS_MODE, pspec); /** * DeeSharedModel:flush-mode: * * Enumeration defining the flushing behavior. * * Setting this to #DEE_SHARED_MODEL_FLUSH_MODE_MANUAL will disable * automatic flushing that usually happens when the application's main event * loop is idle. Automatic flushing should be primarily disabled when * a shared model is used from multiple threads, or when not using #GMainLoop. * When disabled, dee_shared_model_flush_revision_queue() needs to be called * explicitely. */ pspec = g_param_spec_enum ("flush-mode", "Flush mode", "Determines whether flushes occur automatically", DEE_TYPE_SHARED_MODEL_FLUSH_MODE, DEE_SHARED_MODEL_FLUSH_MODE_AUTOMATIC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (obj_class, PROP_FLUSH_MODE, pspec); /** * DeeSharedModel::begin-transaction: * @model: The shared model the signal is emitted on * @begin_seqnum: The seqnum the model has now * @end_seqnum: The seqnum the model will have after the transaction is applied * * Emitted right before a remote transaction will be committed to the model. */ _signals[BEGIN_TRANSACTION] = g_signal_new ("begin-transaction", DEE_TYPE_SHARED_MODEL, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _dee_marshal_VOID__UINT64_UINT64, G_TYPE_NONE, 2, G_TYPE_UINT64, G_TYPE_UINT64); /** * DeeSharedModel::end-transaction: * @model: The shared model the signal is emitted on * @begin_seqnum: The seqnum the model had before the transaction was applied * @end_seqnum: The seqnum the model has now * * Emitted right after a remote transaction has been committed to the model. */ _signals[END_TRANSACTION] = g_signal_new ("end-transaction", DEE_TYPE_SHARED_MODEL, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _dee_marshal_VOID__UINT64_UINT64, G_TYPE_NONE, 2, G_TYPE_UINT64, G_TYPE_UINT64); /* Add private data */ g_type_class_add_private (obj_class, sizeof (DeeSharedModelPrivate)); /* Runtime-check that our defines are correct */ g_assert (g_variant_type_n_items (CLONE_VARIANT_TYPE) == CLONE_TUPLE_ITEMS); g_assert (g_variant_type_n_items (COMMIT_VARIANT_TYPE) == COMMIT_TUPLE_ITEMS); } static void dee_shared_model_init (DeeSharedModel *self) { DeeSharedModelPrivate *priv; priv = self->priv = DEE_SHARED_MODEL_GET_PRIVATE (self); priv->swarm = NULL; priv->model_path = NULL; priv->last_committed_seqnum = 0; priv->revision_queue = NULL; priv->revision_queue_timeout_id = 0; priv->swarm_leader_handler = 0; priv->synchronized = FALSE; priv->found_first_peer = FALSE; priv->suppress_remote_signals = FALSE; if (!dee_shared_model_error_quark) dee_shared_model_error_quark = g_quark_from_string ("dbus-model-error"); priv->connections = NULL; priv->connection_infos = g_array_new (FALSE, TRUE, sizeof (DeeConnectionInfo)); /* Connect to our own signals so we can queue up revisions to be emitted * on the bus */ g_signal_connect (self, "row-added", G_CALLBACK (on_self_row_added), NULL); g_signal_connect (self, "row-removed", G_CALLBACK (on_self_row_removed), NULL); g_signal_connect (self, "row-changed", G_CALLBACK (on_self_row_changed), NULL); } static void handle_dbus_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GVariant *retval; g_return_if_fail (DEE_IS_SHARED_MODEL (user_data)); if (g_strcmp0 ("Clone", method_name) == 0) { /* If we have anything in the rev queue it wont validate against the * seqnum for the cloned model. So flush the rev queue before answering * the Clone call */ flush_revision_queue (DEE_MODEL (user_data)); /* We return a special error if we have no schema. It's legal for the * leader to expect the schema from the slaves */ if (dee_model_get_n_columns (DEE_MODEL (user_data)) == 0) { g_dbus_method_invocation_return_dbus_error (invocation, "com.canonical.Dee.Model.NoSchemaError", "No schema defined"); } else { // FIXME: It can be expensive to build the clone. Perhaps thread this? retval = dee_serializable_serialize (DEE_SERIALIZABLE (user_data)); g_dbus_method_invocation_return_value (invocation, retval); /* dee_serializable_serialize returns full ref, unref it */ g_variant_unref (retval); } } else if (g_strcmp0 ("Invalidate", method_name) == 0) { on_invalidate (DEE_SHARED_MODEL (user_data)); g_dbus_method_invocation_return_value (invocation, NULL); } else { g_warning ("Unknown DBus method call %s.%s from %s on DeeSharedModel", interface_name, method_name, sender); } } static const GDBusInterfaceVTable model_interface_vtable = { handle_dbus_method_call, NULL, NULL }; static void on_connection_acquired (DeeSharedModel *self, GDBusConnection *connection, DeePeer *peer) { DeeSharedModelPrivate *priv; DeeConnectionInfo connection_info; GDBusNodeInfo *model_introspection_data; guint dbus_signal_handler; guint model_registration_id; /* Keep the parsed introspection data of the Model interface around */ static GDBusInterfaceInfo *model_interface_info = NULL; g_return_if_fail (DEE_IS_SHARED_MODEL (self)); priv = self->priv; if (connection == NULL) { g_warning ("Internal error in DeeSharedModel. %s called with NULL " "connection", __func__); return; } /* Update our list of connections */ if (priv->connections) g_slist_free (priv->connections); priv->connections = dee_peer_get_connections (priv->swarm); /* Listen for changes from the peers in the same swarm. * We do this by matching arg0 with the swarm name */ dbus_signal_handler = g_dbus_connection_signal_subscribe ( connection, NULL, // sender "com.canonical.Dee.Model", // iface NULL, // member NULL, // object path dee_peer_get_swarm_name (priv->swarm), // arg0 G_DBUS_SIGNAL_FLAGS_NONE, on_dbus_signal_received, self, // user data NULL); // user data destroy /* Load com.canonical.Dee.Model introspection XML on first run */ if (model_interface_info == NULL) { model_introspection_data = g_dbus_node_info_new_for_xml ( com_canonical_Dee_Model_xml, NULL); model_interface_info = g_dbus_node_info_lookup_interface ( model_introspection_data, "com.canonical.Dee.Model"); g_dbus_interface_info_ref (model_interface_info); g_dbus_node_info_unref (model_introspection_data); } /* Export the model on the bus */ model_registration_id = g_dbus_connection_register_object (connection, priv->model_path, /* object path */ model_interface_info, &model_interface_vtable, self, /* user_data */ NULL, /* user_data_free_func */ NULL); /* GError** */ connection_info.connection = connection; connection_info.signal_subscription_id = dbus_signal_handler; connection_info.registration_id = model_registration_id; g_array_append_val (priv->connection_infos, connection_info); /* If we are swarm leaders and we have column type info we are ready by now. * Otherwise we will be ready when we receive the model clone from the leader */ if (dee_peer_is_swarm_leader (priv->swarm)) { if (dee_model_get_n_columns (DEE_MODEL (self)) > 0 && !priv->synchronized) { priv->synchronized = TRUE; g_object_notify (G_OBJECT (self), "synchronized"); } } else if (dee_peer_get_swarm_leader (priv->swarm) != NULL) { /* There is a leader and it's not us. * Start cloning the model of the leader */ clone_leader (self); } else { // FIXME: There's no known leader, peer should soon emit notify::swarm-leader } } static void on_connection_closed (DeeSharedModel *self, GDBusConnection *connection, DeePeer *peer) { DeeSharedModelPrivate *priv; guint i; g_return_if_fail (DEE_IS_SHARED_MODEL (self)); priv = self->priv; /* Update our list of connections */ if (priv->connections) g_slist_free (priv->connections); priv->connections = dee_peer_get_connections (priv->swarm); /* Disconnect signals etc */ for (i = 0; i < priv->connection_infos->len; i++) { DeeConnectionInfo *info; info = &g_array_index (priv->connection_infos, DeeConnectionInfo, i); if (info->connection == connection) { g_dbus_connection_unregister_object (info->connection, info->registration_id); g_dbus_connection_signal_unsubscribe (info->connection, info->signal_subscription_id); /* remove the item */ g_array_remove_index (priv->connection_infos, i); break; } } } /* Callback for clone_leader() */ static void on_clone_received (GObject *source_object, GAsyncResult *res, gpointer user_data) { DeeModel *model; DeeSharedModel *self; DeeSharedModelPrivate *priv; GVariant *data, *transaction; GError *error; GWeakRef *weak_ref; gchar *dbus_error; weak_ref = (GWeakRef*) user_data; self = (DeeSharedModel*) g_weak_ref_get (weak_ref); if (self == NULL) { g_weak_ref_clear (weak_ref); g_free (weak_ref); return; } priv = self->priv; error = NULL; data = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error); if (error != NULL) { dbus_error = g_dbus_error_get_remote_error (error); if (g_strcmp0 (dbus_error, "com.canonical.Dee.Model.NoSchemaError") == 0) { trace_object (self, "Got Clone reply from leader, but leader has no schema"); g_error_free (error); g_free (dbus_error); } else { g_critical ("Failed to clone model from leader: %s", error->message); g_error_free (error); g_free (dbus_error); goto clone_recieved_out; } } /* The data will be NULL if we received a com.canonical.Dee.Model.NoSchemaError, * but in that case we should still consider our selves synchronized */ if (data != NULL) { const gchar **column_names; guint i, n_column_names; GVariant *vardict; GVariantIter *iter; model = DEE_MODEL (self); /* Guard against a race where we might inadvertedly have accepted a Commit * before receiving the initial Clone */ if (dee_model_get_n_columns (model) > 0) { priv->suppress_remote_signals = TRUE; reset_model (model); priv->suppress_remote_signals = FALSE; } /* Support both the 1.0 Clone signature as well as the 1.2 */ if (g_variant_type_equal (g_variant_get_type (data), CLONE_VARIANT_TYPE)) { GVariant *transaction_members[COMMIT_TUPLE_ITEMS]; guint n_elements; n_elements = G_N_ELEMENTS (transaction_members); for (i = 0; i < n_elements; i++) transaction_members[i] = g_variant_get_child_value (data, i); transaction = g_variant_new_tuple (transaction_members, n_elements); transaction = g_variant_ref_sink (transaction); vardict = g_variant_get_child_value (data, 6); if (g_variant_lookup (vardict, "column-names", "^a&s", &column_names)) n_column_names = g_strv_length ((gchar**) column_names); else column_names = NULL; if (!g_variant_lookup (vardict, "fields", "a(uss)", &iter)) iter = NULL; for (i = 0; i < n_elements; i++) g_variant_unref (transaction_members[i]); } else if (g_variant_type_equal (g_variant_get_type (data), COMMIT_VARIANT_TYPE)) { transaction = g_variant_ref (data); vardict = NULL; } else { g_critical ("Unable to Clone model: Unrecognized schema"); goto clone_recieved_out; } /* We use the swarm name as sender_name here, because DBus passes us the * unique name of the swarm leader here and we want to indicate in the debug * messages that the transaction came from the leader */ commit_transaction (self, dee_shared_model_get_swarm_name (self), transaction); if (vardict) { if (column_names && n_column_names > 0 && dee_model_get_column_names (model, NULL) == NULL) { dee_model_set_column_names_full (model, column_names, n_column_names); if (iter != NULL) { dee_shared_model_parse_vardict_schemas (model, iter, n_column_names); g_variant_iter_free (iter); } } g_free (column_names); g_variant_unref (vardict); } g_variant_unref (transaction); g_variant_unref (data); } /* If we where invalidated before, we should be fine now */ if (!priv->synchronized) { priv->synchronized = TRUE; g_object_notify (G_OBJECT (self), "synchronized"); } clone_recieved_out: priv->clone_in_progress = FALSE; g_object_unref (self); // weak ref got us a strong reference g_weak_ref_clear (weak_ref); g_free (weak_ref); } /* Send a Clone message to the swarm leader */ static void clone_leader (DeeSharedModel *self) { DeeSharedModelPrivate *priv; GSList *iter; g_return_if_fail (DEE_IS_SHARED_MODEL (self)); g_return_if_fail (dee_peer_get_swarm_leader (self->priv->swarm) != NULL); g_return_if_fail (self->priv->revision_queue == NULL); g_return_if_fail (dee_model_get_n_rows (DEE_MODEL (self)) == 0); priv = self->priv; trace_object (self, "Cloning leader '%s'", dee_shared_model_get_swarm_name (self)); /* This shouldn't really happen when we have multiple connections, but let's * have it here for consistency */ for (iter = priv->connections; iter != NULL; iter = iter->next) { GWeakRef *weak_ref; weak_ref = g_new (GWeakRef, 1); g_weak_ref_init (weak_ref, self); g_dbus_connection_call((GDBusConnection*) iter->data, dee_shared_model_get_swarm_name (self), // name priv->model_path, // obj path "com.canonical.Dee.Model", // iface "Clone", // member NULL, // args NULL, // ret type G_DBUS_CALL_FLAGS_NONE, -1, // timeout NULL, // cancel on_clone_received, // cb weak_ref); // userdata priv->clone_in_progress = TRUE; } } static void on_dbus_signal_received (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { DeeSharedModel *model; const gchar *unique_name; gboolean forced_ignore; gboolean disable_write; g_return_if_fail (DEE_IS_SHARED_MODEL (user_data)); unique_name = g_dbus_connection_get_unique_name (connection); trace_object (user_data, "%s: sender: %s, our unique_name: %s", __func__, sender_name, unique_name); /* Ignore signals from our selves. We may get those because of the way * we set up the match rules */ if (unique_name != NULL && g_strcmp0 (sender_name, unique_name) == 0) return; if (g_strcmp0 (signal_name, "Commit") == 0) { model = DEE_SHARED_MODEL (user_data); /* If we're waiting for Clone(), we can just ignore Commits coming * meanwhile, this way we'll prevent unnecessary invalidation */ if (model->priv->clone_in_progress) return; /* Similarly if we receive a Commit before knowing who's the swarm leader * (can happen even before Clone() request, ignore the commit */ if (model->priv->synchronized == FALSE && dee_peer_get_swarm_leader (model->priv->swarm) == NULL) return; disable_write = model->priv->access_mode == DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE; forced_ignore = dee_peer_is_swarm_leader (model->priv->swarm) && disable_write; if (!disable_write) { commit_transaction (model, sender_name, parameters); } else if (!forced_ignore) { /* remote writes are disabled, but we're not leader - commit anyway */ g_warning ("Tried to prevent remote write, but SharedModel[%p] is " "not owned by peer named %s.", model, dee_peer_get_swarm_name (model->priv->swarm)); commit_transaction (model, sender_name, parameters); } if (forced_ignore) { /* invalidate all the peers if remote writes are disabled */ invalidate_peer (model, sender_name, NULL); } else if (g_slist_length (model->priv->connections) > 1) { /* this is a server and a client (non-leader) just committed a change * to the model, let's invalidate all other clients */ invalidate_peer (model, sender_name, connection); } } else g_warning ("Unexpected signal %s.%s from %s", interface_name, signal_name, sender_name); } static void on_leader_changed (DeeSharedModel *self) { DeeSharedModelPrivate *priv; priv = self->priv; if (dee_shared_model_is_leader (self)) { /* The leader is the authoritative data source so if we are not * synchronized we will now be by very definition */ if (!priv->synchronized) { priv->synchronized = TRUE; g_object_notify (G_OBJECT (self), "synchronized"); } } else { if (!priv->synchronized) { clone_leader (self); } } } static void commit_transaction (DeeSharedModel *self, const gchar *sender_name, GVariant *transaction) { DeeSharedModelPrivate *priv; GVariantIter iter; GVariant *schema, *row, **row_buf, *val, *aav, *au, *ay, *tt; const gchar **column_schemas; gsize column_schemas_len; gchar *swarm_name; guint64 seqnum_before, seqnum_after, current_seqnum; guint64 n_rows, n_cols, model_n_rows; guint32 pos; guchar change_type; gint i, j; gboolean transaction_error; g_return_if_fail (DEE_IS_SHARED_MODEL (self)); g_return_if_fail (transaction != NULL); g_variant_ref_sink (transaction); priv = self->priv; g_variant_iter_init (&iter, transaction); /* The transaction should have signature '(sasaavauay(tt)'. * Make sure it at least looks right */ if (g_strcmp0 (g_variant_get_type_string (transaction), "(sasaavauay(tt))") != 0) { g_critical ("Unexpected format for Commit message '%s' from %s. " "Expected '(sasaavauay(tt))'", g_variant_get_type_string (transaction), sender_name); g_variant_unref (transaction); return; } /* Assert that this is a Commit for the right swarm name */ g_variant_iter_next (&iter, "s", &swarm_name); if (g_strcmp0 (swarm_name, dee_peer_get_swarm_name (priv->swarm)) != 0) { g_critical ("Error in internal message routing. " "Unexpected swarm name '%s' on Commit from %s." "Expected '%s'", swarm_name, sender_name, dee_peer_get_swarm_name (priv->swarm)); g_variant_unref (transaction); g_free (swarm_name); return; } g_free (swarm_name); /* If the model has no schema then use the one received in the transaction */ schema = g_variant_iter_next_value (&iter); n_cols = dee_model_get_n_columns (DEE_MODEL (self)); if (n_cols == 0) { column_schemas = g_variant_get_strv (schema, &column_schemas_len); if (column_schemas != NULL) { n_cols = column_schemas_len; dee_model_set_schema_full (DEE_MODEL(self), column_schemas, n_cols); g_free (column_schemas); } else { g_warning ("Received transaction before the model schema has been set" " and none received from leader"); g_variant_unref (transaction); g_variant_unref (schema); return; } } g_variant_unref (schema); /* Parse the rest of the transaction */ aav = g_variant_iter_next_value (&iter); au = g_variant_iter_next_value (&iter); ay = g_variant_iter_next_value (&iter); tt = g_variant_iter_next_value (&iter); /* Validate that the seqnums are as we expect */ g_variant_get (tt, "(tt)", &seqnum_before, &seqnum_after); g_variant_unref (tt); transaction_error = FALSE; /* If this is our first transaction we accept anything, if not the * incoming seqnums must align with our own records */ current_seqnum = dee_serializable_model_get_seqnum (DEE_MODEL (self)); if (current_seqnum != 0 && current_seqnum != seqnum_before) { g_warning ("Transaction from %s is in the %s. Expected seqnum %"G_GUINT64_FORMAT ", but got %"G_GUINT64_FORMAT". Ignoring transaction.", sender_name, current_seqnum < seqnum_before ? "future" : "past", current_seqnum, seqnum_before); transaction_error = TRUE; } /* Check that the lengths of all the arrays match up */ n_rows = g_variant_n_children (aav); if (n_rows != g_variant_n_children (au)) { g_warning ("Commit from %s has illegal position vector", sender_name); transaction_error = TRUE; } if (n_rows != g_variant_n_children (ay)) { g_warning ("Commit from %s has illegal change type vector", sender_name); transaction_error = TRUE; } if (n_rows > (seqnum_after - seqnum_before)) { g_warning ("Commit from %s has illegal seqnum count.", sender_name); transaction_error = TRUE; } if (transaction_error) { if (dee_shared_model_is_leader (self)) { g_warning ("Invalidating %s", sender_name); invalidate_peer (self, sender_name, NULL); } else { if (sender_name == NULL || !g_strcmp0 (sender_name, dee_peer_get_swarm_leader (priv->swarm))) { // leader sent an invalid transaction? // let's just invalidate ourselves g_warning ("Errornous transaction came from swarm leader, re-syncing model."); on_invalidate (self); } } g_variant_unref (transaction); g_variant_unref (aav); g_variant_unref (au); g_variant_unref (ay); return; } /* Allocate an array on the stack as a temporary row data buffer */ row_buf = g_alloca (n_cols * sizeof (gpointer)); trace_object (self, "Applying transaction of %i rows", n_rows); /* Phew. Finally. We're ready to merge the changes */ g_signal_emit_by_name (self, "changeset-started"); g_signal_emit (self, _signals[BEGIN_TRANSACTION], 0, seqnum_before, seqnum_after); priv->suppress_remote_signals = TRUE; for (i = 0; i < n_rows; i++) /* Begin outer loop */ { model_n_rows = dee_model_get_n_rows (DEE_MODEL (self)); g_variant_get_child (au, i, "u", &pos); g_variant_get_child (ay, i, "y", &change_type); /* Before parsing the row data we check if it's a remove, * because in that case we might as well not parse the * row data at all */ if (change_type == CHANGE_TYPE_REMOVE) { dee_model_remove (DEE_MODEL (self), dee_model_get_iter_at_row (DEE_MODEL (self), pos)); model_n_rows--; continue; } if (change_type == CHANGE_TYPE_CLEAR) { dee_model_clear (DEE_MODEL (self)); model_n_rows = 0; continue; } /* It's an Add or Change so parse the row data */ row = g_variant_get_child_value (aav, i); /* Add and Change rows must have the correct number of columns */ if (g_variant_n_children (row) != n_cols) { g_critical ("Commit from %s contains rows of illegal length. " "The model may have been left in a dirty state", sender_name); /* cleanup */ g_variant_unref (row); continue; } /* Read the row cells into our stack allocated row buffer. * Note that g_variant_get_child_value() returns a strong ref, * not a floating one */ for (j = 0; j < n_cols; j++) { val = g_variant_get_child_value (row, j); // val is now a 'v' row_buf[j] = g_variant_get_child_value (val, 0); // unbox the 'v' g_variant_unref (val); } if (change_type == CHANGE_TYPE_ADD) { if (pos == 0) dee_model_prepend_row (DEE_MODEL (self), row_buf); else if (pos >= model_n_rows) dee_model_append_row (DEE_MODEL (self), row_buf); else if (pos < model_n_rows) dee_model_insert_row (DEE_MODEL (self), pos, row_buf); } else if (change_type == CHANGE_TYPE_CHANGE) { dee_model_set_row (DEE_MODEL (self), dee_model_get_iter_at_row (DEE_MODEL (self), pos), row_buf); } else { g_critical ("Unknown change type %i from %s. The model may have " "been left in a dirty state", change_type, sender_name); // FIXME: continue looping or bail out? } /* Free the variants in the row_buf. */ for (j = 0; j < n_cols; j++) g_variant_unref (row_buf[j]); g_variant_unref (row); } /* End outer loop */ priv->suppress_remote_signals = FALSE; g_variant_unref (transaction); g_variant_unref (aav); g_variant_unref (au); g_variant_unref (ay); /* We must manually override the seqnum in case we started off from * zero our selves, but the transaction was a later snapshot */ dee_serializable_model_set_seqnum (DEE_MODEL (self), seqnum_after); priv->last_committed_seqnum = seqnum_after; g_signal_emit (self, _signals[END_TRANSACTION], 0, seqnum_before, seqnum_after); g_signal_emit_by_name (self, "changeset-finished"); } static void on_self_row_added (DeeModel *self, DeeModelIter *iter) { DeeSharedModelPrivate *priv; gsize row_slice_size; guint32 pos; GVariant **row; priv = DEE_SHARED_MODEL (self)->priv; if (!priv->suppress_remote_signals) { row_slice_size = dee_model_get_n_columns(self) * sizeof (gpointer); row = g_slice_alloc (row_slice_size); pos = dee_model_get_position (self, iter); enqueue_revision (self, CHANGE_TYPE_ADD, pos, dee_serializable_model_get_seqnum (self), dee_model_get_row (self, iter, row)); } } static void on_self_row_removed (DeeModel *self, DeeModelIter *iter) { DeeSharedModelPrivate *priv; guint32 pos; priv = DEE_SHARED_MODEL (self)->priv; if (!priv->suppress_remote_signals) { pos = dee_model_get_position (self, iter); enqueue_revision (self, CHANGE_TYPE_REMOVE, pos, dee_serializable_model_get_seqnum (self), NULL); } } static void on_self_row_changed (DeeModel *self, DeeModelIter *iter) { DeeSharedModelPrivate *priv; guint32 pos; gsize row_slice_size; GVariant **row; priv = DEE_SHARED_MODEL (self)->priv; if (!priv->suppress_remote_signals) { row_slice_size = dee_model_get_n_columns(self) * sizeof (gpointer); row = g_slice_alloc (row_slice_size); pos = dee_model_get_position (self, iter); enqueue_revision (self, CHANGE_TYPE_CHANGE, pos, dee_serializable_model_get_seqnum (self), dee_model_get_row (self, iter, row)); } } /* Clears all data in the model and resets it to start from scratch */ static void reset_model (DeeModel *self) { g_return_if_fail (DEE_IS_SHARED_MODEL (self)); /* Make sure we don't have any buffered signals awaiting emission */ flush_revision_queue (self); /* Emit 'removed' on all rows and free old row data */ dee_model_clear (self); dee_serializable_model_set_seqnum (self, 0); } /* Call DBus method com.canonical.Dee.Model.Invalidate() on @sender_name */ static void invalidate_peer (DeeSharedModel *self, const gchar *sender_name, GDBusConnection *except) { DeeSharedModelPrivate *priv; GSList *iter; g_return_if_fail (DEE_IS_SHARED_MODEL (self)); if (!dee_shared_model_is_leader (self)) { g_critical ("Internal error in DeeSharedModel. " "Non-leader model tried to invalidate a peer"); return; } priv = self->priv; // invalidate peers on all connections for (iter = priv->connections; iter != NULL; iter = iter->next) { if (iter->data == except) continue; g_dbus_connection_call ((GDBusConnection*) iter->data, sender_name, priv->model_path, "com.canonical.Dee.Model", "Invalidate", NULL, /* params */ NULL, /* reply type */ G_DBUS_CALL_FLAGS_NONE, -1, /* timeout */ NULL, /* cancel */ NULL, /* cb */ NULL); /* user data */ } } /* Public Methods */ GType dee_shared_model_access_mode_get_type (void) { static GType shared_model_access_mode_type = 0; if (shared_model_access_mode_type == 0) { static const GEnumValue values[] = { { DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE, "DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE", "world-writable" }, { DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE, "DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE", "leader-writable" }, { 0, NULL, NULL } }; shared_model_access_mode_type = g_enum_register_static ("DeeSharedModelAccessMode", values); } return shared_model_access_mode_type; } GType dee_shared_model_flush_mode_get_type (void) { static GType shared_model_flush_mode_type = 0; if (shared_model_flush_mode_type == 0) { static const GEnumValue values[] = { { DEE_SHARED_MODEL_FLUSH_MODE_AUTOMATIC, "DEE_SHARED_MODEL_FLUSH_MODE_AUTOMATIC", "automatic" }, { DEE_SHARED_MODEL_FLUSH_MODE_MANUAL, "DEE_SHARED_MODEL_FLUSH_MODE_MANUAL", "manual" }, { 0, NULL, NULL } }; shared_model_flush_mode_type = g_enum_register_static ("DeeSharedModelFlushMode", values); } return shared_model_flush_mode_type; } /** * dee_shared_model_new: * @name: A well known name to publish this model under. Models sharing this name * will synchronize with each other * * Create a new empty shared model without any column schema associated. * The column schema will be set in one of two ways: firstly you may set it * manually with dee_model_set_schema() or secondly it will be set once * the first rows are exchanged with a peer model. * * A #DeeSharedModel with a schema manually set has to be created before * creating more #DeeSharedModel with the same @name. * * A shared model created with this constructor will store row data in a * suitably picked memory backed model. * * Return value: (transfer full) (type DeeSharedModel): a new #DeeSharedModel */ DeeModel* dee_shared_model_new (const gchar *name) { DeeModel *self; g_return_val_if_fail (name != NULL, NULL); self = dee_shared_model_new_with_back_end(name, dee_sequence_model_new ()); return self; } /** * dee_shared_model_new_for_peer: * @peer: (transfer full): A #DeePeer instance. * * Create a new empty shared model without any column schema associated. * The column schema will be set in one of two ways: firstly you may set it * manually with dee_model_set_schema() or secondly it will be set once * the first rows are exchanged with a peer model. * * A #DeeSharedModel with a schema manually set has to be created before * creating more #DeeSharedModel with the same @name. * * A shared model created with this constructor will store row data in a * suitably picked memory backed model. * * Return value: (transfer full) (type DeeSharedModel): a new #DeeSharedModel */ DeeModel* dee_shared_model_new_for_peer (DeePeer *peer) { DeeModel *self; DeeModel *back_end; g_return_val_if_fail (peer != NULL, NULL); back_end = (DeeModel*) dee_sequence_model_new (); self = g_object_new (DEE_TYPE_SHARED_MODEL, "back-end", back_end, "peer", peer, NULL); g_object_unref (back_end); g_object_unref (peer); return self; } /** * dee_shared_model_new_with_back_end: * @name: (transfer none): A well known name to publish this model under. * Models sharing this name will synchronize with each other * @back_end: (transfer full): The #DeeModel that will actually store * the model data. Ownership of the ref to @back_end is transfered to * the shared model. * * Create a new shared model storing all data in @back_end. * * The model will start synchronizing with peer models as soon as possible and * the #DeeSharedModel:synchronized property will be set once finished. * * Return value: (transfer full) (type DeeSharedModel): a new #DeeSharedModel */ DeeModel* dee_shared_model_new_with_back_end (const gchar *name, DeeModel *back_end) { DeeModel *self; DeePeer *swarm; g_return_val_if_fail (name != NULL, NULL); swarm = g_object_new (DEE_TYPE_PEER, "swarm-name", name, NULL); self = g_object_new (DEE_TYPE_SHARED_MODEL, "back-end", back_end, "peer", swarm, NULL); g_object_unref (back_end); g_object_unref (swarm); return self; } /** * dee_shared_model_get_swarm_name: * @self: The model to get the name for * * Convenience function for accessing the #DeePeer:swarm-name property of the * #DeePeer defined in the #DeeSharedModel:peer property. * * Returns: The name of the swarm this model synchrnonizes with */ const gchar* dee_shared_model_get_swarm_name (DeeSharedModel *self) { DeeSharedModelPrivate *priv; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), NULL); priv = self->priv; return dee_peer_get_swarm_name (priv->swarm); } /** * dee_shared_model_get_peer: * @self: The model to get the #DeePeer for * * Convenience function for accessing the #DeeSharedModel:peer property * * Returns: (transfer none): The #DeePeer used to interact with the peer models */ DeePeer* dee_shared_model_get_peer (DeeSharedModel *self) { DeeSharedModelPrivate *priv; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), NULL); priv = self->priv; return priv->swarm; } /** * dee_shared_model_get_flush_mode: * @self: A #DeeSharedModel * * Convenience function for accessing the #DeeSharedModel:flush-mode property. * * Returns: (transfer none): The #DeeSharedModelFlushMode used by the model */ DeeSharedModelFlushMode dee_shared_model_get_flush_mode (DeeSharedModel *self) { g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), DEE_SHARED_MODEL_FLUSH_MODE_MANUAL); return self->priv->flush_mode; } /** * dee_shared_model_set_flush_mode: * @self: A #DeeSharedModel * @mode: Flush mode to use * * Convenience function for setting the #DeeSharedModel:flush-mode property. */ void dee_shared_model_set_flush_mode (DeeSharedModel *self, DeeSharedModelFlushMode mode) { g_return_if_fail (DEE_IS_SHARED_MODEL (self)); g_object_set (self, "flush-mode", mode, NULL); } /** * dee_shared_model_is_leader: * @self: The model to inspect * * Check if the model is the swarm leader. This is a convenience function for * accessing the #DeeSharedModel:peer property and checking if it's the swarm * leader. * * Returns: The value of dee_peer_is_swarm_leader() for the #DeePeer used by * this shared model */ gboolean dee_shared_model_is_leader (DeeSharedModel *self) { DeeSharedModelPrivate *priv; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), FALSE); priv = self->priv; return dee_peer_is_swarm_leader (priv->swarm); } /** * dee_shared_model_is_synchronized: * @self: The model to inspect * * Check if the model is synchronized with its peers. Before modifying a * shared model in any way (except dee_model_set_schema()) you should wait for * it to become synchronized. This is normally done by waiting for the * "notify::synchronized" signal. * * This method is purely a convenience function for accessing the * #DeeSharedModel:synchronized property. * * Returns: The value of the :synchronized property */ gboolean dee_shared_model_is_synchronized (DeeSharedModel *self) { DeeSharedModelPrivate *priv; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), FALSE); priv = self->priv; return priv->synchronized; } /** * dee_shared_model_flush_revision_queue: * @self: The shared model to flush the revision queue on * * Expert: All changes to @self that has not yet been propagated to the peer * models are send. If you also want to block the mainloop until * all the underlying transport streams have been flushed use * dee_shared_model_flush_revision_queue_sync(). * * Normally #DeeSharedModel collects changes to @self into batches and sends * them automatically to all peers. You can use this call to provide fine * grained control of exactly when changes to @self are synchronized to its * peers. This may for example be useful to improve the interactivity of your * application if you have a model-process which intermix small and light * changes with big and expensive changes. Using this call you can make sure * the model-process dispatches small changes more aggresively to the * view-process, while holding on to the expensive changes a bit longer. * * Return value: The number of revisions flushed. */ guint dee_shared_model_flush_revision_queue (DeeSharedModel *self) { g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), 0); return flush_revision_queue (DEE_MODEL (self)); } /** * dee_shared_model_flush_revision_queue_sync: * @self: The shared model to flush the revision queue on * * Similar to dee_shared_model_flush_revision_queue(), but also blocks * the mainloop until all the underlying transport streams have been flushed. * * Important: This method may flush * your internal queue of DBus messages forcing them to be send before this call * returns. * * Return value: The number of revisions flushed. */ guint dee_shared_model_flush_revision_queue_sync (DeeSharedModel *self) { DeeSharedModelPrivate *priv; GError *error; GSList *iter; guint n_revisions; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), 0); n_revisions = dee_shared_model_flush_revision_queue (self); priv = self->priv; for (iter = priv->connections; iter != NULL; iter = iter->next) { error = NULL; g_dbus_connection_flush_sync ((GDBusConnection*) iter->data, NULL, &error); if (error) { g_critical ("Error when flushing %u revisions of %s@%p: %s", n_revisions, G_OBJECT_TYPE_NAME (self), self, error->message); g_error_free (error); // continue, other connections may be working fine } } return n_revisions; } static void dee_shared_model_clear (DeeModel *model) { DeeSharedModel *self; DeeSharedModelPrivate *priv; DeeModel *backend; gboolean was_suppressing; guint64 seqnum; guint n_rows; self = DEE_SHARED_MODEL (model); priv = self->priv; g_object_get (self, "back-end", &backend, NULL); was_suppressing = priv->suppress_remote_signals; seqnum = dee_serializable_model_get_seqnum (model); n_rows = dee_model_get_n_rows (model); if (!was_suppressing && n_rows > 0) { seqnum += n_rows; enqueue_revision (model, CHANGE_TYPE_CLEAR, 0, seqnum, NULL); } /* make sure we don't enqueue lots of CHANGE_TYPE_REMOVE */ priv->suppress_remote_signals = TRUE; /* Chain up to parent class impl. This handles the seqnums for us and the * backend alike. We just hook in before it, really, to player clever * tricks with the revision queue (inserting a CLEAR and not N*REMOVE) */ ((DeeModelIface*) g_type_interface_peek_parent (DEE_MODEL_GET_IFACE(model)))->clear (model); priv->suppress_remote_signals = was_suppressing; g_object_unref (backend); } /* * Dbus Methods */ /* Build a '(sasaavauay(tt))' suitable for sending in a Clone response */ static GVariant* dee_shared_model_serialize (DeeSerializable *self) { DeeSerializableIface *serializable_model_iface; DeeModel *_self; GVariantBuilder au, ay, clone; GVariant *schema, *aav, *tt, *hints, *serialized_model; guint i, n_rows; guint64 last_seqnum; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), NULL); serializable_model_iface = (DeeSerializableIface*) g_type_interface_peek_parent (DEE_SERIALIZABLE_GET_IFACE (self)); serialized_model = serializable_model_iface->serialize (self); if (g_variant_is_floating (serialized_model)) serialized_model = g_variant_ref_sink (serialized_model); g_return_val_if_fail ( g_strcmp0 (g_variant_get_type_string (serialized_model), "(asaav(tt)a{sv})") == 0, NULL); /* Expecting "(asaav(tt)a{sv})" * ^ schema * ^ row_data * ^ seqnums * ^ hints * * Now we'll transform this into our own schema "(sasaavauay(tt)a{sv})". */ _self = DEE_MODEL (self); n_rows = dee_model_get_n_rows (_self); g_variant_builder_init (&au, G_VARIANT_TYPE ("au")); g_variant_builder_init (&ay, G_VARIANT_TYPE ("ay")); /* Clone the rows */ for (i = 0; i < n_rows; i++) { g_variant_builder_add (&au, "u", i); g_variant_builder_add (&ay, "y", (guchar) CHANGE_TYPE_ADD); } schema = g_variant_get_child_value (serialized_model, 0); aav = g_variant_get_child_value (serialized_model, 1); hints = g_variant_get_child_value (serialized_model, 3); /* Collect the seqnums */ last_seqnum = dee_serializable_model_get_seqnum (_self); tt = g_variant_new ("(tt)", last_seqnum - i, last_seqnum);// FIXME last_committed_seqnum g_variant_builder_init (&clone, CLONE_VARIANT_TYPE); g_variant_builder_add (&clone, "s", dee_shared_model_get_swarm_name (DEE_SHARED_MODEL (self))); g_variant_builder_add_value (&clone, schema); g_variant_builder_add_value (&clone, aav); g_variant_builder_add_value (&clone, g_variant_builder_end (&au)); g_variant_builder_add_value (&clone, g_variant_builder_end (&ay)); g_variant_builder_add_value (&clone, tt); g_variant_builder_add_value (&clone, hints); trace_object (self, "Serialized %u rows. " "Seqnum range %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT, i, last_seqnum - i, last_seqnum); g_variant_unref (schema); g_variant_unref (aav); g_variant_unref (hints); g_variant_unref (serialized_model); return g_variant_builder_end (&clone); } /* Handle an incoming Invalidate() message */ static gboolean on_invalidate (DeeSharedModel *self) { DeeSharedModelPrivate *priv; g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), FALSE); priv = self->priv; if (dee_peer_is_swarm_leader (priv->swarm)) { g_warning ("Refusing to invalidate swarm leader"); return FALSE; } trace_object (self, "Model invalidated"); priv->synchronized = FALSE; priv->suppress_remote_signals = TRUE; reset_model (DEE_MODEL (self)); clone_leader (self); priv->suppress_remote_signals = FALSE; return TRUE; } static void dee_shared_model_parse_vardict_schemas (DeeModel *model, GVariantIter *iter, guint n_cols) { GHashTable **vardict_schemas; gchar *field_name, *field_schema; guint column_index; vardict_schemas = g_alloca (n_cols * sizeof (GHashTable*)); memset (vardict_schemas, 0, n_cols * sizeof (GHashTable*)); while (g_variant_iter_next (iter, "(uss)", &column_index, &field_name, &field_schema)) { if (vardict_schemas[column_index] == NULL) { vardict_schemas[column_index] = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, g_free); } // using g_variant_iter_next, so we own field_name & schema g_hash_table_insert (vardict_schemas[column_index], field_name, field_schema); } for (column_index = 0; column_index < n_cols; column_index++) { if (vardict_schemas[column_index] == NULL) continue; dee_model_register_vardict_schema (model, column_index, vardict_schemas[column_index]); g_hash_table_unref (vardict_schemas[column_index]); } } static GObject* dee_shared_model_parse_serialized (GVariant *data) { DeeModel *self; GVariant *transaction, *vardict; GVariantIter *vardict_schema_iter; const gchar **column_names; gchar *swarm_name; guint i, n_cols; gsize tuple_items; g_return_val_if_fail (data != NULL, NULL); // FIXME: this method doesn't consider DeePeer subclasses and naively uses // dee_shared_model_new() which in turn uses dee_peer_new(), if the model // was created with a DeeServer / DeeClient, it won't be possible to // deserialize the model into the original state tuple_items = g_variant_n_children (data); if (tuple_items == COMMIT_TUPLE_ITEMS) /* "(sasaavauay(tt))" */ { transaction = g_variant_ref (data); vardict = NULL; } else if (tuple_items == CLONE_TUPLE_ITEMS) /* "(sasaavauay(tt)a{sv})" */ { GVariant *transaction_members[COMMIT_TUPLE_ITEMS]; guint n_elements; n_elements = G_N_ELEMENTS (transaction_members); for (i = 0; i < n_elements; i++) transaction_members[i] = g_variant_get_child_value (data, i); transaction = g_variant_new_tuple (transaction_members, n_elements); transaction = g_variant_ref_sink (transaction); vardict = g_variant_get_child_value (data, 6); if (!g_variant_lookup (vardict, "column-names", "^a&s", &column_names)) column_names = NULL; if (!g_variant_lookup (vardict, "fields", "a(uss)", &vardict_schema_iter)) vardict_schema_iter = NULL; for (i = 0; i < n_elements; i++) g_variant_unref (transaction_members[i]); } else { g_critical ("Unable to deserialize model: Unrecognized schema"); return NULL; } g_variant_get_child (transaction, 0, "&s", &swarm_name); self = dee_shared_model_new (swarm_name); commit_transaction (DEE_SHARED_MODEL (self), swarm_name, transaction); if (vardict) { if (column_names) { n_cols = g_strv_length ((gchar**) column_names); if (n_cols > 0) dee_model_set_column_names_full (self, column_names, n_cols); } if (vardict_schema_iter != NULL) { dee_shared_model_parse_vardict_schemas (self, vardict_schema_iter, n_cols); g_variant_iter_free (vardict_schema_iter); } g_free (column_names); g_variant_unref (vardict); } g_variant_unref (transaction); return (GObject *) self; } static void dee_shared_model_serializable_iface_init (DeeSerializableIface *iface) { iface->serialize = dee_shared_model_serialize; dee_serializable_register_parser (DEE_TYPE_SHARED_MODEL, COMMIT_VARIANT_TYPE, dee_shared_model_parse_serialized); dee_serializable_register_parser (DEE_TYPE_SHARED_MODEL, CLONE_VARIANT_TYPE, dee_shared_model_parse_serialized); } static void dee_shared_model_model_iface_init (DeeModelIface *iface) { DeeModelIface *proxy_model_iface; proxy_model_iface = (DeeModelIface*) g_type_interface_peek_parent (iface); /* we just need to override clear, but gobject is making this difficult */ iface->set_schema_full = proxy_model_iface->set_schema_full; iface->get_schema = proxy_model_iface->get_schema; iface->get_column_schema = proxy_model_iface->get_column_schema; iface->get_n_columns = proxy_model_iface->get_n_columns; iface->get_n_rows = proxy_model_iface->get_n_rows; iface->prepend_row = proxy_model_iface->prepend_row; iface->append_row = proxy_model_iface->append_row; iface->insert_row = proxy_model_iface->insert_row; iface->insert_row_before = proxy_model_iface->insert_row_before; iface->remove = proxy_model_iface->remove; iface->set_value = proxy_model_iface->set_value; iface->set_row = proxy_model_iface->set_row; iface->get_value = proxy_model_iface->get_value; iface->get_first_iter = proxy_model_iface->get_first_iter; iface->get_last_iter = proxy_model_iface->get_last_iter; iface->get_iter_at_row = proxy_model_iface->get_iter_at_row; iface->get_bool = proxy_model_iface->get_bool; iface->get_uchar = proxy_model_iface->get_uchar; iface->get_int32 = proxy_model_iface->get_int32; iface->get_uint32 = proxy_model_iface->get_uint32; iface->get_int64 = proxy_model_iface->get_int64; iface->get_uint64 = proxy_model_iface->get_uint64; iface->get_double = proxy_model_iface->get_double; iface->get_string = proxy_model_iface->get_string; iface->next = proxy_model_iface->next; iface->prev = proxy_model_iface->prev; iface->is_first = proxy_model_iface->is_first; iface->is_last = proxy_model_iface->is_last; iface->get_position = proxy_model_iface->get_position; iface->register_tag = proxy_model_iface->register_tag; iface->get_tag = proxy_model_iface->get_tag; iface->set_tag = proxy_model_iface->set_tag; iface->clear = dee_shared_model_clear; } dee-1.2.7+15.04.20150304/src/dee.h0000644000015300001610000000305212475676210016314 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ /** * SECTION:dee * @short_description: A single-include header * @include: dee.h * */ #ifndef _HAVE_DEE_H_ #define _HAVE_DEE_H_ #define _DEE_H_INSIDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef _DEE_H_INSIDE #endif /* _HAVE_DEE_H_ */ dee-1.2.7+15.04.20150304/Makefile.am.coverage0000644000015300001610000000246212475676210020451 0ustar pbuserpbgroup00000000000000 # Coverage targets .PHONY: clean-gcno clean-gcda \ coverage-html generate-coverage-html clean-coverage-html \ coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr clean-local: clean-gcno clean-coverage-html clean-coverage-gcovr if HAVE_GCOV clean-gcno: @echo Removing old coverage instrumentation -find -name '*.gcno' -print | xargs -r rm clean-gcda: @echo Removing old coverage results -find -name '*.gcda' -print | xargs -r rm coverage-html: -$(MAKE) $(AM_MAKEFLAGS) -k check $(MAKE) $(AM_MAKEFLAGS) generate-coverage-html generate-coverage-html: @echo Collecting coverage data $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info clean-coverage-html: clean-gcda -$(LCOV) --directory $(top_builddir) -z -rm -rf coverage.info coveragereport if HAVE_GCOVR coverage-gcovr: -$(MAKE) $(AM_MAKEFLAGS) -k check $(MAKE) $(AM_MAKEFLAGS) generate-coverage-gcovr generate-coverage-gcovr: @echo Generating coverage GCOVR report $(GCOVR) --xml -r $(top_builddir) -o $(top_builddir)/coverage.xml clean-coverage-gcovr: clean-gcda -rm -rf $(top_builddir)/coverage.xml endif # HAVE_GCOVR endif # HAVE_GCOV dee-1.2.7+15.04.20150304/AUTHORS0000644000015300001610000000015012475676210015663 0ustar pbuserpbgroup00000000000000Mikkel Kamstrup Erlandsen Neil Jagdish Patel dee-1.2.7+15.04.20150304/configure.ac0000644000015300001610000002002012475676210017077 0ustar pbuserpbgroup00000000000000AC_PREREQ(2.65) # For releases bump this version here, # but also remember to bump the lib version as instructed below # Don't forget to check also GIR_VERSION m4_define([dee_major], [1]) m4_define([dee_minor], [2]) m4_define([dee_micro], [7]) m4_define([dee_api], [dee_major.dee_minor]) m4_define([dee_version], [dee_major.dee_minor.dee_micro]) AC_INIT([dee],[dee_version],[https://bugs.launchpad.net/dee]) AC_CONFIG_SRCDIR([src/dee.h]) AC_CONFIG_MACRO_DIR([build/autotools]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([1.9]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_PATH_PYTHON ############################################### ### pygobject overrides directory detection ### ############################################### AC_ARG_WITH([pygi_overrides_dir], AC_HELP_STRING([--with-pygi-overrides-dir], [Path to pygobject overrides directory])) AC_MSG_CHECKING(for pygobject overrides directory) if test "x$with_pygi_overrides_dir" = "x" ; then overrides_dir="`$PYTHON -c 'import gi; print(gi._overridesdir)' 2>/dev/null`" # fallback if the previous failed if test "x$overrides_dir" = "x" ; then overrides_dir="${pyexecdir}/gi/overrides" fi else overrides_dir="$with_pygi_overrides_dir" fi PYGI_OVERRIDES_DIR="$overrides_dir" AC_SUBST(PYGI_OVERRIDES_DIR) AC_MSG_RESULT($PYGI_OVERRIDES_DIR) DEE_MAJOR_VERSION=dee_major DEE_MINOR_VERSION=dee_minor DEE_MICRO_VERSION=dee_micro DEE_VERSION=dee_version AC_SUBST(DEE_MAJOR_VERSION) AC_SUBST(DEE_MINOR_VERSION) AC_SUBST(DEE_MICRO_VERSION) AC_SUBST(DEE_VERSION) m4_define([gir_version], [dee_major.0]) GIR_VERSION=gir_version AC_SUBST(GIR_VERSION) # Before making a release, the DEE_LT_VERSION string should be updated. # The string is of the form C:R:A. # - If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A DEE_LT_CURRENT=6 DEE_LT_REV=1 DEE_LT_AGE=2 DEE_LT_VERSION="$DEE_LT_CURRENT:$DEE_LT_REV:$DEE_LT_AGE" DEE_LT_LDFLAGS="-version-info $DEE_LT_VERSION -export-symbols-regex '^dee_.*'" AC_SUBST(DEE_LT_VERSION) AC_SUBST(DEE_LT_LDFLAGS) dnl =========================================================================== # Checks for programs AC_PROG_CC AM_PROG_CC_C_O AC_DISABLE_STATIC LT_INIT # Checks for header files AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h]) # Checks for typedefs, structures and compiler charecteristics AC_C_CONST # Checks for library functions AC_FUNC_MALLOC AC_FUNC_MMAP AC_CHECK_FUNCS([memset munmap strcasecmp strdup]) PKG_CHECK_MODULES(DEE, glib-2.0 >= 2.32 gthread-2.0 >= 2.32 gobject-2.0 >= 2.32 gio-2.0 >= 2.32 gio-unix-2.0 >= 2.32 ) AC_SUBST(DEE_CFLAGS) AC_SUBST(DEE_LIBS) dnl =========================================================================== if test "x$GCC" = "xyes"; then GCC_FLAGS="-g -Wall" fi AC_SUBST(GCC_FLAGS) dnl = use strict compiler flags only on development releases ================== m4_define([maintainer_flags_default], [m4_if(m4_eval(dee_micro % 2), [1], [yes], [no])]) AC_ARG_ENABLE([maintainer-flags], [AS_HELP_STRING([--enable-maintainer-flags=@<:@no/yes@:>@],[Use strict compiler flags @<:@default=no@:>@])], [], [enable_maintainer_flags=maintainer_flags_default]) MAINTAINER_CFLAGS="" AS_IF([test "x$enable_maintainer_flags" = "xyes" && test "x$GCC" = "xyes"], [ MAINTAINER_CFLAGS="-Werror -Wall -Wcast-align -Wno-uninitialized -Wempty-body -Wformat-security -Winit-self -Wno-error=deprecated-declarations" ] ) AC_SUBST(MAINTAINER_CFLAGS) dnl = GObject Introspection Check ============================================= GOBJECT_INTROSPECTION_CHECK([0.10.2]) dnl = GTK Doc Check =========================================================== GTK_DOC_CHECK([1.8]) dnl = Check if build tests ==================================================== AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests=@<:@no/yes@:>@],[build tests suite @<:@default=yes@:>@]),, [enable_tests=yes]) AM_CONDITIONAL([WANT_TESTS], [test "x$enable_tests" != "xno"]) ########################### # gcov coverage reporting ########################### AC_TDD_GCOV AM_CONDITIONAL([HAVE_GCOV], [test "x$ac_cv_check_gcov" = xyes]) AM_CONDITIONAL([HAVE_LCOV], [test "x$ac_cv_check_lcov" = xyes]) AM_CONDITIONAL([HAVE_GCOVR], [test "x$ac_cv_check_gcovr" = xyes]) AC_SUBST(COVERAGE_CFLAGS) AC_SUBST(COVERAGE_LDFLAGS) dnl = Check for GLib Test Extensions (GTX) ==================================== AC_ARG_ENABLE([extended-tests], AS_HELP_STRING([--enable-extended-tests=@<:@no/yes@:>@],[build extended test suite (requires libgtx from lp:gtx) @<:@default=no@:>@]),, [enable_extended_tests=no]) if test "x$enable_extended_tests" = "xyes"; then AM_COND_IF([WANT_TESTS],,[ AC_MSG_ERROR([extended tests require tests support, please --enable-tests]) ]) AC_DEFINE(HAVE_GTX, 1, [Define to 1 if we have GLib Test Extensions (libgtx from lp:gtx)]) PKG_CHECK_MODULES(GTX, [ gtx >= 0.2.2 ]) AC_SUBST(GTX_CFLAGS) AC_SUBST(GTX_LIBS) fi AM_CONDITIONAL(HAVE_GTX, test "$enable_extended_tests" = "yes") dnl = Check if we should compile with trace logging =========================== AC_ARG_ENABLE([trace-log], AS_HELP_STRING([--enable-trace-log=@<:@no/yes@:>@],[build with very verbose logging @<:@default=no@:>@]),, [enable_trace_log=no]) if test "x$enable_trace_log" = "xyes"; then AC_DEFINE(ENABLE_TRACE_LOG, 1, [build with vert verbose logging]) fi AM_CONDITIONAL(ENABLE_TRACE_LOG, test "$enable_trace_log" = "yes") dnl =========================================================================== AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums) AC_SUBST(GLIB_MKENUMS) AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal) AC_SUBST(GLIB_GENMARSHAL) dnl =========================================================================== AC_PATH_PROG([VALA_API_GEN], [vapigen]) AM_CONDITIONAL([HAVE_VAPIGEN], [test "x$VALA_API_GEN" != "x"]) dnl = Check for ICU ==================================== AC_ARG_ENABLE([icu], AS_HELP_STRING([--enable-icu=@<:@no/yes@:>@],[build with advanced unicode text handling (requires ICU >= 4.6) @<:@default=yes@:>@]),, [enable_icu=yes]) if test "x$enable_icu" = "xyes"; then AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[ #include "unicode/uvernum.h" #if U_ICU_VERSION_MAJOR_NUM < 4 #error Dee ICU requires at least ICU v4.6 #elif U_ICU_VERSION_MAJOR_NUM == 4 && U_ICU_VERSION_MINOR_NUM < 6 #error Dee ICU requires at least ICU v4.6 #endif]], [[]])], [icu_available=yes], [icu_available=no]) if test "x$icu_available" = "xyes"; then AC_DEFINE(HAVE_ICU, 1, [Define to 1 if we have ICU]) ICU_CFLAGS="$(icu-config --cflags)" ICU_LIBS="$(icu-config --ldflags-libsonly)" AC_SUBST(ICU_CFLAGS) AC_SUBST(ICU_LIBS) AC_OUTPUT([dee-icu-1.0.pc]) else AC_MSG_ERROR([Dee ICU support requires ICU >= 4.6]) fi fi AM_CONDITIONAL(HAVE_ICU, test "x$enable_icu" = "xyes") dnl =========================================================================== AC_OUTPUT([ Makefile build/Makefile build/autotools/Makefile src/Makefile bindings/Makefile bindings/python/Makefile doc/Makefile doc/reference/Makefile doc/reference/dee-1.0/Makefile dee-1.0.pc tests/Makefile tools/Makefile examples/Makefile vapi/Makefile ]) dnl Output the results AC_MSG_NOTICE([ dee $VERSION ---------------- Prefix : ${prefix} ICU support : ${enable_icu} Documentation : ${enable_gtk_doc} Introspection : ${enable_introspection} PyGi Overrides : ${overrides_dir} Tests : ${enable_tests} Extended Tests : ${enable_extended_tests} Coverage : ${use_gcov} Verbose logging: ${enable_trace_log} Extra CFlags : ${CPPFLAGS} $MAINTAINER_CFLAGS Extra ValaFlags: ${CPPFLAGS} $MAINTAINER_VALAFLAGS ]) dee-1.2.7+15.04.20150304/COPYING0000644000015300001610000001672512475676210015665 0ustar pbuserpbgroup00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. dee-1.2.7+15.04.20150304/gtk-doc.make0000644000015300001610000002061312475676210017010 0ustar pbuserpbgroup00000000000000# -*- mode: makefile -*- #################################### # Everything below here is generic # #################################### if GTK_DOC_USE_LIBTOOL GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) GTKDOC_RUN = $(LIBTOOL) --mode=execute else GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) GTKDOC_RUN = endif # We set GPATH here; this gives us semantics for GNU make # which are more like other make's VPATH, when it comes to # whether a source that is a target of one rule is then # searched for in VPATH/GPATH. # GPATH = $(srcdir) TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) SETUP_FILES = \ $(content_files) \ $(DOC_MAIN_SGML_FILE) \ $(DOC_MODULE)-sections.txt \ $(DOC_MODULE)-overrides.txt EXTRA_DIST = \ $(HTML_IMAGES) \ $(SETUP_FILES) DOC_STAMPS=setup-build.stamp scan-build.stamp tmpl-build.stamp sgml-build.stamp \ html-build.stamp pdf-build.stamp \ tmpl.stamp sgml.stamp html.stamp pdf.stamp SCANOBJ_FILES = \ $(DOC_MODULE).args \ $(DOC_MODULE).hierarchy \ $(DOC_MODULE).interfaces \ $(DOC_MODULE).prerequisites \ $(DOC_MODULE).signals REPORT_FILES = \ $(DOC_MODULE)-undocumented.txt \ $(DOC_MODULE)-undeclared.txt \ $(DOC_MODULE)-unused.txt CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) if ENABLE_GTK_DOC if GTK_DOC_BUILD_HTML HTML_BUILD_STAMP=html-build.stamp else HTML_BUILD_STAMP= endif if GTK_DOC_BUILD_PDF PDF_BUILD_STAMP=pdf-build.stamp else PDF_BUILD_STAMP= endif all-local: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) else all-local: endif docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) $(REPORT_FILES): sgml-build.stamp #### setup #### setup-build.stamp: -@if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ echo ' DOC Preparing build'; \ files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ if test "x$$files" != "x" ; then \ for file in $$files ; do \ test -f $(abs_srcdir)/$$file && \ cp -pu $(abs_srcdir)/$$file $(abs_builddir)/ || true; \ done; \ fi; \ test -d $(abs_srcdir)/tmpl && \ { cp -rp $(abs_srcdir)/tmpl $(abs_builddir)/; \ chmod -R u+w $(abs_builddir)/tmpl; } \ fi @touch setup-build.stamp #### scan #### scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) @echo ' DOC Scanning header files' @_source_dir='' ; \ for i in $(DOC_SOURCE_DIR) ; do \ _source_dir="$${_source_dir} --source-dir=$$i" ; \ done ; \ gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES) @if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ echo " DOC Introspecting gobjects"; \ scanobj_options=""; \ gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ scanobj_options="--verbose"; \ fi; \ fi; \ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \ else \ for i in $(SCANOBJ_FILES) ; do \ test -f $$i || touch $$i ; \ done \ fi @touch scan-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp @true #### templates #### tmpl-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt @echo ' DOC Rebuilding template files' @gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS) @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ if test -w $(abs_srcdir) ; then \ cp -rp $(abs_builddir)/tmpl $(abs_srcdir)/; \ fi \ fi @touch tmpl-build.stamp tmpl.stamp: tmpl-build.stamp @true $(srcdir)/tmpl/*.sgml: @true #### xml #### sgml-build.stamp: tmpl.stamp $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files) @echo ' DOC Building XML' @-chmod -R u+w $(srcdir) @_source_dir='' ; \ for i in $(DOC_SOURCE_DIR) ; do \ _source_dir="$${_source_dir} --source-dir=$$i" ; \ done ; \ gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS) @touch sgml-build.stamp sgml.stamp: sgml-build.stamp @true #### html #### html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) @echo ' DOC Building HTML' @rm -rf html @mkdir html @mkhtml_options=""; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkhtml_options="$$mkhtml_options --verbose"; \ fi; \ fi; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ if test "$(?)" = "0"; then \ mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ fi; \ cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) -@test "x$(HTML_IMAGES)" = "x" || \ for file in $(HTML_IMAGES) ; do \ if test -f $(abs_srcdir)/$$file ; then \ cp $(abs_srcdir)/$$file $(abs_builddir)/html; \ fi; \ if test -f $(abs_builddir)/$$file ; then \ cp $(abs_builddir)/$$file $(abs_builddir)/html; \ fi; \ done; @echo ' DOC Fixing cross-references' @gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) @touch html-build.stamp #### pdf #### pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) @echo ' DOC Building PDF' @rm -f $(DOC_MODULE).pdf @mkpdf_options=""; \ gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkpdf_options="$$mkpdf_options --verbose"; \ fi; \ fi; \ if test "x$(HTML_IMAGES)" != "x"; then \ for img in $(HTML_IMAGES); do \ part=`dirname $$img`; \ echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \ if test $$? != 0; then \ mkpdf_options="$$mkpdf_options --imgdir=$$part"; \ fi; \ done; \ fi; \ gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS) @touch pdf-build.stamp ############## clean-local: @rm -f *~ *.bak @rm -rf .libs distclean-local: @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ rm -rf tmpl; \ fi maintainer-clean-local: clean @rm -rf xml html install-data-local: @installfiles=`echo $(builddir)/html/*`; \ if test "$$installfiles" = '$(builddir)/html/*'; \ then echo 1>&2 'Nothing to install' ; \ else \ if test -n "$(DOC_MODULE_VERSION)"; then \ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ else \ installdir="$(DESTDIR)$(TARGET_DIR)"; \ fi; \ $(mkinstalldirs) $${installdir} ; \ for i in $$installfiles; do \ echo ' $(INSTALL_DATA) '$$i ; \ $(INSTALL_DATA) $$i $${installdir}; \ done; \ if test -n "$(DOC_MODULE_VERSION)"; then \ mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ fi; \ $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \ fi uninstall-local: @if test -n "$(DOC_MODULE_VERSION)"; then \ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ else \ installdir="$(DESTDIR)$(TARGET_DIR)"; \ fi; \ rm -rf $${installdir} # # Require gtk-doc when making dist # if ENABLE_GTK_DOC dist-check-gtkdoc: else dist-check-gtkdoc: @echo "*** gtk-doc must be installed and enabled in order to make dist" @false endif dist-hook: dist-check-gtkdoc dist-hook-local @mkdir $(distdir)/tmpl @mkdir $(distdir)/html @-cp ./tmpl/*.sgml $(distdir)/tmpl @cp ./html/* $(distdir)/html @-cp ./$(DOC_MODULE).pdf $(distdir)/ @-cp ./$(DOC_MODULE).types $(distdir)/ @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/ @cd $(distdir) && rm -f $(DISTCLEANFILES) @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html .PHONY : dist-hook-local docs dee-1.2.7+15.04.20150304/examples/0000755000015300001610000000000012475676370016444 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/examples/master-model.c0000644000015300001610000000340712475676210021176 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include #include static gboolean add_ (DeeModel *model) { dee_model_append (model, 10, "Rooney"); return TRUE; } static void on_row_added (DeeModel *self, DeeModelIter *iter) { gint i = 0; gchar *s = NULL; dee_model_get (self, iter, &i, &s); if (!g_str_equal (s, "Rooney")) g_debug ("Master: Row Added: %d %s", i, s); } gint main (gint argc, gchar *argv[]) { GMainLoop *loop; DeeModel *model; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif #if !GLIB_CHECK_VERSION(2, 32, 0) g_thread_init (NULL); #endif model = dee_shared_model_new ("com.canonical.Dee.Model.Example"); dee_model_set_schema (model, "i", "s", NULL); g_assert (DEE_IS_MODEL (model)); g_signal_connect (model, "row-added", G_CALLBACK (on_row_added), NULL); g_timeout_add_seconds (2, (GSourceFunc)add_, model); loop = g_main_loop_new (g_main_context_default (), TRUE); g_main_loop_run (loop); return 0; } dee-1.2.7+15.04.20150304/examples/slave-model.py0000755000015300001610000000255012475676210021224 0ustar pbuserpbgroup00000000000000#!/usr/bin/python # # Copyright (C) 2010 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as # published by the Free Software Foundation. # # 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, see . # # Authored by # Ken VanDine # from gi.repository import Dee from gi.repository import GObject class Slave: def __init__(self): model_name = "com.canonical.Dee.Model.Example" print ("Joining model %s" % model_name) self.model = Dee.SharedModel.new(model_name) self.model.connect("row-added", self.on_row_added, None) def print_row (self, model, iter): while i < self.model.get_n_columns (): s = str(self.model.get_value (model, iter, i)) if (i == 0): print "ADDED: %s" % s else: print ", %s" % s i = i + 1 def on_row_added(self, model, iter): print_row(model, iter) if __name__ == "__main__": s = Slave () GObject.MainLoop().run() dee-1.2.7+15.04.20150304/examples/master-model.py0000755000015300001610000000265512475676210021413 0ustar pbuserpbgroup00000000000000#!/usr/bin/python # # Copyright (C) 2010 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as # published by the Free Software Foundation. # # 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, see . # # Authored by # Ken VanDine # from gi.repository import Dee from gi.repository import GObject class Master: def __init__(self): self.model = Dee.SharedModel.new("com.canonical.Dee.Model.Example") self.model.set_schema ("i", "s") self.model.connect("row-added", self.on_row_added) GObject.timeout_add_seconds(1, self.add) def on_row_added (self, model, itr): print "SIG", self, model, itr i = self.model.get_int32 (itr, 0) s = self.model.get_string (itr, 1) print "Master:", i, s def add(self): itr = self.model.append(10, "Rooney") print "ADDED", itr print "GET(i)", self.model.get_int32 (itr, 0), self.model.get_string (itr, 1) return True if __name__ == "__main__": master = Master () GObject.MainLoop().run() dee-1.2.7+15.04.20150304/examples/Makefile.am0000644000015300001610000000154712475676210020500 0ustar pbuserpbgroup00000000000000noinst_PROGRAMS = \ master-model \ peers \ slave-model # synced-lists AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_srcdir)/src \ $(GCC_FLAGS) \ $(DEE_CFLAGS) \ $(MAINTAINER_CFLAGS) master_model_SOURCES = master-model.c master_model_LDADD = $(top_builddir)/src/libdee-1.0.la $(DEE_LIBS) peers_SOURCES = peers.c peers_LDADD = $(top_builddir)/src/libdee-1.0.la $(DEE_LIBS) slave_model_SOURCES = slave-model.c slave_model_LDADD = $(top_builddir)/src/libdee-1.0.la $(DEE_LIBS) ## Comment in to compile synced-lists againt uninstalled dev libs #synced_lists_SOURCES = synced-lists.c #synced_lists_LDADD = $(top_builddir)/src/libdee-1.0.la #synced_lists_LDFLAGS = `pkg-config --libs gtk+-2.0` #synced_lists_CFLAGS = `pkg-config --cflags gtk+-2.0` EXTRA_DIST = \ list-peers \ master-model.vala \ master-model.py \ pythontricks.py \ slave-model.py dee-1.2.7+15.04.20150304/examples/peers.c0000644000015300001610000000400512475676210017716 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include #include #include static void on_swarm_leader_changed (GObject *peer_o, GParamSpec *pspec, gpointer user_data) { gchar *swarm_leader; g_object_get (peer_o, "swarm-leader", &swarm_leader, NULL); g_printf ("Swarm leader changed: %s\n", swarm_leader); g_free (swarm_leader); } static void on_peer_found (DeePeer *self, const gchar* peer_name) { g_printf ("Peer found: %s\n", peer_name); } static void on_peer_lost (DeePeer *self, const gchar* peer_name) { g_printf ("Peer lost: %s\n", peer_name); } gint main (gint argc, gchar *argv[]) { GMainLoop *loop; DeePeer *peer; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif #if !GLIB_CHECK_VERSION(2, 32, 0) g_thread_init (NULL); #endif peer = g_object_new (DEE_TYPE_PEER, "swarm-name", "com.canonical.DeePeer.Test", NULL); g_signal_connect (peer, "notify::swarm-leader", G_CALLBACK (on_swarm_leader_changed), NULL); g_signal_connect (peer, "peer-found", G_CALLBACK (on_peer_found), NULL); g_signal_connect (peer, "peer-lost", G_CALLBACK (on_peer_lost), NULL); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); return 0; } dee-1.2.7+15.04.20150304/examples/synced-lists.c0000644000015300001610000001647712475676210021241 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * * Compile with: * * gcc synced-lists.c -o synced-lists `pkg-config --libs --cflags dee-1.0 gtk+-2.0` * */ #include #include #include #include #include #include static DeeModel *model; static GtkWidget *window; static GtkWidget *list; static GtkListStore *store; static void on_row_added (DeeModel *model, DeeModelIter *iter) { gint i = 0; gchar *str = NULL; GtkTreeIter titer; dee_model_get (model, iter, &i, &str); gtk_list_store_append (store, &titer); gtk_list_store_set (store, &titer, 0, g_strdup_printf ("%d", i), 1, str, 2, iter, -1); g_free (str); } static void on_row_removed (DeeModel *model, DeeModelIter *old_iter) { GtkTreeIter iter = { 0}; gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); do { gpointer data = NULL; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 2, &data, -1); if (data == old_iter) { gtk_list_store_remove (store, &iter); break; } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); } static void on_row_changed (DeeModel *model, DeeModelIter *row_iter) { GtkTreeIter iter = { 0 }; gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); do { gpointer data = NULL; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 2, &data, -1); if (data == row_iter) { gint i = 0; gchar *str = NULL; dee_model_get (model, row_iter, &i, &str); gtk_list_store_set (store, &iter, 0, g_strdup_printf ("%d", i), 1, str, -1); break; } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); } static void add_row (GtkWidget *button) { dee_model_append (model, (gint)getpid (), "Wazza"); } static void remove_row (GtkWidget *button) { GtkTreeSelection *sel; GtkTreeIter iter; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); if (gtk_tree_selection_get_selected (sel, NULL, &iter)) { gpointer data = NULL; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 2, &data, -1); dee_model_remove (model, data); } else g_debug ("No selection to delete"); } static void clear_rows (GtkWidget *button) { dee_model_clear (model); } static void on_cell_edited (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer old_data) { GtkTreeIter iter; if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &iter, path)) { gpointer data = NULL; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 2, &data, -1); dee_model_set (model, (DeeModelIter *)data, new_text); } } gint main (gint argc, gchar *argv[]) { GtkWidget *vbox, *hbox, *scroll, *button; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_resize (GTK_WINDOW (window), 300, 600); gtk_container_set_border_width (GTK_CONTAINER (window), 12); vbox = gtk_vbox_new (FALSE, 12); gtk_container_add (GTK_CONTAINER (window), vbox); button = gtk_label_new (g_strdup_printf ("My PID: %d", getpid())); g_object_set (button, "use-markup", TRUE, NULL); gtk_misc_set_alignment (GTK_MISC (button), 0.5, 0.5); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); scroll = gtk_scrolled_window_new (NULL, NULL); gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0); gtk_widget_show (scroll); list = gtk_tree_view_new (); gtk_container_add (GTK_CONTAINER (scroll), list); gtk_widget_show (list); { GtkCellRenderer *cell; GtkTreeViewColumn *col; cell = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("0", cell, "text", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), col); cell = gtk_cell_renderer_text_new (); g_object_set (cell, "editable", TRUE, NULL); g_signal_connect (cell, "edited", G_CALLBACK (on_cell_edited), NULL); col = gtk_tree_view_column_new_with_attributes ("1", cell, "text", 1, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), col); } hbox = gtk_hbox_new (TRUE, 12); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); button = gtk_button_new_from_stock (GTK_STOCK_ADD); gtk_container_add (GTK_CONTAINER (hbox), button); g_signal_connect (button, "clicked", G_CALLBACK (add_row), NULL); button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); gtk_container_add (GTK_CONTAINER (hbox), button); g_signal_connect (button, "clicked", G_CALLBACK (remove_row), NULL); button = gtk_button_new_from_stock (GTK_STOCK_CLEAR); gtk_container_add (GTK_CONTAINER (hbox), button); g_signal_connect (button, "clicked", G_CALLBACK (clear_rows), NULL); gtk_widget_show_all (window); store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (store)); model = dee_shared_model_new ("com.canonical.Dbus.Model.Example"); dee_model_set_schema (model, "i", "s", NULL); g_signal_connect (model, "row-added", G_CALLBACK (on_row_added), NULL); g_signal_connect (model, "row-removed", G_CALLBACK (on_row_removed), NULL); g_signal_connect (model, "row-changed", G_CALLBACK (on_row_changed), NULL); gtk_main (); return 0; } dee-1.2.7+15.04.20150304/examples/slave-model.c0000644000015300001610000000407112475676210021013 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * */ #include #include #include #include #include static void print_row (DeeModel *self, DeeModelIter *iter) { GVariant *value; gchar *s; gint i; for (i = 0; i < dee_model_get_n_columns (self); i++) { value = dee_model_get_value (self, iter, i); s = g_variant_print (value, FALSE); if (i == 0) g_printf ("%s", s); else g_printf (", %s", s); g_variant_unref (value); g_free (s); } } static void on_row_added (DeeModel *self, DeeModelIter *iter) { g_printf ("ADDED: "); print_row (self, iter); g_printf ("\n"); } gint main (gint argc, gchar *argv[]) { GMainLoop *loop; DeeModel *model; const gchar *model_name; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif #if !GLIB_CHECK_VERSION(2, 32, 0) g_thread_init (NULL); #endif if (argc < 2) model_name = "com.canonical.Dee.Model.Example"; else model_name = argv[1]; g_debug ("Joining model '%s'", model_name); model = dee_shared_model_new (model_name); g_assert (DEE_IS_MODEL (model)); g_signal_connect (model, "row-added", G_CALLBACK (on_row_added), NULL); loop = g_main_loop_new (g_main_context_default (), TRUE); g_main_loop_run (loop); return 0; } dee-1.2.7+15.04.20150304/examples/list-peers0000755000015300001610000000057712475676210020463 0ustar pbuserpbgroup00000000000000#! /bin/bash # # Print out all peers in a swarm given a swarm name # if [ -z "$1" ]; then echo "Please provide a swarm name to list. Eg. 'com.canonical.DeePeer.Test'" 1>&2 exit 1 fi SWARM_NAME="$1" SWARM_PATH="/com/canonical/dee/peer/$(echo $SWARM_NAME | sed -e 's@\.@/@g')" dbus-send --dest=$SWARM_NAME --type=method_call --print-reply $SWARM_PATH com.canonical.Dee.Peer.List dee-1.2.7+15.04.20150304/examples/master-model.vala0000644000015300001610000000331012475676210021670 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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, see . * * Authored by * Neil Jagdish Patel * Mikkel Kamstrup Erlandsen * * Compile with: * * valac --vapidir ../vapi --pkg glib-2.0 --pkg gtk+-2.0 --pkg dee-1.0 master-model.vala -o master-model-vala -X -I../dee */ using GLib; using Dee; public class Master { private Model model; public Master () { this.model = new SharedModel ("com.canonical.Dee.Model.Example"); model.set_schema ("i", "s"); this.model.row_added.connect (this.on_row_added); GLib.Timeout.add_seconds (2, (GLib.SourceFunc) this.add); } private void on_row_added (ModelIter iter) { int i; unowned string s; this.model.get (iter, 0, out i, out s); i = this.model.get_int32 (iter, 0); var ss = this.model.get_string (iter, 1); print (@"Master: $i $ss\n"); } private bool add (Model *model) { this.model.append (10, "Rooney"); return true; } } public static int main (string[] args) { Master master; master = new Master (); Gtk.main (); return 0; } dee-1.2.7+15.04.20150304/examples/pythontricks.py0000644000015300001610000000446012475676210021554 0ustar pbuserpbgroup00000000000000#!/usr/bin/python # # Copyright (C) 2010 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as # published by the Free Software Foundation. # # 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, see . # # Authored by # Mikkel Kamstrup Erlandsen # from gi.repository import Dee from gi.repository import GLib m = Dee.SequenceModel.new() m.set_schema("i", "s") m.append(27, "Hello") m.append(68, "world") # Pythonic iteration for row in m: print row # Python len() behaviour print "That was %s rows right there" % len(m) # Pythonic access-by-index print "At position [0][0] we have: %s" % m[0][0] print "At position [0][1] we have: %s" % m[0][1] print "At position [1][0] we have: %s" % m[1][0] print "At position [1][1] we have: %s" % m[1][1] # Pythonic updates by index m[1][1] = "Mars" print "And we've now changed [1][1] to: %s" % m[1][1] # Individual row handling by index or row iter itr = m.get_iter_at_row(1) row1 = m[itr] row2 = m[1] print "Is it true that rows can be compared? %s" % (row1 == row2) # Pythonic list comprehension on the row level print "Values in row 1: %s" % ", ".join (map(str,row1)) # And check this out - assign full rows in one go! # Works with row iters and indexes alike m[1] = 16, "Points for awesome Python integration" print "And now we've done a full row assignment: %s" % m[1] # Persistent storage of models - and Dee.Serializables in general resources = Dee.resource_manager_get_default () resources.store (m, "pythontricks.testmodel") m2 = resources.load ("pythontricks.testmodel") print "Model stored and loaded from disk, and 2nd row still says: %s" % m2[1] # # Model with more advanced schemas # complex_model = Dee.SequenceModel.new() complex_model.set_schema ("i", "a{sv}", "(uss)") complex_model.append (32, { "myproperty" : GLib.Variant("i", 42) }, (52, "hello", "world")) print "A complex model: %s" % complex_model[0] dee-1.2.7+15.04.20150304/COPYING.GPL0000644000015300001610000010437412475676210016304 0ustar pbuserpbgroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . dee-1.2.7+15.04.20150304/NEWS0000644000015300001610000000000012475676210015304 0ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/INSTALL0000644000015300001610000003633212475676210015657 0ustar pbuserpbgroup00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. dee-1.2.7+15.04.20150304/tools/0000755000015300001610000000000012475676370015766 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/tools/Makefile.am0000644000015300001610000000035412475676210020015 0ustar pbuserpbgroup00000000000000bin_PROGRAMS = \ dee-tool AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_srcdir)/src \ $(GCC_FLAGS) \ $(DEE_CFLAGS) \ $(MAINTAINER_CFLAGS) dee_tool_SOURCES = dee-tool.c dee_tool_LDADD = $(top_builddir)/src/libdee-1.0.la $(DEE_LIBS) dee-1.2.7+15.04.20150304/tools/dee-tool.c0000644000015300001610000002001112475676210017625 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library 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 Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #include #include #include #include static gchar *resource_name = NULL; static gchar *model_name = NULL; static gchar *peer_name = NULL; static gboolean linger; static gboolean private; static gboolean server; static gboolean watch_changes; static GOptionEntry option_entries[] = { { "resource", 'r', 0, G_OPTION_ARG_STRING, &resource_name, "Dump a resource given by name" }, { "model", 'm', 0, G_OPTION_ARG_STRING, &model_name, "Dump a model given by name" }, { "peer", 'p', 0, G_OPTION_ARG_STRING, &peer_name, "List peers and leader of a swarm" }, { "linger", '\0', 0, G_OPTION_ARG_NONE, &linger, "Don't exit, but keep the process running the mainloop" }, { "private", '\0', 0, G_OPTION_ARG_NONE, &private, "Use a private (aka peer-2-peer) DBus connection" }, { "server", '\0', 0, G_OPTION_ARG_NONE, &server, "Set up a private DBus server. Implies --private and --linger" }, { "watch-changes", '\0', 0, G_OPTION_ARG_NONE, &watch_changes, "Watch for changes to the given resource. Implies --linger" }, { NULL } }; static void dump_resource (const gchar *name) { DeeResourceManager *rs; GObject *r; GError *error; GVariant *v; gchar *dump; rs = dee_resource_manager_get_default (); error = NULL; r = dee_resource_manager_load (rs, name, &error); if (error) { g_printerr ("Failed loading resource '%s': %s\n", name, error->message); exit (4); } if (!r) { g_printerr ("No parser registered for resource '%s'\n", name); exit (5); } v = dee_serializable_serialize (DEE_SERIALIZABLE (r)); dump = g_variant_print (v, FALSE); g_printf ("%s\n", dump); g_free (dump); g_variant_unref (v); g_object_unref (r); } static guint num_rows_added = 0; static guint num_rows_changed = 0; static guint num_rows_deleted = 0; static void on_model_trasaction_begin (DeeSharedModel *model, guint64 bsq, guint64 esq, gpointer user_data) { guint n_rows; gchar *time_str; GTimeVal time_val; n_rows = dee_model_get_n_rows (DEE_MODEL (model)); g_get_current_time (&time_val); time_str = g_time_val_to_iso8601 (&time_val); g_print ("%s:\n Transaction begin - %u rows (seqnums: " "%" G_GUINT64_FORMAT " - %" G_GUINT64_FORMAT ")\n", time_str + 11, n_rows, bsq, esq); num_rows_added = 0; num_rows_changed = 0; num_rows_deleted = 0; g_free (time_str); } static void increment_callback (DeeModel *model, DeeModelIter *iter, gpointer user_data) { guint *int_ptr = (guint*) user_data; *int_ptr = *int_ptr + 1; } static void on_model_trasaction_end (DeeSharedModel *model, guint64 bsq, guint64 esq, gpointer user_data) { guint n_rows; n_rows = dee_model_get_n_rows (DEE_MODEL (model)); g_print (" end - %u rows (%u added, %u changed, %u deleted)\n", n_rows, num_rows_added, num_rows_changed, num_rows_deleted); } static void dump_model (const gchar *name) { DeeSharedModel *m; GMainContext *ctx; GVariant *v; gchar *dump; if (server) { m = DEE_SHARED_MODEL (dee_shared_model_new_for_peer ( DEE_PEER (dee_server_new (name)))); dee_model_set_schema (DEE_MODEL (m), "s", "i", NULL); } else if (private) { m = DEE_SHARED_MODEL (dee_shared_model_new_for_peer ( DEE_PEER (dee_client_new (name)))); } else m = DEE_SHARED_MODEL (dee_shared_model_new (name)); ctx = g_main_context_default (); while (!dee_shared_model_is_synchronized (m)) { g_main_context_iteration (ctx, TRUE); } v = dee_serializable_serialize (DEE_SERIALIZABLE (m)); dump = g_variant_print (v, FALSE); g_printf ("%s\n", dump); if (watch_changes) { g_signal_connect (m, "begin-transaction", G_CALLBACK (on_model_trasaction_begin), NULL); g_signal_connect (m, "end-transaction", G_CALLBACK (on_model_trasaction_end), NULL); g_signal_connect (m, "row-added", G_CALLBACK (increment_callback), &num_rows_added); g_signal_connect (m, "row-changed", G_CALLBACK (increment_callback), &num_rows_changed); g_signal_connect (m, "row-removed", G_CALLBACK (increment_callback), &num_rows_deleted); } if (linger) { while (TRUE) { g_main_context_iteration (ctx, TRUE); } } g_free (dump); g_variant_unref (v); g_object_unref (m); } static void _peer_found_cb (DeePeer *p, const gchar *name) { g_printf ("+ %s\n", name); } static void _peer_lost_cb (DeePeer *p, const gchar *name) { g_printf ("- %s\n", name); } static gboolean timed_out = FALSE; static gboolean _timeout_cb () { timed_out = TRUE; return FALSE; } static void dump_peer (const gchar *name) { DeePeer *p; GMainContext *ctx; if (server) p = DEE_PEER (dee_server_new (name)); else if (private) p = DEE_PEER (dee_client_new (name)); else p = dee_peer_new (name); ctx = g_main_context_default (); g_signal_connect (p, "peer-found", G_CALLBACK (_peer_found_cb), NULL); g_signal_connect (p, "peer-lost", G_CALLBACK (_peer_lost_cb), NULL); /* Wait untli we have the leader */ while (!dee_peer_get_swarm_leader (p)) { g_main_context_iteration (ctx, TRUE); } g_printf ("LEADER %s\n", dee_peer_get_swarm_leader (p)); /* If we're serving a private conn stick around indefinitely, * otherwise quit after 1s */ if (!linger) g_timeout_add_seconds (1, _timeout_cb, NULL); while (!timed_out) { g_main_context_iteration (ctx, TRUE); } } int main (int argc, char *argv[]) { GError *error; GOptionContext *options; #if !GLIB_CHECK_VERSION(2, 35, 1) g_type_init (); #endif options = g_option_context_new (NULL); g_option_context_add_main_entries (options, option_entries, NULL); error = NULL; if (!g_option_context_parse (options, &argc, &argv, &error)) { g_printerr ("Invalid command line: %s\n", error->message); g_error_free (error); return 1; } if (server) { private = TRUE; linger = TRUE; } if (watch_changes) { linger = TRUE; } if (resource_name) { if (model_name || peer_name) { g_printerr ("Invalid command line: You must specify precisely one of " "--resource, --model, or --peer\n"); return 2; } dump_resource (resource_name); } else if (model_name) { if (resource_name || peer_name) { g_printerr ("Invalid command line: You must specify precisely one of " "--resource, --model, or --peer\n"); return 2; } dump_model (model_name); } else if (peer_name) { if (resource_name || model_name) { g_printerr ("Invalid command line: You must specify precisely one of " "--resource, --model, or --peer\n"); return 2; } dump_peer (peer_name); } else { g_printerr ("Invalid command line: Unexpected arguments\n"); return 3; } return 0; } dee-1.2.7+15.04.20150304/TODO0000644000015300001610000000000112475676210015276 0ustar pbuserpbgroup00000000000000 dee-1.2.7+15.04.20150304/vapi/0000755000015300001610000000000012475676370015565 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/vapi/Makefile.am0000644000015300001610000000106712475676210017616 0ustar pbuserpbgroup00000000000000NULL = vapidir = $(datadir)/vala/vapi if HAVE_INTROSPECTION if HAVE_VAPIGEN dee-1.0.vapi: $(top_builddir)/src/Dee-$(GIR_VERSION).gir $(srcdir)/Dee-1.0-custom.vala $(srcdir)/Dee-1.0.metadata $(AM_V_GEN)$(VALA_API_GEN) --pkg gio-2.0 --library dee-1.0 --metadatadir=$(srcdir) $(filter %.gir %.vala,$^) @touch $@ endif endif dist_vapi_DATA = \ dee-1.0.deps \ dee-1.0.vapi \ $(NULL) dist_noinst_DATA = \ Dee-1.0.metadata \ Dee-1.0-custom.vala \ $(NULL) DISTCLEANFILES = dee-1.0.vapi EXTRA_DIST = \ $(NULL) dee-1.2.7+15.04.20150304/vapi/Dee-1.0.metadata0000644000015300001610000000161612475676210020255 0ustar pbuserpbgroup00000000000000GListResultSet skip GListResultSetClass skip FilterModel .filter unowned Model .append skip=false .build_named_row skip .get skip=false .get_row skip .insert skip=false .insert_before skip=false .insert_sorted skip=false .find_sorted skip=false .prepend skip=false .set skip=false .set_schema skip=false .set_column_names skip .register_tag skip .get_tag skip .set_tag skip .clear_tag skip SerializableParseFunc skip=false Serializable .register_parser skip=false .externalize skip // we need floating annotation here, see -custom SerializableModel .get_seqnum virtual .set_seqnum virtual .inc_seqnum virtual SharedModelError errordomain .shared_model_error_leader_invalidated name="LEADER_INVALIDATED" ICUError cheader_filename="dee-icu.h" // why does gir add these twice? filter_new skip resource_manager_get_default skip serializable_parse skip serializable_parse_external skip dee-1.2.7+15.04.20150304/vapi/dee-1.0.deps0000644000015300001610000000003512475676210017462 0ustar pbuserpbgroup00000000000000glib-2.0 gobject-2.0 gio-2.0 dee-1.2.7+15.04.20150304/vapi/Dee-1.0-custom.vala0000644000015300001610000000614512475676210020732 0ustar pbuserpbgroup00000000000000namespace Dee { [CCode (type_id = "dee_model_get_type ()")] public interface Model { [CCode (array_length_pos = 1.33333, array_length_type = "guint", cname = "dee_model_build_named_row_sunk")] public GLib.Variant[] build_named_row ([CCode (array_length = false)] GLib.Variant[]? out_row_members, string first_column_name, ...); [CCode (array_length_pos = 1.33333, array_length_type = "guint", cname = "dee_model_build_named_row_sunk")] public unowned GLib.Variant[] build_named_row_static ([CCode (array_length = false)] GLib.Variant[] out_row_members, string first_column_name, ...); [CCode (array_length = false, array_null_terminated = true)] public abstract GLib.Variant[] get_row (Dee.ModelIter iter, [CCode (array_length = false)] GLib.Variant[]? out_row_members = null); [CCode (array_length = false, array_null_terminated = true, cname = "dee_model_get_row")] public unowned GLib.Variant[] get_row_static (Dee.ModelIter iter, [CCode (array_length = false)] GLib.Variant[] out_row_members); public void set_column_names (string first_column_name, ...); } public struct Filter { [CCode (cname = "destroy")] public GLib.DestroyNotify destroy_notify; public static Filter @new (Dee.StaticFilterMapFunc map_func, owned Dee.FilterMapNotify map_notify); } [CCode (ref_function = "", unref_function = "")] [Compact] public class ModelIter { } [CCode (cheader_filename = "dee.h", ref_function = "", unref_function = "")] [Compact] public class ModelTag { [CCode (cname = "dee_model_register_tag", simple_generics = true)] public ModelTag (Dee.Model model); [CCode (cname = "dee_model_set_tag", instance_pos = 2.1)] public void @set (Dee.Model model, Dee.ModelIter iter, owned G value); [CCode (cname = "dee_model_get_tag", instance_pos = 2.1)] public unowned G @get (Dee.Model model, Dee.ModelIter iter); [CCode (cname = "dee_model_clear_tag", instance_pos = 2.1)] public void clear (Dee.Model model, Dee.ModelIter iter); } [CCode (type_id = "dee_result_set_get_type ()")] public interface ResultSet { [CCode (cname = "_vala_dee_result_set_iterator")] public Dee.ResultSet iterator (); [CCode (cname = "_vala_dee_result_set_next_value")] public unowned Dee.ModelIter? next_value (); } [CCode (type_id = "dee_serializable_get_type ()")] public interface Serializable : GLib.Object { [CCode (returns_floating_reference = true)] public GLib.Variant externalize (); } [CCode (type_id = "dee_term_list_get_type ()")] public class TermList : GLib.Object { [CCode (cname = "dee_term_list_get_term")] public unowned string @get (uint index); public uint size { [CCode (cname = "dee_term_list_num_terms")] get; } } [CCode (cheader_filename = "dee.h", has_target = false, cname = "DeeFilterMapFunc")] public delegate void StaticFilterMapFunc (Dee.Model orig_model, Dee.FilterModel filter_model, void* data); [CCode (cheader_filename = "dee-icu.h", free_function = "dee_icu_term_filter_destroy")] [Compact] public class ICUTermFilter { public ICUTermFilter (string system_id, string? rules) throws Dee.ICUError; public ICUTermFilter.ascii_folder (); } } dee-1.2.7+15.04.20150304/vapi/dee-1.0.vapi0000644000015300001610000005334412475676210017501 0ustar pbuserpbgroup00000000000000/* dee-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Dee", gir_namespace = "Dee", gir_version = "1.0", lower_case_cprefix = "dee_")] namespace Dee { [CCode (cheader_filename = "dee.h", type_id = "dee_analyzer_get_type ()")] public class Analyzer : GLib.Object { [CCode (has_construct_function = false)] public Analyzer (); public virtual void add_term_filter (owned Dee.TermFilterFunc filter_func); public virtual void analyze (string data, Dee.TermList? terms_out, Dee.TermList? colkeys_out); public virtual int collate_cmp (string key1, string key2); public static int collate_cmp_func (string key1, string key2, void* analyzer); public virtual string collate_key (string data); public virtual void tokenize (string data, Dee.TermList terms_out); } [CCode (cheader_filename = "dee.h", type_id = "dee_client_get_type ()")] public class Client : Dee.Peer { [CCode (has_construct_function = false)] public Client (string swarm_name); [CCode (has_construct_function = false)] public Client.for_address (string swarm_name, string bus_address); [NoAccessorMethod] public string bus_address { owned get; construct; } } [CCode (cheader_filename = "dee.h", type_id = "dee_file_resource_manager_get_type ()")] public class FileResourceManager : GLib.Object, Dee.ResourceManager { [CCode (has_construct_function = false, type = "DeeResourceManager*")] public FileResourceManager (string primary_path); public void add_search_path (string path); public unowned string get_primary_path (); public string primary_path { get; construct; } } [CCode (cheader_filename = "dee.h", type_id = "dee_filter_model_get_type ()")] public class FilterModel : Dee.ProxyModel, Dee.Model, Dee.Serializable { [CCode (has_construct_function = false, type = "DeeModel*")] public FilterModel (Dee.Model orig_model, Dee.Filter filter); public unowned Dee.ModelIter append_iter (Dee.ModelIter iter); public bool contains (Dee.ModelIter iter); public unowned Dee.ModelIter insert_iter (Dee.ModelIter iter, uint pos); public unowned Dee.ModelIter insert_iter_before (Dee.ModelIter iter, Dee.ModelIter pos); public unowned Dee.ModelIter insert_iter_with_original_order (Dee.ModelIter iter); public unowned Dee.ModelIter prepend_iter (Dee.ModelIter iter); [NoAccessorMethod] public Dee.Filter filter { get; construct; } } [CCode (cheader_filename = "dee.h", type_id = "dee_hash_index_get_type ()")] public class HashIndex : Dee.Index { [CCode (has_construct_function = false)] public HashIndex (Dee.Model model, Dee.Analyzer analyzer, Dee.ModelReader reader); } [CCode (cheader_filename = "dee-icu.h", free_function = "dee_icu_term_filter_destroy")] [Compact] public class ICUTermFilter { public ICUTermFilter (string system_id, string? rules) throws Dee.ICUError; public string apply (string text); public ICUTermFilter.ascii_folder (); public void destroy (); } [CCode (cheader_filename = "dee.h", type_id = "dee_index_get_type ()")] public abstract class Index : GLib.Object { [CCode (has_construct_function = false)] protected Index (); public virtual void @foreach (string start_term, Dee.IndexIterFunc func); public unowned Dee.Analyzer get_analyzer (); public unowned Dee.Model get_model (); public virtual uint get_n_rows (); public virtual uint get_n_rows_for_term (string term); public virtual uint get_n_terms (); public Dee.ModelReader get_reader (); public virtual uint get_supported_term_match_flags (); public virtual Dee.ResultSet lookup (string term, Dee.TermMatchFlag flags); public unowned Dee.ModelIter lookup_one (string term); public Dee.Analyzer analyzer { get; construct; } public Dee.Model model { get; construct; } public Dee.ModelReader reader { construct; } } [CCode (cheader_filename = "dee.h", ref_function = "", unref_function = "")] [Compact] public class ModelIter { } [CCode (cheader_filename = "dee.h", ref_function = "", unref_function = "")] [Compact] public class ModelTag { [CCode (cname = "dee_model_register_tag", simple_generics = true)] public ModelTag (Dee.Model model); [CCode (cname = "dee_model_clear_tag", instance_pos = 2.1)] public void clear (Dee.Model model, Dee.ModelIter iter); [CCode (cname = "dee_model_get_tag", instance_pos = 2.1)] public unowned G @get (Dee.Model model, Dee.ModelIter iter); [CCode (cname = "dee_model_set_tag", instance_pos = 2.1)] public void @set (Dee.Model model, Dee.ModelIter iter, owned G value); } [CCode (cheader_filename = "dee.h", type_id = "dee_peer_get_type ()")] public class Peer : GLib.Object { [CCode (has_construct_function = false)] public Peer (string swarm_name); public virtual GLib.SList get_connections (); public virtual unowned string get_swarm_leader (); public unowned string get_swarm_name (); public virtual bool is_swarm_leader (); public bool is_swarm_owner (); [CCode (array_length = false, array_null_terminated = true)] public virtual string[] list_peers (); public string swarm_leader { get; } [NoAccessorMethod] public string swarm_name { owned get; set construct; } [NoAccessorMethod] public bool swarm_owner { get; construct; } public virtual signal void connection_acquired (GLib.DBusConnection connection); public virtual signal void connection_closed (GLib.DBusConnection connection); public virtual signal void peer_found (string name); public virtual signal void peer_lost (string name); } [CCode (cheader_filename = "dee.h", type_id = "dee_proxy_model_get_type ()")] public class ProxyModel : Dee.SerializableModel, Dee.Model, Dee.Serializable { [CCode (has_construct_function = false)] protected ProxyModel (); [NoAccessorMethod] public Dee.Model back_end { owned get; construct; } [NoAccessorMethod] public bool inherit_seqnums { get; construct; } [NoAccessorMethod] public bool proxy_signals { get; construct; } } [CCode (cheader_filename = "dee.h", type_id = "dee_sequence_model_get_type ()")] public class SequenceModel : Dee.SerializableModel, Dee.Model, Dee.Serializable { [CCode (has_construct_function = false, type = "DeeModel*")] public SequenceModel (); } [CCode (cheader_filename = "dee.h", type_id = "dee_serializable_model_get_type ()")] public abstract class SerializableModel : GLib.Object, Dee.Model, Dee.Serializable { [CCode (has_construct_function = false)] protected SerializableModel (); public virtual uint64 get_seqnum (); public virtual uint64 inc_seqnum (); public virtual void set_seqnum (uint64 seqnum); } [CCode (cheader_filename = "dee.h", type_id = "dee_server_get_type ()")] public class Server : Dee.Peer { [CCode (has_construct_function = false)] public Server (string swarm_name); public static string bus_address_for_name (string name, bool include_username); [CCode (has_construct_function = false)] public Server.for_address (string swarm_name, string bus_address); public unowned string get_client_address (); [NoAccessorMethod] public string bus_address { owned get; construct; } [NoAccessorMethod] public bool same_user_only { get; construct; } } [CCode (cheader_filename = "dee.h", type_id = "dee_shared_model_get_type ()")] public class SharedModel : Dee.ProxyModel, Dee.Model, Dee.Serializable { [CCode (has_construct_function = false, type = "DeeModel*")] public SharedModel (string name); public uint flush_revision_queue (); public uint flush_revision_queue_sync (); [CCode (has_construct_function = false, type = "DeeModel*")] public SharedModel.for_peer (owned Dee.Peer peer); public Dee.SharedModelFlushMode get_flush_mode (); public unowned Dee.Peer get_peer (); public unowned string get_swarm_name (); public bool is_leader (); public bool is_synchronized (); public void set_flush_mode (Dee.SharedModelFlushMode mode); [CCode (has_construct_function = false, type = "DeeModel*")] public SharedModel.with_back_end (string name, owned Dee.Model back_end); [NoAccessorMethod] public Dee.SharedModelAccessMode access_mode { get; construct; } public Dee.SharedModelFlushMode flush_mode { get; set; } public Dee.Peer peer { get; construct; } [NoAccessorMethod] public bool synchronized { get; } public signal void begin_transaction (uint64 begin_seqnum, uint64 end_seqnum); public signal void end_transaction (uint64 begin_seqnum, uint64 end_seqnum); } [CCode (cheader_filename = "dee.h", type_id = "dee_term_list_get_type ()")] public class TermList : GLib.Object { [CCode (has_construct_function = false)] protected TermList (); public virtual unowned Dee.TermList add_term (string term); public virtual unowned Dee.TermList clear (); public virtual Dee.TermList clone (); [CCode (cname = "dee_term_list_get_term")] public unowned string @get (uint index); public virtual unowned string get_term (uint n); public virtual uint num_terms (); public uint size { [CCode (cname = "dee_term_list_num_terms")] get; } } [CCode (cheader_filename = "dee.h", type_id = "dee_text_analyzer_get_type ()")] public class TextAnalyzer : Dee.Analyzer { [CCode (has_construct_function = false)] public TextAnalyzer (); } [CCode (cheader_filename = "dee.h", type_id = "dee_transaction_get_type ()")] public class Transaction : Dee.SerializableModel, Dee.Model, Dee.Serializable { [CCode (has_construct_function = false, type = "DeeModel*")] public Transaction (Dee.Model target); public bool commit () throws GLib.Error; public static GLib.Quark error_quark (); public unowned Dee.Model get_target (); public bool is_committed (); public Dee.Model target { get; construct; } } [CCode (cheader_filename = "dee.h", type_id = "dee_tree_index_get_type ()")] public class TreeIndex : Dee.Index { [CCode (has_construct_function = false)] public TreeIndex (Dee.Model model, Dee.Analyzer analyzer, Dee.ModelReader reader); } [CCode (cheader_filename = "dee.h", type_id = "dee_model_get_type ()")] public interface Model : GLib.Object { public unowned Dee.ModelIter append (...); public abstract unowned Dee.ModelIter append_row ([CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_members); public abstract void begin_changeset (); [CCode (array_length_pos = 1.33333, array_length_type = "guint", cname = "dee_model_build_named_row_sunk")] public GLib.Variant[] build_named_row ([CCode (array_length = false)] GLib.Variant[]? out_row_members, string first_column_name, ...); [CCode (array_length_pos = 1.33333, array_length_type = "guint", cname = "dee_model_build_named_row_sunk")] public unowned GLib.Variant[] build_named_row_static ([CCode (array_length = false)] GLib.Variant[] out_row_members, string first_column_name, ...); public abstract void clear (); public abstract void end_changeset (); public abstract unowned Dee.ModelIter find_row_sorted ([CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_spec, [CCode (delegate_target_pos = 2.5)] Dee.CompareRowFunc cmp_func, out bool out_was_found); public unowned Dee.ModelIter find_row_sorted_with_sizes ([CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_spec, [CCode (delegate_target_pos = 2.5)] Dee.CompareRowSizedFunc cmp_func, out bool out_was_found); public unowned Dee.ModelIter find_sorted ([CCode (delegate_target_pos = 1.5)] Dee.CompareRowFunc cmp_func, out bool out_was_found, ...); public void @get (Dee.ModelIter iter, ...); public abstract bool get_bool (Dee.ModelIter iter, uint column); public abstract int get_column_index (string column_name); [CCode (array_length_pos = 0.1, array_length_type = "guint")] public abstract unowned string[] get_column_names (); public abstract unowned string get_column_schema (uint column); public abstract double get_double (Dee.ModelIter iter, uint column); public abstract unowned string get_field_schema (string field_name, out uint out_column); public abstract unowned Dee.ModelIter get_first_iter (); public abstract int32 get_int32 (Dee.ModelIter iter, uint column); public abstract int64 get_int64 (Dee.ModelIter iter, uint column); public abstract unowned Dee.ModelIter get_iter_at_row (uint row); public abstract unowned Dee.ModelIter get_last_iter (); public abstract uint get_n_columns (); public abstract uint get_n_rows (); public abstract uint get_position (Dee.ModelIter iter); [CCode (array_length = false, array_null_terminated = true)] public abstract GLib.Variant[] get_row (Dee.ModelIter iter, [CCode (array_length = false)] GLib.Variant[]? out_row_members = null); [CCode (array_length = false, array_null_terminated = true, cname = "dee_model_get_row")] public unowned GLib.Variant[] get_row_static (Dee.ModelIter iter, [CCode (array_length = false)] GLib.Variant[] out_row_members); [CCode (array_length_pos = 0.1, array_length_type = "guint")] public abstract unowned string[] get_schema (); public abstract unowned string get_string (Dee.ModelIter iter, uint column); public abstract uint8 get_uchar (Dee.ModelIter iter, uint column); public abstract uint32 get_uint32 (Dee.ModelIter iter, uint column); public abstract uint64 get_uint64 (Dee.ModelIter iter, uint column); public abstract GLib.Variant get_value (Dee.ModelIter iter, uint column); public abstract GLib.Variant get_value_by_name (Dee.ModelIter iter, string column_name); public abstract GLib.HashTable get_vardict_schema (uint num_column); public unowned Dee.ModelIter insert (uint pos, ...); public unowned Dee.ModelIter insert_before (Dee.ModelIter iter, ...); public abstract unowned Dee.ModelIter insert_row (uint pos, [CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_members); public abstract unowned Dee.ModelIter insert_row_before (Dee.ModelIter iter, [CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_members); public abstract unowned Dee.ModelIter insert_row_sorted ([CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_members, Dee.CompareRowFunc cmp_func); public unowned Dee.ModelIter insert_row_sorted_with_sizes ([CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_members, Dee.CompareRowSizedFunc cmp_func); public unowned Dee.ModelIter insert_sorted ([CCode (delegate_target_pos = 1.5)] Dee.CompareRowFunc cmp_func, ...); public abstract bool is_first (Dee.ModelIter iter); public abstract bool is_last (Dee.ModelIter iter); public abstract unowned Dee.ModelIter next (Dee.ModelIter iter); public unowned Dee.ModelIter prepend (...); public abstract unowned Dee.ModelIter prepend_row ([CCode (array_length = false, array_null_terminated = true)] GLib.Variant[] row_members); public abstract unowned Dee.ModelIter prev (Dee.ModelIter iter); public abstract void register_vardict_schema (uint num_column, GLib.HashTable schemas); public abstract void remove (Dee.ModelIter iter); public void @set (Dee.ModelIter iter, ...); public void set_column_names (string first_column_name, ...); public abstract void set_column_names_full ([CCode (array_length_cname = "num_columns", array_length_pos = 1.1, array_length_type = "guint", array_null_terminated = true)] string[] column_names); public abstract void set_row (Dee.ModelIter iter, [CCode (array_length = false)] GLib.Variant[] row_members); public void set_schema (...); public abstract void set_schema_full ([CCode (array_length_cname = "num_columns", array_length_pos = 1.1, array_length_type = "guint", array_null_terminated = true)] string[] column_schemas); public abstract void set_value (Dee.ModelIter iter, uint column, GLib.Variant value); public virtual signal void changeset_finished (); public virtual signal void changeset_started (); public virtual signal void row_added (Dee.ModelIter iter); public virtual signal void row_changed (Dee.ModelIter iter); public virtual signal void row_removed (Dee.ModelIter iter); } [CCode (cheader_filename = "dee.h", type_id = "dee_resource_manager_get_type ()")] public interface ResourceManager : GLib.Object { public static unowned Dee.ResourceManager get_default (); public abstract GLib.Object load (string resource_name) throws GLib.Error; public abstract bool store (Dee.Serializable resource, string resource_name) throws GLib.Error; } [CCode (cheader_filename = "dee.h", type_id = "dee_result_set_get_type ()")] public interface ResultSet : GLib.Object { public abstract unowned Dee.Model get_model (); public abstract uint get_n_rows (); public abstract bool has_next (); [CCode (cname = "_vala_dee_result_set_iterator")] public Dee.ResultSet iterator (); public abstract unowned Dee.ModelIter next (); [CCode (cname = "_vala_dee_result_set_next_value")] public unowned Dee.ModelIter? next_value (); public abstract unowned Dee.ModelIter peek (); public abstract void seek (uint pos); public abstract uint tell (); } [CCode (cheader_filename = "dee.h", type_id = "dee_serializable_get_type ()")] public interface Serializable : GLib.Object { [CCode (returns_floating_reference = true)] public GLib.Variant externalize (); public static GLib.Object parse (GLib.Variant data, GLib.Type type); public static GLib.Object parse_external (GLib.Variant data); public static void register_parser (GLib.Type type, GLib.VariantType vtype, Dee.SerializableParseFunc parse_func); public abstract GLib.Variant serialize (); } [CCode (cheader_filename = "dee.h")] public struct Filter { [CCode (cname = "destroy")] public GLib.DestroyNotify destroy_notify; public weak Dee.FilterMapFunc map_func; public weak Dee.FilterMapNotify map_notify; public void* userdata; public void destroy (); public void map (Dee.Model orig_model, Dee.FilterModel filter_model); public static Dee.Filter @new (Dee.StaticFilterMapFunc map_func, owned Dee.FilterMapNotify map_notify); public static Dee.Filter new_collator (uint column); public static Dee.Filter new_collator_desc (uint column); public static Dee.Filter new_for_any_column (uint column, GLib.Variant value); public static Dee.Filter new_for_key_column (uint column, string key); public static Dee.Filter new_regex (uint column, GLib.Regex regex); public static Dee.Filter new_sort (owned Dee.CompareRowFunc cmp_row); public bool notify (Dee.ModelIter orig_iter, Dee.Model orig_model, Dee.FilterModel filter_model); } [CCode (cheader_filename = "dee.h", has_type_id = false)] public struct ModelReader { public weak Dee.ModelReaderFunc reader_func; public void* userdata; public void destroy (); public static Dee.ModelReader @new (owned Dee.ModelReaderFunc reader_func); public static Dee.ModelReader new_for_int32_column (uint column); public static Dee.ModelReader new_for_string_column (uint column); public static Dee.ModelReader new_for_uint32_column (uint column); public string read (Dee.Model model, Dee.ModelIter iter); } [CCode (cheader_filename = "dee.h", cprefix = "DEE_SHARED_MODEL_ACCESS_MODE_", type_id = "dee_shared_model_access_mode_get_type ()")] public enum SharedModelAccessMode { WORLD_WRITABLE, LEADER_WRITABLE } [CCode (cheader_filename = "dee.h", cprefix = "DEE_SHARED_MODEL_FLUSH_MODE_", type_id = "dee_shared_model_flush_mode_get_type ()")] public enum SharedModelFlushMode { AUTOMATIC, MANUAL } [CCode (cheader_filename = "dee.h", cprefix = "DEE_TERM_MATCH_", has_type_id = false)] [Flags] public enum TermMatchFlag { EXACT, PREFIX } [CCode (cheader_filename = "dee.h", cprefix = "DEE_TRANSACTION_ERROR_", has_type_id = false)] public enum TransactionError { CONCURRENT_MODIFICATION, COMMITTED } [CCode (cheader_filename = "dee-icu.h", cprefix = "DEE_ICU_ERROR_")] public errordomain ICUError { BAD_RULE, BAD_ID, UNKNOWN; public static GLib.Quark quark (); } [CCode (cheader_filename = "dee.h", cprefix = "DEE_SHARED_MODEL_ERROR_LEADER_")] public errordomain SharedModelError { [CCode (cname = "DEE_SHARED_MODEL_ERROR_LEADER_INVALIDATED")] LEADER_INVALIDATED } [CCode (cheader_filename = "dee.h", instance_pos = 1.9)] public delegate string CollatorFunc (string input); [CCode (cheader_filename = "dee.h", instance_pos = 2.9)] public delegate int CompareRowFunc ([CCode (array_length = false)] GLib.Variant[] row1, [CCode (array_length = false)] GLib.Variant[] row2); [CCode (cheader_filename = "dee.h", instance_pos = 2.9)] public delegate int CompareRowSizedFunc ([CCode (array_length_cname = "row1_length", array_length_pos = 1.5, array_length_type = "guint")] GLib.Variant[] row1, [CCode (array_length_cname = "row2_length", array_length_pos = 2.1, array_length_type = "guint")] GLib.Variant[] row2); [CCode (cheader_filename = "dee.h", instance_pos = 2.9)] public delegate void FilterMapFunc (Dee.Model orig_model, Dee.FilterModel filter_model); [CCode (cheader_filename = "dee.h", instance_pos = 3.9)] public delegate bool FilterMapNotify (Dee.Model orig_model, Dee.ModelIter orig_iter, Dee.FilterModel filter_model); [CCode (cheader_filename = "dee.h", instance_pos = 2.9)] public delegate bool IndexIterFunc (string key, Dee.ResultSet rows); [CCode (cheader_filename = "dee.h", instance_pos = 2.9)] public delegate string ModelReaderFunc (Dee.Model model, Dee.ModelIter iter); [CCode (cheader_filename = "dee.h", has_target = false)] public delegate GLib.Object SerializableParseFunc (GLib.Variant data); [CCode (cheader_filename = "dee.h", cname = "DeeFilterMapFunc", has_target = false)] public delegate void StaticFilterMapFunc (Dee.Model orig_model, Dee.FilterModel filter_model, void* data); [CCode (cheader_filename = "dee.h", instance_pos = 2.9)] public delegate void TermFilterFunc (Dee.TermList terms_in, Dee.TermList terms_out); [CCode (cheader_filename = "dee.h", cname = "DEE_PEER_DBUS_IFACE")] public const string PEER_DBUS_IFACE; [CCode (cheader_filename = "dee.h", cname = "DEE_SEQUENCE_MODEL_DBUS_IFACE")] public const string SEQUENCE_MODEL_DBUS_IFACE; [CCode (cheader_filename = "dee.h", cname = "DEE_SHARED_MODEL_DBUS_IFACE")] public const string SHARED_MODEL_DBUS_IFACE; } dee-1.2.7+15.04.20150304/dee-1.0.pc.in0000644000015300001610000000045712475676210016607 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: @PACKAGE_NAME@ Description: Peer discovery and shared models over DBus Version: @VERSION@ Libs: -L${libdir} -ldee-1.0 Cflags: -I${includedir}/dee-1.0 Requires: glib-2.0 gthread-2.0 gobject-2.0 gio-2.0 gio-unix-2.0 dee-1.2.7+15.04.20150304/doc/0000755000015300001610000000000012475676370015373 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/doc/Makefile.am0000644000015300001610000000002212475676210017412 0ustar pbuserpbgroup00000000000000SUBDIRS=reference dee-1.2.7+15.04.20150304/doc/reference/0000755000015300001610000000000012475676370017331 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/doc/reference/dee-1.0/0000755000015300001610000000000012475676370020362 5ustar pbuserpbgroup00000000000000dee-1.2.7+15.04.20150304/doc/reference/dee-1.0/Makefile.am0000644000015300001610000000567612475676210022425 0ustar pbuserpbgroup00000000000000# We require automake 1.7 at least. AUTOMAKE_OPTIONS = 1.7 # The name of the module, e.g. 'glib'. DOC_MODULE=dee-1.0 # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # The directory containing the source code. Relative to $(srcdir). # gtk-doc will search all .c & .h files beneath here for inline comments # documenting the functions and macros. # e.g. DOC_SOURCE_DIR=../../../gtk DOC_SOURCE_DIR=$(top_srcdir)/src # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS= # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml MKDB_OPTIONS=--sgml-mode --output-format=xml --ignore-files=trio # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS= # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB=$(top_srcdir)/src/*.h CFILE_GLOB=$(top_srcdir)/src/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES=trace-log.h # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES= # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files= # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) #GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(DEE_CFLAGS) INCLUDES = \ $(DEE_CFLAGS) \ -I$(top_srcdir)/src GTKDOC_LIBS = \ $(DEE_LIBS) \ $(top_builddir)/src/libdee-1.0.la # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt #DISTCLEANFILES += # Comment this out if you want your docs-status tested during 'make check' #TESTS = $(GTKDOC_CHECK) dee-1.2.7+15.04.20150304/doc/reference/dee-1.0/dee-1.0-docs.sgml0000644000015300001610000000405112475676210023216 0ustar pbuserpbgroup00000000000000 ]> Dee Reference Manual Peer Discovery Models Indexes Resources and Serialization Object Hierarchy API Index dee-1.2.7+15.04.20150304/doc/reference/dee-1.0/dee-1.0.types0000644000015300001610000000111112475676210022464 0ustar pbuserpbgroup00000000000000dee_analyzer_get_type dee_client_get_type dee_file_resource_manager_get_type dee_filter_model_get_type dee_glist_result_set_get_type dee_hash_index_get_type dee_index_get_type dee_model_get_type dee_model_iter_get_type dee_peer_get_type dee_proxy_model_get_type dee_resource_manager_get_type dee_result_set_get_type dee_sequence_model_get_type dee_serializable_get_type dee_serializable_model_get_type dee_server_get_type dee_shared_model_access_mode_get_type dee_shared_model_get_type dee_term_list_get_type dee_text_analyzer_get_type dee_transaction_get_type dee_tree_index_get_type dee-1.2.7+15.04.20150304/doc/reference/Makefile.am0000644000015300001610000000002012475676210021346 0ustar pbuserpbgroup00000000000000SUBDIRS=dee-1.0 dee-1.2.7+15.04.20150304/ChangeLog0000644000015300001610000000000112475676210016360 0ustar pbuserpbgroup00000000000000