inspect: Fix listing GStrv properties
[platform/upstream/folks.git] / tools / inspect / utils.vala
1 /*
2  * Copyright (C) 2010 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  *       Philip Withnall <philip.withnall@collabora.co.uk>
19  */
20
21 using Folks;
22 using Gee;
23 using GLib;
24
25 private class Folks.Inspect.Utils
26 {
27   /* The current indentation level, in spaces */
28   private static uint indentation = 0;
29   private static string indentation_string = "";
30
31   public static void init ()
32     {
33       Utils.indentation_string = "";
34
35       /* Register some general transformation functions */
36       Value.register_transform_func (typeof (Object), typeof (string),
37           Utils.transform_object_to_string);
38       Value.register_transform_func (typeof (Folks.PersonaStore),
39           typeof (string), Utils.transform_persona_store_to_string);
40       Value.register_transform_func (typeof (string[]), typeof (string),
41           Utils.transform_string_array_to_string);
42     }
43
44   private static void transform_object_to_string (Value src,
45       out Value dest)
46     {
47       /* FIXME: works around bgo#638363 */
48       Value dest_tmp = Value (typeof (string));
49       dest_tmp.take_string ("%p".printf (src.get_object ()));
50       dest = dest_tmp;
51     }
52
53   private static void transform_persona_store_to_string (Value src,
54       out Value dest)
55     {
56       /* FIXME: works around bgo#638363 */
57       Value dest_tmp = Value (typeof (string));
58       Folks.PersonaStore store = (Folks.PersonaStore) src.get_object ();
59       dest_tmp.take_string ("%p: %s, %s (%s)".printf (store, store.type_id,
60           store.id, store.display_name));
61       dest = dest_tmp;
62     }
63
64   private static void transform_string_array_to_string (Value src,
65       out Value dest)
66     {
67       /* FIXME: works around bgo#638363 */
68       Value dest_tmp = Value (typeof (string));
69       unowned string[] array = (string[]) src.get_boxed ();
70       string output = "{ ";
71       bool first = true;
72       /* FIXME: Work around bgo#656467 by using for() instead of foreach() */
73       for (uint i = 0; array[i] != null; i++)
74         {
75           var element = array[i];
76           if (first == false)
77             output += ", ";
78           output += "'%s'".printf (element);
79           first = false;
80         }
81       output += " }";
82       dest_tmp.take_string (output);
83       dest = dest_tmp;
84     }
85
86   public static void indent ()
87     {
88       /* We indent in increments of two spaces */
89       Utils.indentation += 2;
90       Utils.indentation_string = string.nfill (Utils.indentation, ' ');
91     }
92
93   public static void unindent ()
94     {
95       Utils.indentation -= 2;
96       Utils.indentation_string = string.nfill (Utils.indentation, ' ');
97     }
98
99   [PrintfFormat ()]
100   public static void print_line (string format, ...)
101     {
102       /* FIXME: store the va_list temporarily to work around bgo#638308 */
103       var valist = va_list ();
104       string output = format.vprintf (valist);
105       stdout.printf ("%s%s\n", Utils.indentation_string, output);
106     }
107
108   public static void print_individual (Individual individual,
109       bool show_personas)
110     {
111       Utils.print_line ("Individual '%s' with %u personas:",
112           individual.id, individual.personas.size);
113
114       /* List the Individual's properties */
115       unowned ParamSpec[] properties =
116           individual.get_class ().list_properties ();
117
118       Utils.indent ();
119       foreach (unowned ParamSpec pspec in properties)
120         {
121           Value prop_value;
122           string output_string;
123
124           /* Ignore the personas property if we're printing the personas out */
125           if (show_personas == true && pspec.get_name () == "personas")
126             continue;
127
128           prop_value = Value (pspec.value_type);
129           individual.get_property (pspec.get_name (), ref prop_value);
130
131           output_string = Utils.property_to_string (individual.get_type (),
132               pspec.get_name (), prop_value);
133
134           Utils.print_line ("%-20s  %s", pspec.get_nick (), output_string);
135         }
136
137       if (show_personas == true)
138         {
139           Utils.print_line ("");
140           Utils.print_line ("Personas:");
141
142           Utils.indent ();
143           foreach (Persona persona in individual.personas)
144             Utils.print_persona (persona);
145           Utils.unindent ();
146         }
147       Utils.unindent ();
148     }
149
150   public static void print_persona (Persona persona)
151     {
152       Utils.print_line ("Persona '%s':", persona.uid);
153
154       /* List the Persona's properties */
155       unowned ParamSpec[] properties =
156           persona.get_class ().list_properties ();
157
158       Utils.indent ();
159       foreach (unowned ParamSpec pspec in properties)
160         {
161           Value prop_value;
162           string output_string;
163
164           prop_value = Value (pspec.value_type);
165           persona.get_property (pspec.get_name (), ref prop_value);
166
167           output_string = Utils.property_to_string (persona.get_type (),
168               pspec.get_name (), prop_value);
169
170           Utils.print_line ("%-20s  %s", pspec.get_nick (), output_string);
171         }
172       Utils.unindent ();
173     }
174
175   public static void print_persona_store (PersonaStore store,
176       bool show_personas)
177     {
178       if (store.is_prepared == false)
179         {
180           Utils.print_line ("Persona store '%s':", store.id);
181           Utils.indent ();
182           Utils.print_line ("Not prepared.");
183           Utils.unindent ();
184
185           return;
186         }
187
188       Utils.print_line ("Persona store '%s' with %u personas:",
189           store.id, store.personas.size);
190
191       /* List the store's properties */
192       unowned ParamSpec[] properties =
193           store.get_class ().list_properties ();
194
195       Utils.indent ();
196       foreach (unowned ParamSpec pspec in properties)
197         {
198           Value prop_value;
199           string output_string;
200
201           /* Ignore the personas property if we're printing the personas out */
202           if (show_personas == true && pspec.get_name () == "personas")
203             continue;
204
205           prop_value = Value (pspec.value_type);
206           store.get_property (pspec.get_name (), ref prop_value);
207
208           output_string = Utils.property_to_string (store.get_type (),
209               pspec.get_name (), prop_value);
210
211           Utils.print_line ("%-20s  %s", pspec.get_nick (), output_string);
212         }
213
214       if (show_personas == true)
215         {
216           Utils.print_line ("");
217           Utils.print_line ("Personas:");
218
219           Utils.indent ();
220           foreach (var persona in store.personas.values)
221             {
222               Utils.print_persona (persona);
223             }
224           Utils.unindent ();
225         }
226       Utils.unindent ();
227     }
228
229   private static string property_to_string (Type object_type,
230       string prop_name,
231       Value prop_value)
232     {
233       string output_string;
234
235       /* Overrides for various known properties */
236       if (object_type.is_a (typeof (Individual)) && prop_name == "personas")
237         {
238           Set<Persona> personas = (Set<Persona>) prop_value.get_object ();
239           return "List of %u personas".printf (personas.size);
240         }
241       else if (object_type.is_a (typeof (PersonaStore)) &&
242           prop_name == "personas")
243         {
244           Map<string, Persona> personas =
245               (Map<string, Persona>) prop_value.get_object ();
246           return "Set of %u personas".printf (personas.size);
247         }
248       else if (prop_name == "groups" ||
249                prop_name == "local-ids")
250         {
251           Set<string> groups = (Set<string>) prop_value.get_object ();
252           output_string = "{ ";
253           bool first = true;
254
255           foreach (var group in groups)
256             {
257               if (first == false)
258                 output_string += ", ";
259               output_string += "'%s'".printf (group);
260               first = false;
261             }
262
263           output_string += " }";
264           return output_string;
265         }
266       else if (prop_name == "avatar")
267         {
268           string ret = null;
269           LoadableIcon? avatar = (LoadableIcon) prop_value.get_object ();
270
271           if (avatar != null &&
272               avatar is FileIcon && ((FileIcon) avatar).get_file () != null)
273             {
274               ret = "%p (file: %s)".printf (avatar,
275                   ((FileIcon) avatar).get_file ().get_uri ());
276             }
277           else if (avatar != null)
278             {
279               ret = "%p".printf (avatar);
280             }
281
282           return ret;
283         }
284       else if (prop_name == "im-addresses" ||
285                prop_name == "web-service-addresses")
286         {
287           var prop_list =
288               (MultiMap<string, AbstractFieldDetails<string>>)
289                   prop_value.get_object ();
290           output_string = "{ ";
291           bool first = true;
292
293           foreach (var k in prop_list.get_keys ())
294             {
295               if (first == false)
296                 output_string += ", ";
297               output_string += "'%s' : { ".printf (k);
298               first = false;
299
300               var v = prop_list.get (k);
301               bool _first = true;
302               foreach (var a in v)
303                 {
304                   if (_first == false)
305                     output_string += ", ";
306                   output_string += "'%s'".printf (a.value);
307                   _first = false;
308                 }
309
310               output_string += " }";
311             }
312
313           output_string += " }";
314           return output_string;
315         }
316       else if (prop_name == "email-addresses" ||
317                prop_name == "phone-numbers" ||
318                prop_name == "urls")
319         {
320           output_string = "{ ";
321           bool first = true;
322           var prop_list =
323               (Set<AbstractFieldDetails<string>>) prop_value.get_object ();
324
325           foreach (var p in prop_list)
326             {
327               if (!first)
328                 {
329                   output_string += ", ";
330                 }
331               output_string +=  p.value;
332               first = false;
333             }
334             output_string += " }";
335
336             return output_string;
337         }
338       else if (prop_name == "birthday")
339         {
340           unowned DateTime dobj = (DateTime) prop_value.get_boxed ();
341           if (dobj != null)
342             return dobj.to_string ();
343           else
344             return "";
345         }
346       else if (prop_name == "postal-addresses")
347         {
348           output_string = "{ ";
349           bool first = true;
350           var prop_list =
351               (Set<PostalAddressFieldDetails>) prop_value.get_object ();
352
353           foreach (var p in prop_list)
354             {
355               if (!first)
356                 {
357                   output_string += ". ";
358                 }
359               output_string +=  p.value.to_string ();
360               first = false;
361             }
362             output_string += " }";
363
364             return output_string;
365         }
366       else if (prop_name == "notes")
367         {
368           Set<NoteFieldDetails> notes =
369               prop_value.get_object () as Set<NoteFieldDetails>;
370
371           output_string = "{ ";
372           bool first = true;
373
374           foreach (var note in notes)
375             {
376               if (!first)
377                 {
378                   output_string += ", ";
379                 }
380               output_string += note.uid;
381               first = false;
382             }
383             output_string += " }";
384
385             return output_string;
386         }
387       else if (prop_name == "roles")
388         {
389           var roles = (Set<RoleFieldDetails>) prop_value.get_object ();
390
391           output_string = "{ ";
392           bool first = true;
393
394           foreach (var role in roles)
395             {
396               if (!first)
397                 {
398                   output_string += ", ";
399                 }
400               output_string += role.value.to_string ();
401               first = false;
402             }
403             output_string += " }";
404
405             return output_string;
406         }
407       else if (prop_name == "structured-name")
408         {
409           unowned StructuredName sn = (StructuredName) prop_value.get_object ();
410           string ret = null;
411           if (sn != null)
412             ret = sn.to_string ();
413           return ret;
414         }
415
416       return Utils.transform_value_to_string (prop_value);
417     }
418
419   public static string transform_value_to_string (Value prop_value)
420     {
421       if (Value.type_transformable (prop_value.type (), typeof (string)))
422         {
423           /* Convert to a string value */
424           Value string_value = Value (typeof (string));
425           prop_value.transform (ref string_value);
426           return string_value.get_string ();
427         }
428       else
429         {
430           /* Can't convert the property value to a string */
431           return "Can't convert from type '%s' to '%s'".printf (
432               prop_value.type ().name (), typeof (string).name ());
433         }
434     }
435
436   /* FIXME: This can't be in the command_completion_cb() function because Vala
437    * doesn't allow static local variables. Erk. */
438   private static MapIterator<string, Command>? command_name_iter = null;
439
440   /* Complete a command name, starting with @word. */
441   public static string? command_name_completion_cb (string word,
442       int state)
443     {
444       /* Initialise state. Whoever wrote the readline API should be shot. */
445       if (state == 0)
446         Utils.command_name_iter = main_client.commands.map_iterator ();
447
448       while (Utils.command_name_iter.next () == true)
449         {
450           string command_name = Utils.command_name_iter.get_key ();
451           if (command_name.has_prefix (word))
452             return command_name;
453         }
454
455       /* Clean up */
456       Utils.command_name_iter = null;
457       return null;
458     }
459
460   /* FIXME: This can't be in the individual_id_completion_cb() function because
461    * Vala doesn't allow static local variables. Erk. */
462   private static MapIterator<string, Individual>? individual_id_iter = null;
463
464   /* Complete an individual's ID, starting with @word. */
465   public static string? individual_id_completion_cb (string word,
466       int state)
467     {
468       /* Initialise state. Whoever wrote the readline API should be shot. */
469       if (state == 0)
470         {
471           Utils.individual_id_iter =
472               main_client.aggregator.individuals.map_iterator ();
473         }
474
475       while (Utils.individual_id_iter.next () == true)
476         {
477           var id = Utils.individual_id_iter.get_key ();
478           if (id.has_prefix (word))
479             return id;
480         }
481
482       /* Clean up */
483       Utils.individual_id_iter = null;
484       return null;
485     }
486
487   /* FIXME: This can't be in the individual_id_completion_cb() function because
488    * Vala doesn't allow static local variables. Erk. */
489   private static Iterator<Persona>? persona_uid_iter = null;
490
491   /* Complete an individual's ID, starting with @word. */
492   public static string? persona_uid_completion_cb (string word,
493       int state)
494     {
495       /* Initialise state. Whoever wrote the readline API should be shot. */
496       if (state == 0)
497         {
498           Utils.individual_id_iter =
499               main_client.aggregator.individuals.map_iterator ();
500           Utils.persona_uid_iter = null;
501         }
502
503       while (Utils.persona_uid_iter != null ||
504           Utils.individual_id_iter.next () == true)
505         {
506           var individual = Utils.individual_id_iter.get_value ();
507
508           if (Utils.persona_uid_iter == null)
509             {
510               assert (individual != null);
511               Utils.persona_uid_iter = individual.personas.iterator ();
512             }
513
514           while (Utils.persona_uid_iter.next ())
515             {
516               var persona = Utils.persona_uid_iter.get ();
517               if (persona.uid.has_prefix (word))
518                 return persona.uid;
519             }
520
521           /* Clean up */
522           Utils.persona_uid_iter = null;
523         }
524
525       /* Clean up */
526       Utils.individual_id_iter = null;
527       return null;
528     }
529
530   /* FIXME: This can't be in the backend_name_completion_cb() function because
531    * Vala doesn't allow static local variables. Erk. */
532   private static Iterator<Backend>? backend_name_iter = null;
533
534   /* Complete an individual's ID, starting with @word. */
535   public static string? backend_name_completion_cb (string word,
536       int state)
537     {
538       /* Initialise state. Whoever wrote the readline API should be shot. */
539       if (state == 0)
540         {
541           Utils.backend_name_iter =
542               main_client.backend_store.list_backends ().iterator ();
543         }
544
545       while (Utils.backend_name_iter.next () == true)
546         {
547           Backend backend = Utils.backend_name_iter.get ();
548           if (backend.name.has_prefix (word))
549             return backend.name;
550         }
551
552       /* Clean up */
553       Utils.backend_name_iter = null;
554       return null;
555     }
556
557   /* FIXME: This can't be in the persona_store_id_completion_cb() function
558    * because Vala doesn't allow static local variables. Erk. */
559   private static MapIterator<string, PersonaStore>? persona_store_id_iter =
560       null;
561
562   /* Complete a persona store's ID, starting with @word. */
563   public static string? persona_store_id_completion_cb (string word,
564       int state)
565     {
566       /* Initialise state. Whoever wrote the readline API should be shot. */
567       if (state == 0)
568         {
569           Utils.backend_name_iter =
570               main_client.backend_store.list_backends ().iterator ();
571           Utils.persona_store_id_iter = null;
572         }
573
574       while (Utils.persona_store_id_iter != null ||
575           Utils.backend_name_iter.next () == true)
576         {
577           if (Utils.persona_store_id_iter == null)
578             {
579               Backend backend = Utils.backend_name_iter.get ();
580               Utils.persona_store_id_iter =
581                   backend.persona_stores.map_iterator ();
582             }
583
584           while (Utils.persona_store_id_iter.next () == true)
585             {
586               var id = Utils.persona_store_id_iter.get_key ();
587               if (id.has_prefix (word))
588                 return id;
589             }
590
591           /* Clean up */
592           Utils.persona_store_id_iter = null;
593         }
594
595       /* Clean up */
596       Utils.backend_name_iter = null;
597       return null;
598     }
599 }