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