2 * xmlformat_compare - comparsion logic of xmlformat
3 * Copyright (C) 2006 Daniel Friedrich <daniel.friedrich@opensync.org>
4 * Copyright (C) 2007 Daniel Gollub <dgollub@suse.de>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "xmlformat.h"
24 #include <libxml/xmlstring.h>
25 #include <libxml/tree.h>
27 #include "opensync/xmlformat/opensync_xmlformat_internals.h"
30 #include "opensync/xmlformat/opensync_xmlfield_private.h" /* FIXME: direct include of private header */
34 * @brief Search for the points in the sorted OSyncXMLPoints array for a given fieldname.
35 * @param points The sorted points array
36 * @param cur_pos Pointer to the actual line in the points array
37 * @param basic_points Points which should be returned if fieldname will not found in the points array
38 * @param fieldname The name of the field for which the points should be returned
39 * @returns The points for the fieldname
41 int xmlformat_get_points(OSyncXMLPoints
*points
, int* cur_pos
, int basic_points
, const char* fieldname
)
43 for(; points
[(*cur_pos
)].fieldname
; (*cur_pos
)++) {
44 int res
= strcmp(points
[(*cur_pos
)].fieldname
, fieldname
);
46 return points
[(*cur_pos
)].points
;
55 * @brief Search for the points in the sorted OSyncXMLPoints array for the fieldname of xmlfield and handle ignored fields.
57 * This uses xmlformat_get_points() but handle the case if the field needs to be ignored.
58 * Ignored fields doesn't have influence on the compare result. This is needed to keep the compare
59 * result SAME if an ignored fields are the only differences between the entries.
61 * 0 Points got returned for ignored fields to avoid influence of the collected_points value and let
62 * collected_points not raise over the threshold value. This could change the compare result to SIMILAR,
63 * even if the entry should MISMATCH, this should be avoided. This happens when the number of ignored fiels
64 * is the greather equal as the thresold value.
66 * @param xmlfield Pointer to xmlfield
67 * @param points The sorted points array
68 * @param cur_pos Pointer to the actual line in the points array. Gets incremented if the xmlfield requires this!
69 * @param basic_points Points which should be returned if fieldname will not found in the points array
70 * @param same Pointer ot the compare result flag SAME, which got not set to FALSE if the fields should be ignored.
71 * @returns The points for the fieldname. If the field should be ignored 0 points got retunred.
73 static int xmlformat_subtract_points(OSyncXMLField
*xmlfield
, OSyncXMLPoints
*points
, int *cur_pos
, int basic_points
, int *same
) {
75 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p, %i, %p)", __func__
, xmlfield
, points
, cur_pos
, basic_points
, same
);
76 p
= xmlformat_get_points(points
, cur_pos
, basic_points
, osync_xmlfield_get_name(xmlfield
));
78 /* Stay with SAME as compare result and don't substract any points - if fields should be ignored */
80 osync_trace(TRACE_INTERNAL
, "Not same anymore - \"%s\" field differs!", osync_xmlfield_get_name(xmlfield
));
83 osync_trace(TRACE_INTERNAL
, "Ignored field: %s", osync_xmlfield_get_name(xmlfield
));
87 osync_trace(TRACE_EXIT
, "%s: %i", __func__
, p
);
92 * @brief Help method which return the content of a xmlNode
93 * @param node The pointer to a xmlNode
94 * @return The value of the xmlNode or a empty string
96 static xmlChar
*xml_node_get_content(xmlNodePtr node
)
98 if (node
->children
&& node
->children
->content
)
99 return node
->children
->content
;
101 return (xmlChar
*)"";
105 * @brief Compares two xmlfield objects with each other
106 * @param xmlfield1 The pointer to a xmlformat object
107 * @param xmlfield2 The pointer to a xmlformat object
108 * @return TRUE if both xmlfield objects are the same otherwise FALSE
110 osync_bool
xmlfield_compare(OSyncXMLField
*xmlfield1
, OSyncXMLField
*xmlfield2
)
114 xmlNodePtr key1
= NULL
;
115 xmlNodePtr key2
= NULL
;
118 osync_trace(TRACE_ENTRY
, "%s(%p, %p)", __func__
, xmlfield1
, xmlfield2
);
119 osync_assert(xmlfield1
);
120 osync_assert(xmlfield2
);
122 if(strcmp(osync_xmlfield_get_name(xmlfield1
), osync_xmlfield_get_name(xmlfield2
))) {
123 osync_trace(TRACE_EXIT
, "%s: %i", __func__
, FALSE
);
128 key1
= xmlfield1
->node
->children
;
129 key2
= xmlfield2
->node
->children
;
133 GSList
*keylist1
= NULL
;
134 GSList
*keylist2
= NULL
;
135 const char *curkeyname
= NULL
;
137 if(key1
== NULL
&& key2
== NULL
) {
141 if(key1
== NULL
|| key2
== NULL
) {
146 curkeyname
= (const char *)key1
->name
;
148 keylist1
= g_slist_prepend(keylist1
, key1
);
152 i
= strcmp((const char *)key1
->name
, curkeyname
);
156 keylist2
= g_slist_prepend(keylist2
, key2
);
160 i
= strcmp((const char *)key2
->name
, curkeyname
);
164 GSList
*cur_list1
= NULL
;
165 GSList
*cur_list2
= NULL
;
167 /* both lists must have the same length */
168 if(g_slist_length(keylist1
) != g_slist_length(keylist2
)) {
169 osync_trace(TRACE_INTERNAL
, "It's not the same anymore...");
175 cur_list1
= keylist1
;
176 cur_list2
= keylist2
;
179 if(!xmlStrcmp(xml_node_get_content(cur_list1
->data
), xml_node_get_content(cur_list2
->data
)))
181 cur_list2
= g_slist_next(cur_list2
);
182 if(cur_list2
== NULL
) {
189 keylist1
= g_slist_delete_link(keylist1
, cur_list1
);
190 keylist2
= g_slist_delete_link(keylist2
, cur_list2
);
194 }while(keylist1
!= NULL
);
198 g_slist_free(keylist1
);
200 g_slist_free(keylist2
);
203 /* now we check if the attributes are equal */
209 i1
=osync_xmlfield_get_attr_count(xmlfield1
);
210 i2
=osync_xmlfield_get_attr_count(xmlfield2
);
217 for(i
=0; i
< i1
; i
++) {
218 const char *attrvalue
= osync_xmlfield_get_attr(xmlfield2
, osync_xmlfield_get_nth_attr_name(xmlfield1
, i
));
219 if( attrvalue
== NULL
||
220 strcmp(attrvalue
, osync_xmlfield_get_nth_attr_value(xmlfield1
, i
))) {
226 osync_trace(TRACE_EXIT
, "%s: %i", __func__
, same
);
231 * @brief Compares two xmlfield objects with each other of similarity
232 * @param xmlfield1 The pointer to a xmlformat object
233 * @param xmlfield2 The pointer to a xmlformat object
234 * @param keys OSyncXMLPoints::keys
235 * @return TRUE if both xmlfield objects are the similar otherwise FALSE
237 osync_bool
xmlfield_compare_similar(OSyncXMLField
*xmlfield1
, OSyncXMLField
*xmlfield2
, char* keys
[])
239 osync_bool res
= TRUE
;
240 xmlNodePtr node1
, node2
;
242 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, xmlfield1
, xmlfield2
, keys
);
243 osync_assert(xmlfield1
);
244 osync_assert(xmlfield2
);
246 if(strcmp((const char *) osync_xmlfield_get_name(xmlfield1
), (const char *) osync_xmlfield_get_name(xmlfield2
)) != 0)
249 node1
= xmlfield1
->node
->children
;
250 node2
= xmlfield2
->node
->children
;
252 for(i
=0; keys
[i
]; i
++) {
261 while(node1
!= NULL
) {
262 if(strcmp(keys
[i
], (const char *)node1
->name
) == 0)
263 list1
= g_slist_prepend(list1
, node1
);
267 while(node2
!= NULL
) {
268 if(strcmp(keys
[i
], (const char *)node2
->name
) == 0)
269 list2
= g_slist_prepend(list2
, node2
);
278 if(cur_list2
== NULL
) {
283 while(xmlStrcmp(xml_node_get_content((xmlNodePtr
)cur_list1
->data
),
284 xml_node_get_content((xmlNodePtr
)cur_list2
->data
))) {
285 cur_list2
= g_slist_next(cur_list2
);
286 if(cur_list2
== NULL
) {
293 list1
= g_slist_delete_link(list1
, cur_list1
);
294 list2
= g_slist_delete_link(list2
, cur_list2
);
308 osync_trace(TRACE_EXIT
, "%s: %i", __func__
, res
);
314 * @brief Compares two xmlformat objects with each other
315 * @param xmlformat1 The pointer to a xmlformat object
316 * @param xmlformat2 The pointer to a xmlformat object
317 * @param points The sorted points array
318 * @param basic_points Points which should be used if a xmlfield name is not found in the points array
319 * @param threshold If the two xmlformats are not the same, then this value will decide if the two xmlformats are similar
320 * @return One of the values of the OSyncConvCmpResult enumeration
322 OSyncConvCmpResult
xmlformat_compare(OSyncXMLFormat
*xmlformat1
, OSyncXMLFormat
*xmlformat2
, OSyncXMLPoints points
[], int basic_points
, int threshold
)
324 int res
, collected_points
, cur_pos
;
325 OSyncXMLField
*xmlfield1
= NULL
;
326 OSyncXMLField
*xmlfield2
= NULL
;
327 osync_bool same
= TRUE
;
329 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p, %i, %i)", __func__
, xmlformat1
, xmlformat2
, points
, basic_points
, threshold
);
331 xmlfield1
= osync_xmlformat_get_first_field(xmlformat1
);
332 xmlfield2
= osync_xmlformat_get_first_field(xmlformat2
);
335 collected_points
= 0;
337 while(xmlfield1
!= NULL
|| xmlfield2
!= NULL
)
340 /* subtract points for xmlfield2*/
341 if(xmlfield1
== NULL
) {
342 collected_points
-= xmlformat_subtract_points(xmlfield2
, points
, &cur_pos
, basic_points
, &same
);
343 xmlfield2
= osync_xmlfield_get_next(xmlfield2
);
347 /* subtract points for xmlfield1*/
348 if(xmlfield2
== NULL
) {
349 collected_points
-= xmlformat_subtract_points(xmlfield1
, points
, &cur_pos
, basic_points
, &same
);
350 xmlfield1
= osync_xmlfield_get_next(xmlfield1
);
354 res
= strcmp(osync_xmlfield_get_name(xmlfield1
), osync_xmlfield_get_name(xmlfield2
));
355 osync_trace(TRACE_INTERNAL
, "result of strcmp(): %i (%s || %s)", res
, osync_xmlfield_get_name(xmlfield1
), osync_xmlfield_get_name(xmlfield2
));
357 /* subtract points for xmlfield1*/
359 collected_points
-= xmlformat_subtract_points(xmlfield1
, points
, &cur_pos
, basic_points
, &same
);
360 xmlfield1
= osync_xmlfield_get_next(xmlfield1
);
364 /* subtract points for xmlfield2*/
366 collected_points
-= xmlformat_subtract_points(xmlfield2
, points
, &cur_pos
, basic_points
, &same
);
367 xmlfield2
= osync_xmlfield_get_next(xmlfield2
);
371 /* make lists and compare */
374 GSList
*fieldlist1
= NULL
;
375 GSList
*fieldlist2
= NULL
;
377 const char *curfieldname
= osync_xmlfield_get_name(xmlfield1
);
380 int p
= xmlformat_get_points(points
, &cur_pos
, basic_points
, curfieldname
);
382 /* don't compare both fields if they should be ignore to avoid influence of the compare result */
384 xmlfield1
= osync_xmlfield_get_next(xmlfield1
);
385 xmlfield2
= osync_xmlfield_get_next(xmlfield2
);
390 fieldlist1
= g_slist_prepend(fieldlist1
, xmlfield1
);
391 xmlfield1
= osync_xmlfield_get_next(xmlfield1
);
392 if(xmlfield1
== NULL
)
394 res
= strcmp(osync_xmlfield_get_name(xmlfield1
), curfieldname
);
398 fieldlist2
= g_slist_prepend(fieldlist2
, xmlfield2
);
399 xmlfield2
= osync_xmlfield_get_next(xmlfield2
);
400 if(xmlfield2
== NULL
)
402 res
= strcmp(osync_xmlfield_get_name(xmlfield2
), curfieldname
);
407 /* if same then compare and give points*/
415 /* both lists must have the same length */
416 if(g_slist_length(fieldlist1
) != g_slist_length(fieldlist2
)) {
418 osync_trace(TRACE_INTERNAL
, "both list don't have the same length");
423 cur_list1
= fieldlist1
;
424 cur_list2
= fieldlist2
;
427 if(xmlfield_compare((OSyncXMLField
*)cur_list1
->data
, (OSyncXMLField
*)cur_list2
->data
) == TRUE
)
429 cur_list2
= g_slist_next(cur_list2
);
430 if(cur_list2
== NULL
) {
432 osync_trace(TRACE_INTERNAL
, "one field is alone: %s", osync_xmlfield_get_name(cur_list1
->data
));
439 osync_trace(TRACE_INTERNAL
, "add %i point(s) for same fields: %s", p
, curfieldname
);
440 collected_points
+= p
;
441 fieldlist1
= g_slist_delete_link(fieldlist1
, cur_list1
);
442 fieldlist2
= g_slist_delete_link(fieldlist2
, cur_list2
);
446 }while(fieldlist1
!= NULL
);
449 /* if similar then compare and give points*/
454 int subtracted_count
;
458 /* if no points to add or to subtract we need no compair of similarity */
460 g_slist_free(fieldlist1
);
461 g_slist_free(fieldlist2
);
467 subtracted_count
= 0;
470 cur_list1
= fieldlist1
;
471 cur_list2
= fieldlist2
;
475 if(xmlfield_compare_similar((OSyncXMLField
*)cur_list1
->data
,
476 (OSyncXMLField
*)cur_list2
->data
,
477 points
[cur_pos
].keys
) == TRUE
) {
482 cur_list2
= g_slist_next(cur_list2
);
485 /* add or subtract the points */
488 osync_trace(TRACE_INTERNAL
, "add %i point(s) for similiar field: %s", p
, curfieldname
);
489 collected_points
+= p
;
490 fieldlist1
= g_slist_delete_link(fieldlist1
, cur_list1
);
491 fieldlist2
= g_slist_delete_link(fieldlist2
, cur_list2
);
493 /* subtract the points */
494 osync_trace(TRACE_INTERNAL
, "subtracting %i point(s) for missing field: %s", p
, curfieldname
);
495 collected_points
-= p
;
497 fieldlist1
= g_slist_delete_link(fieldlist1
, cur_list1
);
499 }while(fieldlist1
!= NULL
);
501 /* subtract the points for the remaining elements in the list2 */
503 /* subtract the points */
504 if(subtracted_count
> 0) {
507 osync_trace(TRACE_INTERNAL
, "subtracting %i point(s) for remaining field: %s", p
, curfieldname
);
508 collected_points
-= p
;
510 fieldlist2
= g_slist_delete_link(fieldlist2
, fieldlist2
);
517 /* the lists should not exist */
518 g_assert(!fieldlist1
);
519 g_assert(!fieldlist2
);
523 osync_trace(TRACE_INTERNAL
, "Result is: %i, Treshold is: %i", collected_points
, threshold
);
525 osync_trace(TRACE_EXIT
, "%s: SAME", __func__
);
526 return OSYNC_CONV_DATA_SAME
;
528 if (collected_points
>= threshold
) {
529 osync_trace(TRACE_EXIT
, "%s: SIMILAR", __func__
);
530 return OSYNC_CONV_DATA_SIMILAR
;
532 osync_trace(TRACE_EXIT
, "%s: MISMATCH", __func__
);
533 return OSYNC_CONV_DATA_MISMATCH
;