diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 09:06:59 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 09:06:59 +0200 |
commit | 4ea2cc3bd4a7d9b1c54a9d33e6a1cf82e7c8c21d (patch) | |
tree | d2e54377d14d604356c86862a326f64ae64dadd6 /src/db/TagTable.vala |
Imported Upstream version 0.18.1upstream/0.18.1
Diffstat (limited to 'src/db/TagTable.vala')
-rw-r--r-- | src/db/TagTable.vala | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/db/TagTable.vala b/src/db/TagTable.vala new file mode 100644 index 0000000..a0fade8 --- /dev/null +++ b/src/db/TagTable.vala @@ -0,0 +1,248 @@ +/* Copyright 2011-2014 Yorba Foundation + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +public struct TagID { + public const int64 INVALID = -1; + + public int64 id; + + public TagID(int64 id = INVALID) { + this.id = id; + } + + public bool is_invalid() { + return (id == INVALID); + } + + public bool is_valid() { + return (id != INVALID); + } +} + +public class TagRow { + public TagID tag_id; + public string name; + public Gee.Set<string>? source_id_list; + public time_t time_created; +} + +public class TagTable : DatabaseTable { + private static TagTable instance = null; + + private TagTable() { + set_table_name("TagTable"); + + Sqlite.Statement stmt; + int res = db.prepare_v2("CREATE TABLE IF NOT EXISTS " + + "TagTable " + + "(" + + "id INTEGER PRIMARY KEY, " + + "name TEXT UNIQUE NOT NULL, " + + "photo_id_list TEXT, " + + "time_created INTEGER" + + ")", -1, out stmt); + assert(res == Sqlite.OK); + + res = stmt.step(); + if (res != Sqlite.DONE) + fatal("create TagTable", res); + } + + public static TagTable get_instance() { + if (instance == null) + instance = new TagTable(); + + return instance; + } + + public static void upgrade_for_htags() { + TagTable table = get_instance(); + + try { + Gee.List<TagRow?> rows = table.get_all_rows(); + + foreach (TagRow row in rows) { + row.name = row.name.replace(Tag.PATH_SEPARATOR_STRING, "-"); + table.rename(row.tag_id, row.name); + } + } catch (DatabaseError e) { + error ("TagTable: can't upgrade tag names for hierarchical tag support: %s", e.message); + } + } + + public TagRow add(string name) throws DatabaseError { + Sqlite.Statement stmt; + int res = db.prepare_v2("INSERT INTO TagTable (name, time_created) VALUES (?, ?)", -1, + out stmt); + assert(res == Sqlite.OK); + + time_t time_created = (time_t) now_sec(); + + res = stmt.bind_text(1, name); + assert(res == Sqlite.OK); + res = stmt.bind_int64(2, time_created); + assert(res == Sqlite.OK); + + res = stmt.step(); + if (res != Sqlite.DONE) + throw_error("TagTable.add", res); + + TagRow row = new TagRow(); + row.tag_id = TagID(db.last_insert_rowid()); + row.name = name; + row.source_id_list = null; + row.time_created = time_created; + + return row; + } + + // All fields but tag_id are respected in TagRow. + public TagID create_from_row(TagRow row) throws DatabaseError { + Sqlite.Statement stmt; + int res = db.prepare_v2("INSERT INTO TagTable (name, photo_id_list, time_created) VALUES (?, ?, ?)", + -1, out stmt); + assert(res == Sqlite.OK); + + res = stmt.bind_text(1, row.name); + assert(res == Sqlite.OK); + res = stmt.bind_text(2, serialize_source_ids(row.source_id_list)); + assert(res == Sqlite.OK); + res = stmt.bind_int64(3, row.time_created); + assert(res == Sqlite.OK); + + res = stmt.step(); + if (res != Sqlite.DONE) + throw_error("TagTable.create_from_row", res); + + return TagID(db.last_insert_rowid()); + } + + public void remove(TagID tag_id) throws DatabaseError { + delete_by_id(tag_id.id); + } + + public string? get_name(TagID tag_id) throws DatabaseError { + Sqlite.Statement stmt; + if (!select_by_id(tag_id.id, "name", out stmt)) + return null; + + return stmt.column_text(0); + } + + public TagRow? get_row(TagID tag_id) throws DatabaseError { + Sqlite.Statement stmt; + int res = db.prepare_v2("SELECT name, photo_id_list, time_created FROM TagTable WHERE id=?", + -1, out stmt); + assert(res == Sqlite.OK); + + res = stmt.bind_int64(1, tag_id.id); + assert(res == Sqlite.OK); + + res = stmt.step(); + if (res == Sqlite.DONE) + return null; + else if (res != Sqlite.ROW) + throw_error("TagTable.get_row", res); + + TagRow row = new TagRow(); + row.tag_id = tag_id; + row.name = stmt.column_text(0); + row.source_id_list = unserialize_source_ids(stmt.column_text(1)); + row.time_created = (time_t) stmt.column_int64(2); + + return row; + } + + public Gee.List<TagRow?> get_all_rows() throws DatabaseError { + Sqlite.Statement stmt; + int res = db.prepare_v2("SELECT id, name, photo_id_list, time_created FROM TagTable", -1, + out stmt); + assert(res == Sqlite.OK); + + Gee.List<TagRow?> rows = new Gee.ArrayList<TagRow?>(); + + for (;;) { + res = stmt.step(); + if (res == Sqlite.DONE) + break; + else if (res != Sqlite.ROW) + throw_error("TagTable.get_all_rows", res); + + // res == Sqlite.ROW + TagRow row = new TagRow(); + row.tag_id = TagID(stmt.column_int64(0)); + row.name = stmt.column_text(1); + row.source_id_list = unserialize_source_ids(stmt.column_text(2)); + row.time_created = (time_t) stmt.column_int64(3); + + rows.add(row); + } + + return rows; + } + + public void rename(TagID tag_id, string new_name) throws DatabaseError { + update_text_by_id_2(tag_id.id, "name", new_name); + } + + public void set_tagged_sources(TagID tag_id, Gee.Collection<string> source_ids) throws DatabaseError { + Sqlite.Statement stmt; + int res = db.prepare_v2("UPDATE TagTable SET photo_id_list=? WHERE id=?", -1, out stmt); + assert(res == Sqlite.OK); + + res = stmt.bind_text(1, serialize_source_ids(source_ids)); + assert(res == Sqlite.OK); + res = stmt.bind_int64(2, tag_id.id); + assert(res == Sqlite.OK); + + res = stmt.step(); + if (res != Sqlite.DONE) + throw_error("TagTable.set_tagged_photos", res); + } + + private string? serialize_source_ids(Gee.Collection<string>? source_ids) { + if (source_ids == null) + return null; + + StringBuilder result = new StringBuilder(); + + foreach (string source_id in source_ids) { + result.append(source_id); + result.append(","); + } + + return (result.len != 0) ? result.str : null; + } + + private Gee.Set<string> unserialize_source_ids(string? text_list) { + Gee.Set<string> result = new Gee.HashSet<string>(); + + if (text_list == null) + return result; + + string[] split = text_list.split(","); + foreach (string token in split) { + if (is_string_empty(token)) + continue; + + // handle current and legacy encoding of source ids -- in the past, we only stored + // LibraryPhotos in tags so we only needed to store the numeric database key of the + // photo to uniquely identify it. Now, however, tags can store arbitrary MediaSources, + // so instead of simply storing a number we store the source id, a string that contains + // a typename followed by an identifying number (e.g., "video-022354"). + if (token[0].isdigit()) { + // this is a legacy entry + result.add(PhotoID.upgrade_photo_id_to_source_id(PhotoID(parse_int64(token, 10)))); + } else if (token[0].isalpha()) { + // this is a modern entry + result.add(token); + } + } + + return result; + } +} + |