~ubuntu-branches/ubuntu/precise/folks/precise

« back to all changes in this revision

Viewing changes to backends/tracker/lib/trf-persona.vala

  • Committer: Bazaar Package Importer
  • Author(s): Ken VanDine
  • Date: 2011-06-10 11:28:11 UTC
  • mfrom: (1.2.11 upstream) (4.2.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20110610112811-whyeodbo9mjezxfp
Tags: 0.5.2-1ubuntu1
* Merge with Debian experimental, remaining Ubuntu changes:
  - debian/control:
    + Add Vcs-Bzr link

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Collabora Ltd.
 
3
 *
 
4
 * This library is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU Lesser General Public License as published by
 
6
 * the Free Software Foundation, either version 2.1 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public License
 
15
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
16
 *
 
17
 * Authors:
 
18
 *         Travis Reitter <travis.reitter@collabora.co.uk>
 
19
 *         Marco Barisione <marco.barisione@collabora.co.uk>
 
20
 *         Raul Gutierrez Segales <raul.gutierrez.segales@collabora.co.uk>
 
21
 */
 
22
 
 
23
using Gee;
 
24
using GLib;
 
25
using Folks;
 
26
using Tracker;
 
27
using Tracker.Sparql;
 
28
 
 
29
/**
 
30
 * A persona subclass which represents a single nco:Contact.
 
31
 */
 
32
public class Trf.Persona : Folks.Persona,
 
33
    AliasDetails,
 
34
    AvatarDetails,
 
35
    BirthdayDetails,
 
36
    EmailDetails,
 
37
    FavouriteDetails,
 
38
    GenderDetails,
 
39
    ImDetails,
 
40
    LocalIdDetails,
 
41
    NameDetails,
 
42
    NoteDetails,
 
43
    PhoneDetails,
 
44
    PostalAddressDetails,
 
45
    RoleDetails,
 
46
    UrlDetails,
 
47
    WebServiceDetails
 
48
{
 
49
  private string _alias;
 
50
  private bool _is_favourite;
 
51
  private const string[] _linkable_properties =
 
52
      {"im-addresses", "local-ids", "web-service-addresses"};
 
53
  private HashSet<FieldDetails> _phone_numbers;
 
54
  private Set<FieldDetails> _phone_numbers_ro;
 
55
  private HashSet<FieldDetails> _email_addresses;
 
56
  private Set<FieldDetails> _email_addresses_ro;
 
57
  private weak Sparql.Cursor _cursor;
 
58
  private string _tracker_id;
 
59
 
 
60
  /**
 
61
   * An alias for the Persona.
 
62
   *
 
63
   * See {@link Folks.AliasDetails.alias}.
 
64
   */
 
65
  public string alias
 
66
    {
 
67
      get { return this._alias; }
 
68
 
 
69
      set
 
70
        {
 
71
          if (this._alias == value)
 
72
            return;
 
73
          this._alias = value;
 
74
          this.notify_property ("alias");
 
75
          ((Trf.PersonaStore) this.store)._set_alias (this, value);
 
76
        }
 
77
    }
 
78
 
 
79
  /**
 
80
   * {@inheritDoc}
 
81
   */
 
82
  public Set<FieldDetails> phone_numbers
 
83
    {
 
84
      get { return this._phone_numbers_ro; }
 
85
      public set
 
86
        {
 
87
          ((Trf.PersonaStore) this.store)._set_phones (this, value);
 
88
        }
 
89
    }
 
90
 
 
91
  /**
 
92
   * {@inheritDoc}
 
93
   */
 
94
  public Set<FieldDetails> email_addresses
 
95
    {
 
96
      get { return this._email_addresses_ro; }
 
97
      public set
 
98
        {
 
99
          ((Trf.PersonaStore) this.store)._set_emails (this, value);
 
100
        }
 
101
    }
 
102
 
 
103
  /**
 
104
   * {@inheritDoc}
 
105
   */
 
106
  public override string[] linkable_properties
 
107
    {
 
108
      get { return this._linkable_properties; }
 
109
    }
 
110
 
 
111
  private File _avatar;
 
112
  /**
 
113
   * An avatar for the Persona.
 
114
   *
 
115
   * See {@link Folks.Avatar.avatar}.
 
116
   */
 
117
  public File avatar
 
118
    {
 
119
      get { return this._avatar; }
 
120
      public set
 
121
        {
 
122
          ((Trf.PersonaStore) this.store)._set_avatar (this, value);
 
123
        }
 
124
    }
 
125
 
 
126
  private StructuredName _structured_name;
 
127
  /**
 
128
   * {@inheritDoc}
 
129
   */
 
130
  public StructuredName structured_name
 
131
    {
 
132
      get { return this._structured_name; }
 
133
      public set
 
134
        {
 
135
          ((Trf.PersonaStore) this.store)._set_structured_name (this, value);
 
136
        }
 
137
    }
 
138
 
 
139
  private string _full_name;
 
140
  /**
 
141
   * {@inheritDoc}
 
142
   */
 
143
  public string full_name
 
144
    {
 
145
      get { return this._full_name; }
 
146
      public set
 
147
        {
 
148
          ((Trf.PersonaStore) this.store)._set_full_name (this, value);
 
149
        }
 
150
    }
 
151
 
 
152
  private string _nickname;
 
153
  /**
 
154
   * {@inheritDoc}
 
155
   */
 
156
  public string nickname { get { return this._nickname; } }
 
157
 
 
158
  private Gender _gender;
 
159
  /**
 
160
   * {@inheritDoc}
 
161
   */
 
162
  public Gender gender
 
163
    {
 
164
      get { return this._gender; }
 
165
      public set
 
166
        {
 
167
          ((Trf.PersonaStore) this.store)._set_gender (this, value);
 
168
        }
 
169
    }
 
170
 
 
171
  private DateTime _birthday;
 
172
  /**
 
173
   * {@inheritDoc}
 
174
   */
 
175
  public DateTime birthday
 
176
    {
 
177
      get { return this._birthday; }
 
178
      public set
 
179
        {
 
180
          ((Trf.PersonaStore) this.store)._set_birthday (this, value);
 
181
        }
 
182
    }
 
183
 
 
184
  public string calendar_event_id { get; set; }
 
185
 
 
186
  private HashSet<Role> _roles;
 
187
  private Set<Role> _roles_ro;
 
188
 
 
189
  /**
 
190
   * {@inheritDoc}
 
191
   */
 
192
  public Set<Role> roles
 
193
    {
 
194
      get { return this._roles_ro; }
 
195
      public set
 
196
        {
 
197
          ((Trf.PersonaStore) this.store)._set_roles (this, value);
 
198
        }
 
199
    }
 
200
 
 
201
  private HashSet<Note> _notes;
 
202
  private Set<Note> _notes_ro;
 
203
 
 
204
  /**
 
205
   * {@inheritDoc}
 
206
   */
 
207
  public Set<Note> notes
 
208
    {
 
209
      get { return this._notes_ro; }
 
210
      private set
 
211
        {
 
212
          ((Trf.PersonaStore) this.store)._set_notes (this, value);
 
213
        }
 
214
    }
 
215
 
 
216
  private HashSet<FieldDetails> _urls;
 
217
  private Set<FieldDetails> _urls_ro;
 
218
 
 
219
  /**
 
220
   * {@inheritDoc}
 
221
   */
 
222
  public Set<FieldDetails> urls
 
223
    {
 
224
      get { return this._urls_ro; }
 
225
      public set
 
226
        {
 
227
          ((Trf.PersonaStore) this.store)._set_urls (this, value);
 
228
        }
 
229
    }
 
230
 
 
231
  private HashSet<PostalAddress> _postal_addresses;
 
232
  private Set<PostalAddress> _postal_addresses_ro;
 
233
 
 
234
  /**
 
235
   * {@inheritDoc}
 
236
   */
 
237
  public Set<PostalAddress> postal_addresses
 
238
    {
 
239
      get { return this._postal_addresses_ro; }
 
240
      private set
 
241
        {
 
242
          ((Trf.PersonaStore) this.store)._set_postal_addresses (this, value);
 
243
        }
 
244
    }
 
245
 
 
246
  private HashTable<string, HashTable<string, string>> _tracker_ids_ims =
 
247
  new HashTable<string, HashTable<string, string>> (str_hash, str_equal);
 
248
 
 
249
  private HashMultiMap<string, string> _im_addresses =
 
250
      new HashMultiMap<string, string> ();
 
251
 
 
252
  /**
 
253
   * {@inheritDoc}
 
254
   */
 
255
  public MultiMap<string, string> im_addresses
 
256
    {
 
257
      get { return this._im_addresses; }
 
258
      public set
 
259
        {
 
260
          ((Trf.PersonaStore) this.store)._set_im_addresses (this,
 
261
              value);
 
262
        }
 
263
    }
 
264
 
 
265
  /**
 
266
   * Whether this contact is a user-defined favourite.
 
267
   */
 
268
  public bool is_favourite
 
269
      {
 
270
        get { return this._is_favourite; }
 
271
 
 
272
        set
 
273
          {
 
274
            if (this._is_favourite == value)
 
275
              return;
 
276
 
 
277
            /* Note:
 
278
             * this property will be set (and notified)
 
279
             * once we receive a notification event from Tracker
 
280
             */
 
281
            ((Trf.PersonaStore) this.store)._set_is_favourite (this, value);
 
282
          }
 
283
      }
 
284
 
 
285
  private HashSet<string> _local_ids;
 
286
  private Set<string> _local_ids_ro;
 
287
 
 
288
  /**
 
289
   * IDs used to link {@link Trf.Persona}s.
 
290
   */
 
291
  public Set<string> local_ids
 
292
    {
 
293
      get
 
294
        {
 
295
          if (this._local_ids.contains (this.uid) == false)
 
296
            {
 
297
              this._local_ids.add (this.uid);
 
298
            }
 
299
          return this._local_ids_ro;
 
300
        }
 
301
      set
 
302
        {
 
303
          if (value.contains (this.uid) == false)
 
304
            {
 
305
              value.add (this.uid);
 
306
            }
 
307
          ((Trf.PersonaStore) this.store)._set_local_ids (this, value);
 
308
        }
 
309
    }
 
310
 
 
311
  private HashMultiMap<string, string> _web_service_addresses =
 
312
      new HashMultiMap<string, string> ();
 
313
 
 
314
  /**
 
315
   * {@inheritDoc}
 
316
   */
 
317
  public MultiMap<string, string> web_service_addresses
 
318
    {
 
319
      get { return this._web_service_addresses; }
 
320
      set
 
321
        {
 
322
          ((Trf.PersonaStore) this.store)._set_web_service_addrs (this, value);
 
323
        }
 
324
    }
 
325
 
 
326
  /**
 
327
   * Build a IID.
 
328
   *
 
329
   * @param store_id the {@link PersonaStore.id}
 
330
   * @param tracker_id the tracker id belonging to nco:PersonContact
 
331
   * @return a valid IID
 
332
   *
 
333
   * @since 0.5.0
 
334
   */
 
335
  internal static string build_iid (string store_id, string tracker_id)
 
336
    {
 
337
      return "%s:%s".printf (store_id, tracker_id);
 
338
    }
 
339
 
 
340
  /**
 
341
   * Create a new persona.
 
342
   *
 
343
   * Create a new persona for the {@link PersonaStore} `store`, representing
 
344
   * the nco:Contact whose details are stored in the cursor.
 
345
   */
 
346
  public Persona (PersonaStore store, string tracker_id,
 
347
                  Sparql.Cursor? cursor = null)
 
348
    {
 
349
      string uid = this.build_uid (BACKEND_NAME, store.id, tracker_id);
 
350
      string iid = this.build_iid (store.id, tracker_id);
 
351
      bool is_user = false;
 
352
      string fullname = "";
 
353
 
 
354
      if (cursor != null)
 
355
        {
 
356
          fullname = cursor.get_string (Trf.Fields.FULL_NAME).dup ();
 
357
          var contact_urn = cursor.get_string (Trf.Fields.CONTACT_URN);
 
358
          if (contact_urn == Trf.OntologyDefs.DEFAULT_CONTACT_URN)
 
359
            {
 
360
              is_user = true;
 
361
            }
 
362
        }
 
363
 
 
364
      debug ("Creating new Trf.Persona with iid '%s'", iid);
 
365
 
 
366
      Object (display_id: fullname,
 
367
              uid: uid,
 
368
              iid: iid,
 
369
              store: store,
 
370
              is_user: is_user);
 
371
 
 
372
      this._gender = Gender.UNSPECIFIED;
 
373
      this._full_name = fullname;
 
374
      this._tracker_id = tracker_id;
 
375
      this._structured_name = new StructuredName (null, null, null, null, null);
 
376
      this._phone_numbers = new HashSet<FieldDetails> ();
 
377
      this._phone_numbers_ro = this._phone_numbers.read_only_view;
 
378
      this._email_addresses = new HashSet<FieldDetails> ();
 
379
      this._email_addresses_ro = this._email_addresses.read_only_view;
 
380
      this._roles = new HashSet<Role> ((GLib.HashFunc) Role.hash,
 
381
          (GLib.EqualFunc) Role.equal);
 
382
      this._roles_ro = this._roles.read_only_view;
 
383
      this._notes = new HashSet<Note> ((GLib.HashFunc) Note.hash,
 
384
          (GLib.EqualFunc) Note.equal);
 
385
      this._notes_ro = this._notes.read_only_view;
 
386
      this._urls = new HashSet<FieldDetails> ();
 
387
      this._urls_ro = this._urls.read_only_view;
 
388
      this._postal_addresses = new HashSet<PostalAddress> ();
 
389
      this._postal_addresses_ro = this._postal_addresses.read_only_view;
 
390
      this._local_ids = new HashSet<string> ();
 
391
      this._local_ids_ro = this._local_ids.read_only_view;
 
392
 
 
393
      if (cursor != null)
 
394
        {
 
395
          this._cursor = cursor;
 
396
          this._update ();
 
397
        }
 
398
    }
 
399
 
 
400
  internal string tracker_id ()
 
401
    {
 
402
      return this._tracker_id;
 
403
    }
 
404
 
 
405
  /**
 
406
   * {@inheritDoc}
 
407
   */
 
408
  public override void linkable_property_to_links (string prop_name,
 
409
      Folks.Persona.LinkablePropertyCallback callback)
 
410
    {
 
411
      if (prop_name == "im-addresses")
 
412
        {
 
413
          foreach (var protocol in this._im_addresses.get_keys ())
 
414
            {
 
415
              var im_addresses = this._im_addresses.get (protocol);
 
416
 
 
417
              foreach (string address in im_addresses)
 
418
                  callback (protocol + ":" + address);
 
419
            }
 
420
        }
 
421
      else if (prop_name == "local-ids")
 
422
        {
 
423
          foreach (var id in this._local_ids)
 
424
            {
 
425
              callback (id);
 
426
            }
 
427
        }
 
428
      else if (prop_name == "web-service-addresses")
 
429
        {
 
430
          foreach (var web_service in this._web_service_addresses.get_keys ())
 
431
            {
 
432
              var web_service_addresses =
 
433
                  this._web_service_addresses.get (web_service);
 
434
 
 
435
              foreach (string address in web_service_addresses)
 
436
                  callback (web_service + ":" + address);
 
437
            }
 
438
        }
 
439
      else
 
440
        {
 
441
          /* Chain up */
 
442
          base.linkable_property_to_links (prop_name, callback);
 
443
        }
 
444
    }
 
445
 
 
446
  ~Persona ()
 
447
    {
 
448
      debug ("Destroying Trf.Persona '%s': %p", this.uid, this);
 
449
    }
 
450
 
 
451
  internal void _update_full_name (string? fn)
 
452
    {
 
453
      if (fn != null && this.full_name != fn)
 
454
        {
 
455
          this._full_name = fn;
 
456
          this.notify_property ("full-name");
 
457
        }
 
458
    }
 
459
 
 
460
  internal void _update_nickname (string? nickname)
 
461
    {
 
462
      if (nickname != null && this._nickname != nickname)
 
463
        {
 
464
          this._nickname = nickname;
 
465
          this.notify_property ("nickname");
 
466
        }
 
467
    }
 
468
 
 
469
  internal void _update_alias (string? alias)
 
470
    {
 
471
      if (alias != null && this._alias != alias)
 
472
        {
 
473
          this._alias = alias;
 
474
          this.notify_property ("alias");
 
475
        }
 
476
    }
 
477
 
 
478
  internal void _update_family_name (string? family_name)
 
479
    {
 
480
      if (family_name != null)
 
481
        {
 
482
          this._structured_name.family_name = family_name;
 
483
          this.notify_property ("structured-name");
 
484
        }
 
485
    }
 
486
 
 
487
  internal void _update_given_name (string? given_name)
 
488
    {
 
489
      if (given_name != null)
 
490
        {
 
491
          this._structured_name.given_name = given_name;
 
492
          this.notify_property ("structured-name");
 
493
        }
 
494
    }
 
495
 
 
496
  internal void _update_additional_names (string? additional_names)
 
497
    {
 
498
      if (additional_names != null)
 
499
        {
 
500
          this._structured_name.additional_names = additional_names;
 
501
          this.notify_property ("structured-name");
 
502
        }
 
503
    }
 
504
 
 
505
  internal void _update_prefixes (string? prefixes)
 
506
    {
 
507
      if (prefixes != null)
 
508
        {
 
509
          this._structured_name.prefixes = prefixes;
 
510
          this.notify_property ("structured-name");
 
511
        }
 
512
    }
 
513
 
 
514
  internal void _update_suffixes (string? suffixes)
 
515
    {
 
516
      if (suffixes != null)
 
517
        {
 
518
          this._structured_name.suffixes = suffixes;
 
519
          this.notify_property ("structured-name");
 
520
        }
 
521
    }
 
522
 
 
523
  internal void _update ()
 
524
    {
 
525
      this._update_names ();
 
526
      this._update_avatar ();
 
527
      this._update_im_addresses ();
 
528
      this._update_phones ();
 
529
      this._update_email_addresses ();
 
530
      this._update_urls ();
 
531
      this._update_favourite ();
 
532
      this._update_roles ();
 
533
      this._update_bday ();
 
534
      this._update_note ();
 
535
      this._update_gender ();
 
536
      this._update_postal_addresses ();
 
537
      this._update_local_ids ();
 
538
    }
 
539
 
 
540
  private void _update_postal_addresses ()
 
541
    {
 
542
      string postal_field = this._cursor.get_string
 
543
          (Trf.Fields.POSTAL_ADDRESS).dup ();
 
544
 
 
545
      if (postal_field == null)
 
546
        {
 
547
          return;
 
548
        }
 
549
 
 
550
      var postal_addresses = new HashSet<PostalAddress> ();
 
551
 
 
552
      string[] addresses_a = postal_field.split ("\n");
 
553
 
 
554
      foreach (var a in addresses_a)
 
555
        {
 
556
          bool address_empty = true;
 
557
          string[] a_info = a.split ("\t");
 
558
          for (int i = 0; i < a_info.length; i++)
 
559
            {
 
560
              if (a_info[i] != null && a_info[i] != "")
 
561
                {
 
562
                  address_empty = false;
 
563
                  break;
 
564
                }
 
565
            }
 
566
 
 
567
          if (address_empty)
 
568
            continue;
 
569
 
 
570
          var types = new HashSet<string> ();
 
571
 
 
572
          var pa = new PostalAddress (a_info[Trf.PostalAddressFields.POBOX],
 
573
              a_info[Trf.PostalAddressFields.EXTENDED_ADDRESS],
 
574
              a_info[Trf.PostalAddressFields.STREET_ADDRESS],
 
575
              a_info[Trf.PostalAddressFields.LOCALITY],
 
576
              a_info[Trf.PostalAddressFields.REGION],
 
577
              a_info[Trf.PostalAddressFields.POSTALCODE],
 
578
              a_info[Trf.PostalAddressFields.COUNTRY],
 
579
              null, types,
 
580
              a_info[Trf.PostalAddressFields.TRACKER_ID]);
 
581
 
 
582
          postal_addresses.add (pa);
 
583
        }
 
584
 
 
585
      this._postal_addresses = postal_addresses;
 
586
      this._postal_addresses_ro = this._postal_addresses.read_only_view;
 
587
    }
 
588
 
 
589
 private void _update_local_ids ()
 
590
    {
 
591
      string local_ids = this._cursor.get_string
 
592
          (Trf.Fields.LOCAL_IDS_PROPERTY).dup ();
 
593
 
 
594
     if (local_ids == null)
 
595
        {
 
596
          return;
 
597
        }
 
598
 
 
599
      this._set_local_ids (local_ids);
 
600
    }
 
601
 
 
602
  internal bool _add_postal_address (PostalAddress postal_address)
 
603
    {
 
604
      foreach (var pa in this._postal_addresses)
 
605
        {
 
606
          if (postal_address.equal (pa))
 
607
            {
 
608
              return false;
 
609
            }
 
610
        }
 
611
 
 
612
      this._postal_addresses.add (postal_address);
 
613
      this.notify_property ("postal-addresses");
 
614
      return true;
 
615
    }
 
616
 
 
617
  internal bool _remove_postal_address (string tracker_id)
 
618
    {
 
619
      foreach (var pa in this._postal_addresses)
 
620
        {
 
621
          if (pa.uid == tracker_id)
 
622
            {
 
623
              this._postal_addresses.remove (pa);
 
624
              this.notify_property ("postal-addresses");
 
625
              return true;
 
626
            }
 
627
        }
 
628
      return false;
 
629
    }
 
630
 
 
631
  private void _update_gender ()
 
632
    {
 
633
      string gender = this._cursor.get_string (Trf.Fields.GENDER).dup ();
 
634
      int gender_id = 0;
 
635
 
 
636
      if (gender != null)
 
637
        {
 
638
          gender_id = int.parse (gender);
 
639
        }
 
640
 
 
641
      this._set_gender (gender_id);
 
642
    }
 
643
 
 
644
  internal void _set_gender (int gender_id)
 
645
    {
 
646
      if (gender_id == 0)
 
647
        {
 
648
          this._gender = Gender.UNSPECIFIED;
 
649
        }
 
650
      else
 
651
        {
 
652
          var trf_store = (Trf.PersonaStore) this.store;
 
653
 
 
654
          if (gender_id == trf_store.get_gender_male_id ())
 
655
            {
 
656
              this._gender = Gender.MALE;
 
657
            }
 
658
          else if (gender_id == trf_store.get_gender_female_id ())
 
659
            {
 
660
              this._gender = Gender.FEMALE;
 
661
            }
 
662
        }
 
663
 
 
664
      this.notify_property ("gender");
 
665
    }
 
666
 
 
667
  private void _update_note ()
 
668
    {
 
669
      string note = this._cursor.get_string (Trf.Fields.NOTE).dup ();
 
670
      this._set_note (note);
 
671
    }
 
672
 
 
673
  internal void _set_note (string? note_content)
 
674
    {
 
675
      if (note_content != null)
 
676
        {
 
677
          var note = new Note (note_content);
 
678
          this._notes.add ((owned) note);
 
679
        }
 
680
      else
 
681
        {
 
682
          this._notes.clear ();
 
683
        }
 
684
      this.notify_property ("notes");
 
685
    }
 
686
 
 
687
  private void _update_bday ()
 
688
    {
 
689
      string bday = this._cursor.get_string (Trf.Fields.BIRTHDAY).dup ();
 
690
      this._set_birthday (bday);
 
691
    }
 
692
 
 
693
  internal void _set_birthday (string? birthday)
 
694
    {
 
695
      if (birthday != null && birthday != "")
 
696
        {
 
697
          TimeVal t = TimeVal ();
 
698
          t.from_iso8601 (birthday);
 
699
          this._birthday = new DateTime.from_timeval_utc (t);
 
700
          this.notify_property ("birthday");
 
701
        }
 
702
      else
 
703
        {
 
704
          if (this._birthday != null)
 
705
            {
 
706
              this._birthday = null;
 
707
              this.notify_property ("birthday");
 
708
            }
 
709
        }
 
710
    }
 
711
 
 
712
  private void _update_roles ()
 
713
    {
 
714
      string roles_field = this._cursor.get_string (
 
715
          Trf.Fields.ROLES).dup ();
 
716
 
 
717
      if (roles_field == null)
 
718
        {
 
719
          return;
 
720
        }
 
721
 
 
722
      HashSet<Role> roles = new HashSet<Role> (
 
723
          (GLib.HashFunc) Role.hash,
 
724
          (GLib.EqualFunc) Role.equal);
 
725
 
 
726
      string[] roles_a = roles_field.split ("\n");
 
727
 
 
728
      foreach (var r in roles_a)
 
729
        {
 
730
          string[] r_info = r.split ("\t");
 
731
          var tracker_id = r_info[Trf.RoleFields.TRACKER_ID];
 
732
          var title = r_info[Trf.RoleFields.ROLE];
 
733
          var organisation = r_info[Trf.RoleFields.DEPARTMENT];
 
734
 
 
735
          var role = new Role (title, organisation, tracker_id);
 
736
          roles.add (role);
 
737
        }
 
738
 
 
739
      this._roles = roles;
 
740
      this._roles_ro = this._roles.read_only_view;
 
741
    }
 
742
 
 
743
  internal bool _add_role (string tracker_id, string? title, string? org)
 
744
    {
 
745
      var role = new Role (title, org, tracker_id);
 
746
      if (this._roles.add (role))
 
747
        {
 
748
          this.notify_property ("roles");
 
749
          return true;
 
750
        }
 
751
      return false;
 
752
    }
 
753
 
 
754
  internal bool _remove_role (string tracker_id)
 
755
    {
 
756
      foreach (var r in this._roles)
 
757
        {
 
758
          if (r.uid == tracker_id)
 
759
            {
 
760
              this._roles.remove (r);
 
761
              this.notify_property ("roles");
 
762
              return true;
 
763
            }
 
764
        }
 
765
 
 
766
      return false;
 
767
    }
 
768
 
 
769
  private void _update_names ()
 
770
    {
 
771
      string fullname = this._cursor.get_string (Trf.Fields.FULL_NAME).dup ();
 
772
      this._update_full_name (fullname);
 
773
 
 
774
      string alias = this._cursor.get_string (Trf.Fields.ALIAS).dup ();
 
775
      this._update_alias (alias);
 
776
 
 
777
      string family_name = this._cursor.get_string (
 
778
          Trf.Fields.FAMILY_NAME).dup ();
 
779
      this._update_family_name (family_name);
 
780
 
 
781
      string given_name  = this._cursor.get_string (
 
782
          Trf.Fields.GIVEN_NAME).dup ();
 
783
      this._update_given_name (given_name);
 
784
 
 
785
      string additional_names = this._cursor.get_string (
 
786
          Trf.Fields.ADDITIONAL_NAMES).dup ();
 
787
      this._update_additional_names (additional_names);
 
788
 
 
789
      string prefixes = this._cursor.get_string (Trf.Fields.PREFIXES).dup ();
 
790
      this._update_prefixes (prefixes);
 
791
 
 
792
      string suffixes = this._cursor.get_string (Trf.Fields.SUFFIXES).dup ();
 
793
      this._update_suffixes (suffixes);
 
794
    }
 
795
 
 
796
  private void _update_avatar ()
 
797
    {
 
798
      string avatar_url = this._cursor.get_string (
 
799
          Trf.Fields.AVATAR_URL).dup ();
 
800
      this._set_avatar (avatar_url);
 
801
    }
 
802
 
 
803
  internal bool _set_avatar (string? avatar_url)
 
804
    {
 
805
      File _avatar = null;
 
806
      if (avatar_url != null && avatar_url != "")
 
807
        {
 
808
          _avatar = File.new_for_uri (avatar_url);
 
809
        }
 
810
      this._avatar = _avatar;
 
811
      this.notify_property ("avatar");
 
812
      return true;
 
813
    }
 
814
 
 
815
  internal bool _set_local_ids (string local_ids)
 
816
    {
 
817
      this._local_ids =
 
818
          (HashSet<string>) Trf.PersonaStore.unserialize_local_ids (local_ids);
 
819
      this._local_ids_ro = this._local_ids.read_only_view;
 
820
      this.notify_property ("local-ids");
 
821
      return true;
 
822
    }
 
823
 
 
824
  internal bool _set_web_service_addrs (string ws_addrs)
 
825
    {
 
826
      this._web_service_addresses =
 
827
        (HashMultiMap<string, string>)
 
828
            Trf.PersonaStore.unserialize_web_services (ws_addrs);
 
829
      this.notify_property ("web-service-addresses");
 
830
      return true;
 
831
    }
 
832
 
 
833
  private void _update_im_addresses ()
 
834
    {
 
835
      string addresses = this._cursor.get_string (
 
836
          Trf.Fields.IM_ADDRESSES).dup ();
 
837
 
 
838
      if (addresses == null)
 
839
        {
 
840
          return;
 
841
        }
 
842
 
 
843
      this._im_addresses.clear ();
 
844
 
 
845
      string[] addresses_a = addresses.split ("\n");
 
846
 
 
847
      foreach (var addr in addresses_a)
 
848
        {
 
849
          string[] addr_info = addr.split ("\t");
 
850
          var tracker_id = addr_info[Trf.IMFields.TRACKER_ID];
 
851
          var proto = addr_info[Trf.IMFields.PROTO];
 
852
          var account_id = addr_info[Trf.IMFields.ID];
 
853
          var nickname = addr_info[Trf.IMFields.IM_NICKNAME];
 
854
 
 
855
          this._update_nickname (nickname);
 
856
          this._add_im_address (tracker_id, proto, account_id, false);
 
857
        }
 
858
 
 
859
      this.notify_property ("im-addresses");
 
860
    }
 
861
 
 
862
  internal bool _add_im_address (string tracker_id, string im_proto,
 
863
      string account_id, bool notify = true)
 
864
    {
 
865
      try
 
866
        {
 
867
          var account_id_copy = account_id.dup ();
 
868
          var normalised_addr = (owned) normalise_im_address
 
869
              ((owned) account_id_copy, im_proto);
 
870
 
 
871
          this._im_addresses.set (im_proto, normalised_addr);
 
872
 
 
873
          var im_proto_hash = new HashTable<string, string> (str_hash,
 
874
              str_equal);
 
875
          var proto_copy_2 = im_proto.dup ();
 
876
          var account_id_copy_2 = account_id.dup ();
 
877
          im_proto_hash.insert ((owned) proto_copy_2,
 
878
              (owned) account_id_copy_2);
 
879
          var tracker_id_copy = tracker_id.dup ();
 
880
          this._tracker_ids_ims.insert ((owned) tracker_id_copy,
 
881
                  (owned) im_proto_hash);
 
882
 
 
883
          if (notify)
 
884
            {
 
885
              this.notify_property ("im-addresses");
 
886
            }
 
887
        }
 
888
      catch (Folks.ImDetailsError e)
 
889
        {
 
890
          GLib.warning (
 
891
              "Problem when trying to normalise address: %s\n",
 
892
              e.message);
 
893
        }
 
894
 
 
895
      return true;
 
896
    }
 
897
 
 
898
  internal bool _remove_im_address (string tracker_id, bool notify = true)
 
899
    {
 
900
      var proto_im = this._tracker_ids_ims.lookup (tracker_id);
 
901
 
 
902
      if (proto_im == null)
 
903
        return false;
 
904
 
 
905
      var proto = proto_im.get_keys ().nth_data (0);
 
906
      var im_addr = proto_im.lookup (proto);
 
907
 
 
908
      if (this._im_addresses.remove (proto, im_addr))
 
909
        {
 
910
          this._tracker_ids_ims.remove (tracker_id);
 
911
          if (notify)
 
912
            {
 
913
              this.notify_property ("im-addresses");
 
914
            }
 
915
 
 
916
          return true;
 
917
        }
 
918
 
 
919
      return false;
 
920
    }
 
921
 
 
922
  private void _update_phones ()
 
923
    {
 
924
      string phones_field = this._cursor.get_string (Trf.Fields.PHONES).dup ();
 
925
 
 
926
      if (phones_field == null)
 
927
        {
 
928
          return;
 
929
        }
 
930
 
 
931
      var phones = new HashSet<FieldDetails> ();
 
932
      string[] phones_a = phones_field.split ("\n");
 
933
 
 
934
      foreach (var p in phones_a)
 
935
        {
 
936
          if (p != null && p != "")
 
937
            {
 
938
              string[] p_info = p.split ("\t");
 
939
              var fd = new FieldDetails (p_info[Trf.PhoneFields.PHONE]);
 
940
              fd.set_parameter ("tracker_id",
 
941
                  p_info[Trf.PhoneFields.TRACKER_ID]);
 
942
              phones.add (fd);
 
943
            }
 
944
        }
 
945
 
 
946
      this._phone_numbers = phones;
 
947
      this._phone_numbers_ro = this._phone_numbers.read_only_view;
 
948
    }
 
949
 
 
950
  internal bool _add_phone (string phone, string tracker_id)
 
951
    {
 
952
      bool found = false;
 
953
 
 
954
      foreach (var p in this._phone_numbers)
 
955
        {
 
956
          if (p.get_parameter_values ("tracker_id").contains (tracker_id))
 
957
            {
 
958
              found = true;
 
959
              break;
 
960
            }
 
961
        }
 
962
 
 
963
      if (!found)
 
964
        {
 
965
          var fd = new FieldDetails (phone);
 
966
          fd.set_parameter ("tracker_id", tracker_id);
 
967
          this._phone_numbers.add (fd);
 
968
          this.notify_property ("phone-numbers");
 
969
        }
 
970
 
 
971
      return !found;
 
972
    }
 
973
 
 
974
  internal bool _remove_phone (string tracker_id)
 
975
    {
 
976
      bool found = false;
 
977
 
 
978
      foreach (var p in this._phone_numbers)
 
979
        {
 
980
          if (p.get_parameter_values ("tracker_id").contains (tracker_id))
 
981
            {
 
982
              this._phone_numbers.remove (p);
 
983
              found = true;
 
984
              break;
 
985
            }
 
986
        }
 
987
 
 
988
      if (found)
 
989
       {
 
990
         this.notify_property ("phone-numbers");
 
991
       }
 
992
 
 
993
      return found;
 
994
    }
 
995
 
 
996
  internal bool _add_email (string addr, string tracker_id)
 
997
    {
 
998
      bool found = false;
 
999
 
 
1000
      foreach (var e in this._email_addresses)
 
1001
        {
 
1002
          if (e.get_parameter_values ("tracker_id").contains (tracker_id))
 
1003
            {
 
1004
              found = true;
 
1005
              break;
 
1006
            }
 
1007
        }
 
1008
 
 
1009
      if (!found)
 
1010
        {
 
1011
          var fd = new FieldDetails (addr);
 
1012
          fd.set_parameter ("tracker_id", tracker_id);
 
1013
          this._email_addresses.add (fd);
 
1014
          this.notify_property ("email-addresses");
 
1015
        }
 
1016
 
 
1017
      return !found;
 
1018
    }
 
1019
 
 
1020
  internal bool _remove_email (string tracker_id)
 
1021
    {
 
1022
      bool found = false;
 
1023
 
 
1024
      foreach (var e in this._email_addresses)
 
1025
        {
 
1026
          if (e.get_parameter_values ("tracker_id").contains (tracker_id))
 
1027
            {
 
1028
              this._email_addresses.remove (e);
 
1029
              found = true;
 
1030
              break;
 
1031
            }
 
1032
        }
 
1033
 
 
1034
      if (found)
 
1035
       {
 
1036
         this.notify_property ("email-addresses");
 
1037
       }
 
1038
 
 
1039
      return found;
 
1040
    }
 
1041
 
 
1042
  private void _update_email_addresses ()
 
1043
    {
 
1044
      string emails_field = this._cursor.get_string (Trf.Fields.EMAILS).dup ();
 
1045
 
 
1046
      if (emails_field == null)
 
1047
        {
 
1048
          return;
 
1049
        }
 
1050
 
 
1051
      var email_addresses = new HashSet<FieldDetails> ();
 
1052
      string[] emails_a = emails_field.split (",");
 
1053
 
 
1054
      foreach (var e in emails_a)
 
1055
        {
 
1056
          if (e != null && e != "")
 
1057
            {
 
1058
              string[] id_addr = e.split ("\t");
 
1059
              var fd = new FieldDetails (id_addr[Trf.EmailFields.EMAIL]);
 
1060
              fd.set_parameter ("tracker_id",
 
1061
                  id_addr[Trf.EmailFields.TRACKER_ID]);
 
1062
              email_addresses.add (fd);
 
1063
            }
 
1064
        }
 
1065
 
 
1066
      this._email_addresses = email_addresses;
 
1067
      this._email_addresses_ro = this._email_addresses.read_only_view;
 
1068
    }
 
1069
 
 
1070
  private void _update_urls ()
 
1071
    {
 
1072
      var urls = new HashSet<FieldDetails> ();
 
1073
      var _urls_field = this._cursor.get_string (Trf.Fields.URLS).dup ();
 
1074
 
 
1075
      if (_urls_field == null)
 
1076
        return;
 
1077
 
 
1078
      string[] urls_table = _urls_field.split ("\n");
 
1079
 
 
1080
      foreach (var row in urls_table)
 
1081
        {
 
1082
          string[] u = row.split ("\t");
 
1083
          var tracker_id = u[Trf.UrlsFields.TRACKER_ID];
 
1084
 
 
1085
          for (int i=1; i< u.length; i++)
 
1086
            {
 
1087
              if (u[i] == null || u[i] == "")
 
1088
                continue;
 
1089
 
 
1090
              string type = "";
 
1091
              switch (i)
 
1092
                {
 
1093
                  case Trf.UrlsFields.BLOG:
 
1094
                    type = "blog";
 
1095
                    break;
 
1096
                  case Trf.UrlsFields.WEBSITE:
 
1097
                    type = "website";
 
1098
                    break;
 
1099
                  case Trf.UrlsFields.URL:
 
1100
                    type = "url";
 
1101
                    break;
 
1102
                }
 
1103
 
 
1104
              var fd = new FieldDetails (u[i]);
 
1105
              fd.set_parameter ("tracker_id", tracker_id);
 
1106
              fd.set_parameter ("type", type);
 
1107
              urls.add (fd);
 
1108
            }
 
1109
        }
 
1110
 
 
1111
      this._urls = urls;
 
1112
      this._urls_ro = this._urls.read_only_view;
 
1113
    }
 
1114
 
 
1115
  internal bool _add_url (string url, string tracker_id, string type = "")
 
1116
    {
 
1117
      bool found = false;
 
1118
 
 
1119
      foreach (var p in this._urls)
 
1120
        {
 
1121
          if (p.get_parameter_values ("tracker_id").contains (tracker_id))
 
1122
            {
 
1123
              found = true;
 
1124
              break;
 
1125
            }
 
1126
        }
 
1127
 
 
1128
      if (!found)
 
1129
        {
 
1130
          var fd = new FieldDetails (url);
 
1131
          fd.set_parameter ("tracker_id", tracker_id);
 
1132
          fd.set_parameter ("type", type);
 
1133
          this._urls.add (fd);
 
1134
          this.notify_property ("urls");
 
1135
        }
 
1136
 
 
1137
      return !found;
 
1138
    }
 
1139
 
 
1140
  internal bool _remove_url (string tracker_id)
 
1141
    {
 
1142
      bool found = false;
 
1143
 
 
1144
      foreach (var u in this._urls)
 
1145
        {
 
1146
          if (u.get_parameter_values ("tracker_id").contains (tracker_id))
 
1147
            {
 
1148
              this._urls.remove (u);
 
1149
              found = true;
 
1150
            }
 
1151
        }
 
1152
 
 
1153
      if (found)
 
1154
        this.notify_property ("urls");
 
1155
 
 
1156
      return found;
 
1157
    }
 
1158
 
 
1159
  private void _update_favourite ()
 
1160
    {
 
1161
      var favourite = this._cursor.get_string (Trf.Fields.FAVOURITE).dup ();
 
1162
 
 
1163
      this._is_favourite = false;
 
1164
 
 
1165
      if (favourite != null)
 
1166
        {
 
1167
          var trf_store = (Trf.PersonaStore) this.store;
 
1168
          int favorite_tracker_id = trf_store.get_favorite_id ();
 
1169
          foreach (var tag in favourite.split (","))
 
1170
            {
 
1171
              if (int.parse (tag) == favorite_tracker_id)
 
1172
                {
 
1173
                  this._is_favourite = true;
 
1174
                }
 
1175
            }
 
1176
        }
 
1177
    }
 
1178
 
 
1179
  /**
 
1180
   * This method sets the is_favourite attribute internally.
 
1181
   * That is, it should be used as a result of an event fired by
 
1182
   * Tracker since this method doesn't propagate changes back
 
1183
   * to Tracker again.
 
1184
   */
 
1185
  internal void _set_favourite (bool is_fav)
 
1186
    {
 
1187
      this._is_favourite = is_fav;
 
1188
      this.notify_property ("is-favourite");
 
1189
    }
 
1190
}