1 /*
2 * gsf-doc-meta-data.c:
3 *
4 * Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
5 * Jody Goldberg (jody@gnome.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-doc-meta-data.h>
24 #include <gsf/gsf.h>
25
26 #include <string.h>
27 #include <stdlib.h>
28
29 struct _GsfDocMetaData {
30 GObject base;
31
32 GHashTable *table;
33 };
34 typedef GObjectClass GsfDocMetaDataClass;
35
36 struct _GsfDocProp {
37 char *name;
38 GValue *val;
39 char *linked_to; /* optionally NULL */
40 unsigned ref_count;
41 };
42
43 static GObjectClass *parent_class;
44
45 static void
gsf_doc_meta_data_finalize(GObject * obj)46 gsf_doc_meta_data_finalize (GObject *obj)
47 {
48 g_hash_table_destroy (GSF_DOC_META_DATA (obj)->table);
49 parent_class->finalize (obj);
50 }
51
52 static void
gsf_doc_meta_data_init(GObject * obj)53 gsf_doc_meta_data_init (GObject *obj)
54 {
55 GsfDocMetaData *meta = GSF_DOC_META_DATA (obj);
56 meta->table = g_hash_table_new_full (g_str_hash, g_str_equal,
57 NULL, (GDestroyNotify) gsf_doc_prop_free);
58 }
59
60 static void
gsf_doc_meta_data_class_init(GObjectClass * gobject_class)61 gsf_doc_meta_data_class_init (GObjectClass *gobject_class)
62 {
63 gobject_class->finalize = gsf_doc_meta_data_finalize;
64 parent_class = g_type_class_peek_parent (gobject_class);
65 }
66
GSF_CLASS(GsfDocMetaData,gsf_doc_meta_data,gsf_doc_meta_data_class_init,gsf_doc_meta_data_init,G_TYPE_OBJECT)67 GSF_CLASS(GsfDocMetaData, gsf_doc_meta_data,
68 gsf_doc_meta_data_class_init, gsf_doc_meta_data_init,
69 G_TYPE_OBJECT)
70
71 /**********************************************************************/
72
73 /**
74 * gsf_doc_meta_data_new:
75 *
76 * Returns: (transfer full): a new metadata property collection
77 **/
78 GsfDocMetaData *
79 gsf_doc_meta_data_new (void)
80 {
81 return g_object_new (GSF_DOC_META_DATA_TYPE, NULL);
82 }
83
84 /**
85 * gsf_doc_meta_data_lookup:
86 * @meta: #GsfDocMetaData
87 * @name:
88 *
89 * Returns: (nullable) (transfer none): the property with @name in @meta. The caller can
90 * modify the property value and link but not the name.
91 **/
92 GsfDocProp *
gsf_doc_meta_data_lookup(GsfDocMetaData const * meta,char const * name)93 gsf_doc_meta_data_lookup (GsfDocMetaData const *meta, char const *name)
94 {
95 g_return_val_if_fail (IS_GSF_DOC_META_DATA (meta), NULL);
96 g_return_val_if_fail (name != NULL, NULL);
97 return g_hash_table_lookup (meta->table, name);
98 }
99
100 /**
101 * gsf_doc_meta_data_insert:
102 * @meta: #GsfDocMetaData
103 * @name: (transfer full): the id.
104 * @value: (transfer full): #GValue
105 *
106 * Take ownership of @name and @value and insert a property into @meta.
107 * If a property exists with @name, it is replaced (The link is lost)
108 **/
109 void
gsf_doc_meta_data_insert(GsfDocMetaData * meta,char * name,GValue * value)110 gsf_doc_meta_data_insert (GsfDocMetaData *meta, char *name, GValue *value)
111 {
112 GsfDocProp *prop;
113
114 g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
115 g_return_if_fail (name != NULL);
116 prop = g_new (GsfDocProp, 1);
117 prop->name = name;
118 prop->val = value;
119 prop->linked_to = NULL;
120 prop->ref_count = 1;
121 g_hash_table_replace (meta->table, prop->name, prop);
122 }
123
124 /**
125 * gsf_doc_meta_data_remove:
126 * @meta: the collection
127 * @name: the non-null string name of the property
128 *
129 * If @name does not exist in the collection, do nothing. If @name does exist,
130 * remove it and its value from the collection
131 **/
132 void
gsf_doc_meta_data_remove(GsfDocMetaData * meta,char const * name)133 gsf_doc_meta_data_remove (GsfDocMetaData *meta, char const *name)
134 {
135 g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
136 g_return_if_fail (name != NULL);
137 g_hash_table_remove (meta->table, name);
138 }
139
140 /**
141 * gsf_doc_meta_data_steal:
142 * @meta: #GsfDocMetaData
143 * @name:
144 *
145 * Returns: (nullable) (transfer full): the property with @name in @meta.
146 **/
147 GsfDocProp *
gsf_doc_meta_data_steal(GsfDocMetaData * meta,char const * name)148 gsf_doc_meta_data_steal (GsfDocMetaData *meta, char const *name)
149 {
150 GsfDocProp *prop;
151 g_return_val_if_fail (IS_GSF_DOC_META_DATA (meta), NULL);
152 g_return_val_if_fail (name != NULL, NULL);
153 prop = g_hash_table_lookup (meta->table, name);
154 if (NULL != prop)
155 g_hash_table_steal (meta->table, name);
156 return prop;
157 }
158
159 /**
160 * gsf_doc_meta_data_store:
161 * @meta: #GsfDocMetaData
162 * @prop: #GsfDocProp
163 *
164 **/
165 void
gsf_doc_meta_data_store(GsfDocMetaData * meta,GsfDocProp * prop)166 gsf_doc_meta_data_store (GsfDocMetaData *meta, GsfDocProp *prop)
167 {
168 g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
169 g_return_if_fail (prop != NULL);
170 g_return_if_fail (prop != g_hash_table_lookup (meta->table, prop->name));
171 g_hash_table_replace (meta->table, prop->name, prop);
172 }
173
174 static void
cb_collect_pairs(char * prop_name,GsfDocProp * prop,GPtrArray * pairs)175 cb_collect_pairs (char *prop_name, GsfDocProp *prop, GPtrArray *pairs)
176 {
177 g_ptr_array_add (pairs, prop_name);
178 g_ptr_array_add (pairs, prop);
179 }
180
181 static int
deref_strcmp(const char ** a,const char ** b)182 deref_strcmp (const char **a, const char **b)
183 {
184 return strcmp (*a, *b);
185 }
186
187 /**
188 * gsf_doc_meta_data_foreach:
189 * @meta: the collection
190 * @func: (scope call): the function called once for each element in the collection
191 * @user_data: (nullable): any supplied user data
192 *
193 * Iterate through each (key, value) pair in this collection
194 **/
195 void
gsf_doc_meta_data_foreach(GsfDocMetaData const * meta,GHFunc func,gpointer user_data)196 gsf_doc_meta_data_foreach (GsfDocMetaData const *meta, GHFunc func, gpointer user_data)
197 {
198 GPtrArray *pairs;
199 unsigned ui;
200
201 g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
202
203 if (g_hash_table_size (meta->table) == 0)
204 return;
205
206 /* Sort the pairs by property name in order to generate consistent
207 files. */
208 pairs = g_ptr_array_new ();
209 g_hash_table_foreach (meta->table, (GHFunc)cb_collect_pairs, pairs);
210
211 qsort (&g_ptr_array_index (pairs, 0),
212 pairs->len / 2,
213 2 * sizeof (gpointer),
214 (GCompareFunc)deref_strcmp);
215
216 for (ui = 0; ui < pairs->len; ui += 2)
217 func (g_ptr_array_index (pairs, ui),
218 g_ptr_array_index (pairs, ui + 1),
219 user_data);
220
221 g_ptr_array_free (pairs, TRUE);
222 }
223
224 /**
225 * gsf_doc_meta_data_size:
226 * @meta: the collection
227 *
228 * Returns: the number of items in this collection
229 **/
230 gsize
gsf_doc_meta_data_size(GsfDocMetaData const * meta)231 gsf_doc_meta_data_size (GsfDocMetaData const *meta)
232 {
233 g_return_val_if_fail (meta != NULL, 0);
234 return (gsize) g_hash_table_size (meta->table);
235 }
236
237 static void
cb_print_property(char const * name,GsfDocProp const * prop)238 cb_print_property (char const *name,
239 GsfDocProp const *prop)
240 {
241 if (gsf_doc_prop_get_link (prop) != NULL)
242 g_print ("prop '%s' LINKED TO -> '%s'\n",
243 name, gsf_doc_prop_get_link (prop));
244 else
245 g_print ("prop '%s'\n", name);
246
247 gsf_doc_prop_dump (prop);
248 }
249
250 /**
251 * gsf_doc_meta_dump:
252 * @meta: #GsfDocMetaData
253 *
254 * A debugging utility to dump the content of @meta via g_print
255 **/
256 void
gsf_doc_meta_dump(GsfDocMetaData const * meta)257 gsf_doc_meta_dump (GsfDocMetaData const *meta)
258 {
259 gsf_doc_meta_data_foreach (meta,
260 (GHFunc) cb_print_property, NULL);
261 }
262
263 /**********************************************************************/
264
265 /**
266 * gsf_doc_prop_new:
267 * @name: (transfer full): The name of the property.
268 *
269 * Returns: (transfer full): a new #GsfDocProp.
270 **/
271 GsfDocProp *
gsf_doc_prop_new(char * name)272 gsf_doc_prop_new (char *name)
273 {
274 GsfDocProp *prop;
275
276 g_return_val_if_fail (name != NULL, NULL);
277
278 prop = g_new (GsfDocProp, 1);
279 prop->name = name;
280 prop->val = NULL;
281 prop->linked_to = NULL;
282
283 return prop;
284 }
285
286 /**
287 * gsf_doc_prop_free:
288 * @prop: (transfer full) (allow-none): #GsfDocProp
289 *
290 * Release the given property.
291 **/
292 void
gsf_doc_prop_free(GsfDocProp * prop)293 gsf_doc_prop_free (GsfDocProp *prop)
294 {
295 if (NULL != prop) {
296 prop->ref_count--;
297 if (prop->ref_count == 0) {
298 g_free (prop->linked_to);
299
300 if (prop->val) {
301 g_value_unset (prop->val);
302 g_free (prop->val);
303 }
304 g_free (prop->name);
305 g_free (prop);
306 }
307 }
308
309 }
310
311 static GsfDocProp *
gsf_doc_prop_ref(GsfDocProp * prop)312 gsf_doc_prop_ref (GsfDocProp *prop)
313 {
314 prop->ref_count++;
315 return prop;
316 }
317
318 GType
gsf_doc_prop_get_type(void)319 gsf_doc_prop_get_type (void)
320 {
321 static GType type = 0;
322
323 if (type == 0)
324 type = g_boxed_type_register_static
325 ("GsfDocProp",
326 (GBoxedCopyFunc) gsf_doc_prop_ref,
327 (GBoxedFreeFunc) gsf_doc_prop_free);
328
329 return type;
330 }
331
332 /**
333 * gsf_doc_prop_get_name:
334 * @prop: #GsfDocProp
335 *
336 * Returns: (transfer none): the name of the property
337 **/
338 char const *
gsf_doc_prop_get_name(GsfDocProp const * prop)339 gsf_doc_prop_get_name (GsfDocProp const *prop)
340 {
341 g_return_val_if_fail (prop != NULL, NULL);
342 return prop->name;
343 }
344
345 /**
346 * gsf_doc_prop_get_val:
347 * @prop: the property
348 *
349 * Returns: the value of the property, the caller should not modify the result.
350 **/
351 GValue const *
gsf_doc_prop_get_val(GsfDocProp const * prop)352 gsf_doc_prop_get_val (GsfDocProp const *prop)
353 {
354 g_return_val_if_fail (prop != NULL, NULL);
355 return prop->val;
356 }
357
358 /**
359 * gsf_doc_prop_set_val:
360 * @prop: #GsfDocProp
361 * @val: (transfer full): #GValue
362 *
363 * Assigns @val to @prop, and unsets and frees the current value.
364 **/
365 void
gsf_doc_prop_set_val(GsfDocProp * prop,GValue * val)366 gsf_doc_prop_set_val (GsfDocProp *prop, GValue *val)
367 {
368 g_return_if_fail (prop != NULL);
369
370 if (val != prop->val) {
371 if (prop->val != NULL) {
372 g_value_unset (prop->val);
373 g_free (prop->val);
374 }
375 prop->val = val;
376 }
377 }
378
379 /**
380 * gsf_doc_prop_swap_val:
381 * @prop: #GsfDocProp
382 * @val: (transfer full): #GValue
383 *
384 * Returns: (transfer full): the current value of @prop, and replaces
385 * it with @val.
386 **/
387 GValue *
gsf_doc_prop_swap_val(GsfDocProp * prop,GValue * val)388 gsf_doc_prop_swap_val (GsfDocProp *prop, GValue *val)
389 {
390 GValue *old_val;
391 g_return_val_if_fail (prop != NULL, NULL);
392
393 old_val = prop->val;
394 prop->val = val;
395 return old_val;
396 }
397
398 /**
399 * gsf_doc_prop_get_link:
400 * @prop: #GsfDocProp
401 *
402 * Returns: (transfer none) (nullable): the current link descriptor of @prop.
403 **/
404 char const *
gsf_doc_prop_get_link(GsfDocProp const * prop)405 gsf_doc_prop_get_link (GsfDocProp const *prop)
406 {
407 g_return_val_if_fail (prop != NULL, NULL);
408 return prop->linked_to;
409 }
410
411 /**
412 * gsf_doc_prop_set_link:
413 * @prop: #GsfDocProp
414 * @link: (transfer full) (nullable): a link.
415 *
416 * Sets @prop's link to @link
417 **/
418 void
gsf_doc_prop_set_link(GsfDocProp * prop,char * link)419 gsf_doc_prop_set_link (GsfDocProp *prop, char *link)
420 {
421 g_return_if_fail (prop != NULL);
422
423 if (link != prop->linked_to) {
424 g_free (prop->linked_to);
425 prop->linked_to = link;
426 }
427 }
428
429 /**
430 * gsf_doc_prop_dump:
431 * @prop: #GsfDocProp
432 *
433 * A debugging utility to dump @prop as text via g_print
434 * New in 1.14.2
435 **/
436 void
gsf_doc_prop_dump(GsfDocProp const * prop)437 gsf_doc_prop_dump (GsfDocProp const *prop)
438 {
439 GValue const *val = gsf_doc_prop_get_val (prop);
440 char *tmp;
441 if (VAL_IS_GSF_DOCPROP_VECTOR ((GValue *)val)) {
442 GArray *va = gsf_value_get_docprop_array (val);
443 unsigned i;
444
445 for (i = 0 ; i < va->len; i++) {
446 tmp = g_strdup_value_contents (&g_array_index (va, GValue, i));
447 g_print ("\t[%u] = %s\n", i, tmp);
448 g_free (tmp);
449 }
450 } else {
451 tmp = g_strdup_value_contents (val);
452 g_print ("\t= %s\n", tmp);
453 g_free (tmp);
454 }
455 }
456