Commit 06a45451 authored by Niels De Graef's avatar Niels De Graef 😁
Browse files

WIP

parent 775ce8ff
Pipeline #457381 failed with stages
in 25 minutes and 32 seconds
......@@ -135,4 +135,16 @@ public class Contacts.Address : BinChunkChild {
return new PostalAddressFieldDetails (this.address, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(sssssssv)",
this.address.po_box,
this.address.extension,
this.address.street,
this.address.locality,
this.address.region,
this.address.postal_code,
this.address.country,
parameters_to_gvariant ());
}
}
......@@ -54,4 +54,8 @@ public class Contacts.AliasChunk : Chunk {
yield ((AliasDetails) this.persona).change_alias (this.alias);
}
public override Variant? to_gvariant () {
return new Variant.string (this.alias);
}
}
......@@ -50,4 +50,9 @@ public class Contacts.AvatarChunk : Chunk {
requires (this.persona is AvatarDetails) {
yield ((AvatarDetails) this.persona).change_avatar (this.avatar);
}
public override Variant? to_gvariant () {
// FIXME: implement
return null;
}
}
......@@ -107,6 +107,17 @@ public abstract class Contacts.BinChunk : Chunk, GLib.ListModel {
return afds;
}
public override Variant? to_gvariant () {
var builder = new GLib.VariantBuilder (GLib.VariantType.ARRAY);
for (uint i = 0; i < this.elements.length; i++) {
var child_variant = this.elements[i].to_gvariant ();
if (child_variant != null)
builder.add_value (child_variant);
}
return builder.end ();
}
// ListModel implementation
public uint n_items { get { return this.elements.length; } }
......@@ -153,6 +164,35 @@ public abstract class Contacts.BinChunkChild : GLib.Object {
*/
public abstract AbstractFieldDetails? create_afd ();
/**
* XXX
*/
public Variant? to_gvariant () {
if (this.is_empty)
return null;
return to_gvariant_internal ();
}
protected abstract Variant? to_gvariant_internal ();
// Helper to serialize the parameters field
protected Variant parameters_to_gvariant () {
if (this.parameters.size == 0) {
return new GLib.Variant ("a(ss)", null); // Empty array
}
var builder = new GLib.VariantBuilder (GLib.VariantType.ARRAY);
var iter = this.parameters.map_iterator ();
while (iter.next ()) {
string param_name = iter.get_key ();
string param_value = iter.get_value ();
builder.add ("(ss)", param_name, param_value);
}
return builder.end ();
}
// A helper to change a string field with the proper propery notifies
protected void change_string_prop (string prop_name,
ref string old_value,
......
......@@ -59,4 +59,13 @@ public class Contacts.BirthdayChunk : Chunk {
requires (this.persona is BirthdayDetails) {
yield ((BirthdayDetails) this.persona).change_birthday (this.birthday);
}
public override Variant? to_gvariant () {
if (this.birthday == null)
return null;
int year, month, day;
this.birthday.get_ymd (out year, out month, out day);
return new GLib.Variant ("(iii)", year, month, day);
}
}
......@@ -60,4 +60,9 @@ public abstract class Contacts.Chunk : GLib.Object {
*/
public abstract async void save_to_persona () throws GLib.Error
requires (this.persona != null);
/**
* XXX
*/
public abstract Variant? to_gvariant ();
}
......@@ -87,6 +87,10 @@ public class Contacts.EmailAddress : BinChunkChild {
return new EmailFieldDetails (this.raw_address, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(sv)", this.raw_address, parameters_to_gvariant ());
}
public string get_mailto_uri () {
return "mailto:" + Uri.escape_string (this.raw_address, "@" , false);
}
......
......@@ -50,6 +50,11 @@ public class Contacts.FullNameChunk : Chunk {
}
}
public FullNameChunk.from_gvariant (GLib.Variant variant) {
unowned var fn = variant.get_string ();
Object (persona: null, full_name: fn);
}
public override Value? to_value () {
return this.full_name;
}
......@@ -58,4 +63,8 @@ public class Contacts.FullNameChunk : Chunk {
requires (this.persona is NameDetails) {
yield ((NameDetails) this.persona).change_full_name (this.full_name);
}
public override Variant? to_gvariant () {
return new Variant.string (this.full_name);
}
}
......@@ -96,4 +96,11 @@ public class Contacts.ImAddress : BinChunkChild {
return new ImFieldDetails (this.address, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(ssv)",
this.protocol,
this.address,
parameters_to_gvariant ());
}
}
......@@ -57,4 +57,8 @@ public class Contacts.NicknameChunk : Chunk {
yield ((NameDetails) this.persona).change_nickname (this.nickname);
}
public override Variant? to_gvariant () {
return new Variant.string (this.nickname);
}
}
......@@ -82,4 +82,8 @@ public class Contacts.Note : BinChunkChild {
return new NoteFieldDetails (this.text, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(sv)", this.text, parameters_to_gvariant ());
}
}
......@@ -94,4 +94,8 @@ public class Contacts.Phone : BinChunkChild {
return new PhoneFieldDetails (this.raw_number, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(sv)", this.raw_number, parameters_to_gvariant ());
}
}
......@@ -79,6 +79,13 @@ public class Contacts.OrgRole : BinChunkChild {
return new RoleFieldDetails (this.role, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(ssv)",
this.role.organisation_name,
this.role.title,
parameters_to_gvariant ());
}
public string to_string () {
if (this.role.title != "") {
if (this.role.organisation_name != "") {
......
......@@ -66,4 +66,13 @@ public class Contacts.StructuredNameChunk : Chunk {
requires (this.persona is NameDetails) {
yield ((NameDetails) this.persona).change_structured_name (this.structured_name);
}
public override Variant? to_gvariant () {
return new Variant ("(sssss)",
this.structured_name.family_name,
this.structured_name.given_name,
this.structured_name.additional_names,
this.structured_name.prefixes,
this.structured_name.suffixes);
}
}
......@@ -92,4 +92,8 @@ public class Contacts.Url : BinChunkChild {
return new UrlFieldDetails (this.raw_url, this.parameters);
}
protected override Variant? to_gvariant_internal () {
return new Variant ("(sv)", this.raw_url, parameters_to_gvariant ());
}
}
......@@ -18,22 +18,18 @@
using Folks;
/**
* An Parser is an object that can deal with importing a specific format
* of describing a Contact (VCard is the most common example, but there exist
* A Parser is an object that can deal with importing a specific format of
* describing a Contact (vCard is the most common example, but there exist
* also CSV based formats and others).
*
* The main purpose of an Io.Parser is to parser whatever input it gets into a
* {@link GLib.HashTable} with string keys and {@link Value} as values. After
* that, we can choose to either serialize (using the serializing methods in
* Contacts.Io), or to immediately import it in folks using
* {@link Folks.PersonaStore.add_from_details}.
* The main purpose of an Io.Parser is to parser whatever input it gets into an
* array of {@link Contacts.Contact}s. After that, we can for example either
* serialize the contact into a contact again.
*/
public abstract class Contacts.Io.Parser : Object {
/**
* Takes the given input stream and tries to parse it into a
* {@link GLib.HashTable}, which can then be used for methods like
* {@link Folks.PersonaStore.add_persona_from_details}.
* Takes the given input stream and tries to parse it into a set of contacts.
*/
public abstract GLib.HashTable<string, Value?>[] parse (InputStream input) throws GLib.Error;
public abstract Contact[] parse (InputStream input) throws GLib.Error;
}
......@@ -25,7 +25,7 @@ public class Contacts.Io.VCardParser : Contacts.Io.Parser {
public VCardParser () {
}
public override HashTable<string, Value?>[] parse (InputStream input) throws GLib.Error {
public override Contact[] parse (InputStream input) throws GLib.Error {
// Read the whole input into a string.
// We can probably do better, but that takes a bit of extra work
var memory_stream = new MemoryOutputStream.resizable ();
......@@ -34,7 +34,7 @@ public class Contacts.Io.VCardParser : Contacts.Io.Parser {
memory_stream.close ();
var input_str = (string) memory_stream.get_data ();
var result = new GenericArray<HashTable<string, Value?>> ();
var result = new GenericArray<Contact> ();
// Parse the input stream into a set of vcards
int begin_index = input_str.index_of ("BEGIN:VCARD");
......@@ -51,44 +51,44 @@ public class Contacts.Io.VCardParser : Contacts.Io.Parser {
unowned var vcard_attrs = vcard.get_attributes ();
debug ("Got %u attributes in this vcard", vcard_attrs.length ());
var details = new HashTable<string, Value?> (GLib.str_hash, GLib.str_equal);
var contact = new Contact.for_empty (null);
foreach (unowned E.VCardAttribute attr in vcard_attrs) {
switch (attr.get_name ()) {
// Identification Properties
case E.EVC_FN:
handle_fn (details, attr);
handle_fn (contact, attr);
break;
case E.EVC_N:
handle_n (details, attr);
handle_n (contact, attr);
break;
case E.EVC_NICKNAME:
handle_nickname (details, attr);
handle_nickname (contact, attr);
break;
/* FIXME
case E.EVC_PHOTO:
handle_photo (details, attr);
handle_photo (contact, attr);
break;
*/
case E.EVC_BDAY:
handle_bday (details, attr);
handle_bday (contact, attr);
break;
// Delivery Addressing Properties
case E.EVC_ADR:
handle_adr (details, attr);
handle_adr (contact, attr);
break;
// Communications Properties
case E.EVC_TEL:
handle_tel (details, attr);
handle_tel (contact, attr);
break;
case E.EVC_EMAIL:
handle_email (details, attr);
handle_email (contact, attr);
break;
// Explanatory Properties
case E.EVC_NOTE:
handle_note (details, attr);
handle_note (contact, attr);
break;
case E.EVC_URL:
handle_url (details, attr);
handle_url (contact, attr);
break;
default:
......@@ -97,7 +97,7 @@ public class Contacts.Io.VCardParser : Contacts.Io.Parser {
}
}
result.add (details);
result.add (contact);
begin_index = input_str.index_of ("BEGIN:VCARD", end_index);
}
......@@ -106,193 +106,134 @@ public class Contacts.Io.VCardParser : Contacts.Io.Parser {
}
// Handles the "FN" (Full Name) attribute
private void handle_fn (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_fn (Contact contact, E.VCardAttribute attr) {
var full_name = attr.get_value ();
debug ("Got FN '%s'", full_name);
Value? fn_v = Value (typeof (string));
fn_v.set_string (full_name);
details.insert (Folks.PersonaStore.detail_key (PersonaDetail.FULL_NAME),
(owned) fn_v);
var fn_chunk = contact.create_chunk ("full-name", null);
fn_chunk.full_name = full_name;
}
// Handles the "N" (structured Name) attribute
private void handle_n (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_n (Contact contact, E.VCardAttribute attr) {
unowned var values = attr.get_values ();
// From the VCard spec:
// The structured property value corresponds, in sequence, to the Family
// Names (also known as surnames), Given Names, Additional Names, Honorific
// Prefixes, and Honorific Suffixes.
unowned var family_name = values.nth_data (0) ?? "";
unowned var given_name = values.nth_data (1) ?? "";
unowned var additional_names = values.nth_data (2) ?? "";
unowned var prefixes = values.nth_data (3) ?? "";
unowned var suffixes = values.nth_data (4) ?? "";
var structured_name = new StructuredName (family_name, given_name,
additional_names,
prefixes, suffixes);
Value? n_v = Value (typeof (StructuredName));
n_v.take_object ((owned) structured_name);
details.insert (Folks.PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME),
(owned) n_v);
var sn_chunk = contact.create_chunk ("structured-name", null);
sn_chunk.structured_name.family_name = values.nth_data (0) ?? "";
sn_chunk.structured_name.given_name = values.nth_data (1) ?? "";
sn_chunk.structured_name.additional_names = values.nth_data (2) ?? "";
sn_chunk.structured_name.prefixes = values.nth_data (3) ?? "";
sn_chunk.structured_name.suffixes = values.nth_data (4) ?? "";
}
private void handle_nickname (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_nickname (Contact contact, E.VCardAttribute attr) {
var nickname = attr.get_value ();
debug ("Got nickname '%s'", nickname);
Value? nick_v = Value (typeof (string));
nick_v.set_string (nickname);
details.insert (Folks.PersonaStore.detail_key (PersonaDetail.NICKNAME),
(owned) nick_v);
var nick_chunk = contact.create_chunk ("nickname", null);
nick_chunk.nickname = nickname;
}
// Handles the "BDAY" (birthday) attribute
private void handle_bday (HashTable<string, Value?> details,
E.VCardAttribute attr) {
// Get the attribute valuec
private void handle_bday (Contact contact, E.VCardAttribute attr) {
var bday = attr.get_value ();
// Parse it using the logic in E.ContactDate
var e_date = E.ContactDate.from_string (bday);
// Turn it into a GLib.DateTime
var datetime = new DateTime.utc ((int) e_date.year,
(int) e_date.month,
(int) e_date.day,
0, 0, 0.0);
// Insert it into the hashtable as a GLib.Value
Value? bday_val = Value (typeof (DateTime));
bday_val.take_boxed ((owned) datetime);
details.insert (Folks.PersonaStore.detail_key (PersonaDetail.BIRTHDAY),
(owned) bday_val);
var bd_chunk = contact.create_chunk ("birthday", null);
bd_chunk.birthday = birthday;
}
private void handle_email (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_email (Contact contact, E.VCardAttribute attr) {
var email = attr.get_value ();
if (email == null || email == "")
return;
var email_fd = new EmailFieldDetails (email);
add_params (email_fd, attr);
insert_field_details<EmailFieldDetails> (details, PersonaDetail.EMAIL_ADDRESSES,
email_fd,
AbstractFieldDetails<string>.hash_static,
AbstractFieldDetails<string>.equal_static);
var child = add_chunk_child_for_property (contact, "email-addresses");
((EmailAddress) child).raw_address = email;
add_params (child, attr);
}
private void handle_tel (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_tel (Contact contact, E.VCardAttribute attr) {
var phone_nr = attr.get_value ();
if (phone_nr == null || phone_nr == "")
return;
var phone_fd = new PhoneFieldDetails (phone_nr);
add_params (phone_fd, attr);
insert_field_details<PhoneFieldDetails> (details, PersonaDetail.PHONE_NUMBERS,
phone_fd,
AbstractFieldDetails<string>.hash_static,
AbstractFieldDetails<string>.equal_static);
var child = add_chunk_child_for_property (contact, "phone-numbers");
((Phone) child).raw_number = phone_nr;
add_params (child, attr);
}
// Handles the ADR (postal address) attributes
private void handle_adr (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_adr (Contact contact, E.VCardAttribute attr) {
unowned var values = attr.get_values ();
var child = add_chunk_child_for_property (contact, "postal-addresses");
unowned var address = ((Addresss) child).address;
// From the VCard spec:
// ADR-value = ADR-component-pobox ";" ADR-component-ext ";"
// ADR-component-street ";" ADR-component-locality ";"
// ADR-component-region ";" ADR-component-code ";"
// ADR-component-country
unowned var po_box = values.nth_data (0) ?? "";
unowned var extension = values.nth_data (1) ?? "";
unowned var street = values.nth_data (2) ?? "";
unowned var locality = values.nth_data (3) ?? "";
unowned var region = values.nth_data (4) ?? "";
unowned var postal_code = values.nth_data (5) ?? "";
unowned var country = values.nth_data (6) ?? "";
var addr = new PostalAddress (po_box, extension, street, locality, region,
postal_code, country, "", null);
var addr_fd = new PostalAddressFieldDetails ((owned) addr);
add_params (addr_fd, attr);
insert_field_details<PostalAddressFieldDetails> (details,
PersonaDetail.POSTAL_ADDRESSES,
addr_fd,
AbstractFieldDetails<PostalAddress>.hash_static,
AbstractFieldDetails<PostalAddress>.equal_static);
address.po_box = values.nth_data (0) ?? "";
address.extension = values.nth_data (1) ?? "";
address.street = values.nth_data (2) ?? "";
address.locality = values.nth_data (3) ?? "";
address.region = values.nth_data (4) ?? "";
address.postal_code = values.nth_data (5) ?? "";
address.country = values.nth_data (6) ?? "";
add_params (child, attr);
}
private void handle_url (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_url (Contact contact, E.VCardAttribute attr) {
var url = attr.get_value ();
if (url == null || url == "")
return;
var url_fd = new UrlFieldDetails (url);
add_params (url_fd, attr);
insert_field_details<UrlFieldDetails> (details, PersonaDetail.URLS,
url_fd,
AbstractFieldDetails<string>.hash_static,
AbstractFieldDetails<string>.equal_static);
var child = add_chunk_child_for_property (contact, "urls");
((Contacts.Url) child).url = url;
add_params (child, attr);
}
private void handle_note (HashTable<string, Value?> details,
E.VCardAttribute attr) {
private void handle_note (Contact contact, E.VCardAttribute attr) {
var note = attr.get_value ();
if (note == null || note == "")
return;
var note_fd = new NoteFieldDetails (note);
add_params (note_fd, attr);
insert_field_details<NoteFieldDetails> (details, PersonaDetail.NOTES,
note_fd,
AbstractFieldDetails<string>.hash_static,
AbstractFieldDetails<string>.equal_static);
var child = add_chunk_child_for_property (contact, "notes");
((Contacts.Note) child).text = note;
add_params (child, attr);
}
// Helper method for inserting aggregated properties
private bool insert_field_details<T> (HashTable<string, Value?> details,
PersonaDetail key,
T field_details,
owned Gee.HashDataFunc<T>? hash_func,
owned Gee.EqualDataFunc<T>? equal_func) {
// Get the existing set, or create a new one and add it
unowned var old_val = details.lookup (Folks.PersonaStore.detail_key (key));
if (old_val != null) {
unowned var values = old_val as Gee.HashSet<T>;
return values.add (field_details);
}
var values = new Gee.HashSet<T> ((owned) hash_func, (owned) equal_func);
Value? new_val = Value (typeof (Gee.Set));
new_val.set_object (values);
details.insert (Folks.PersonaStore.detail_key (key), (owned) new_val);
return values.add (field_details);
private void add_chunk_child_for_property (Contact contact,
string property_name) {
var chunk = contact.get_most_relevant_chunk (property_name, true);
if (chunk == null)
chunk = contact.create_chunk (property_name, null);
return_if_fail (chunk is BinChunk);
return ((BinChunk) chunk).create_empty_child ();
}
// Helper method to get VCard parameters into an AbstractFieldDetails object.
// Helper method to get VCard parameters into a BinChunkChild
// Will take care of setting the correct "type"
private void add_params (AbstractFieldDetails details, E.VCardAttribute attr) {
private void add_params (BinChunkChild chunk_cild, E.VCardAttribute attr) {
foreach (unowned E.VCardAttributeParam param in attr.get_params ()) {
string param_name = param.get_name ().down ();
foreach (unowned string param_value in param.get_values ()) {
if (param_name == AbstractFieldDetails.PARAM_TYPE)
details.add_parameter (param_name, param_value.down ());
if (param_name == "type")
chunk_child.parameters.add (param_name, param_value.down ());
else