/* * libopensync - A synchronization framework * Copyright (C) 2004-2005 Armin Bauer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "opensync.h" #include "opensync_internals.h" /** * @ingroup OSyncConvPrivate */ /*@{*/ #ifndef DOXYGEN_SHOULD_SKIP_THIS typedef struct conv_tree { OSyncFormatEnv *env; OSyncObjType *type; /* The converters that weren't reached yet */ GList *unused; /* The search queue for the Breadth-first search */ GList *search; } conv_tree; typedef struct vertice { OSyncObjFormat *format; /* The invoke_decap will return a new change everytime * we run it so that the original change does not get * changed. We also need to track if the data of this change * should be freed or if it contains a reference into data of * a previous change */ OSyncChange *change; osync_bool free_change_data; osync_bool free_change; /** Keep reference counts because * the data returned by converters * can be references to other data, * and we can't free them too early */ size_t references; /** The path of converters */ GList *path; unsigned losses; unsigned objtype_changes; unsigned conversions; } vertice; #endif static OSyncFormatConverter *osync_conv_find_converter_objformat(OSyncFormatEnv *env, OSyncObjFormat *fmt_src, OSyncObjFormat *fmt_trg) { GList *element = NULL; for (element = env->converters; element; element = element->next) { OSyncFormatConverter *converter = element->data; if (fmt_src == converter->source_format && fmt_trg == converter->target_format) return converter; } return NULL; } osync_bool osync_converter_invoke(OSyncFormatConverter *converter, OSyncChange *change, void *converter_data, OSyncError **error) { osync_trace(TRACE_ENTRY, "osync_converter_invoke(%p, %p, %p)", converter, change, error); osync_trace(TRACE_INTERNAL, "converter: Type: %i, source: %s, target %s", converter->type, converter->source_format->name, converter->target_format->name); char *data = NULL; int datasize = 0; osync_bool ret = TRUE; if (converter->type == CONVERTER_DETECTOR && !converter->convert_func) { change->format = converter->target_format; change->objtype = osync_change_get_objformat(change)->objtype; osync_trace(TRACE_EXIT, "osync_converter_invoke: TRUE: Detector path"); return TRUE; } if (!converter->convert_func) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Invalid converter"); osync_trace(TRACE_EXIT_ERROR, "osync_converter_invoke: %s", osync_error_print(error)); return FALSE; } if (change->data) { //Invoke the converter and all extensions //osync_conv_invoke_extensions(converter->source_format, FALSE, change); osync_bool free_input = FALSE; if ((ret = converter->convert_func(converter_data, change->data, change->size, &data, &datasize, &free_input, error))) { if (converter->type == CONVERTER_DECAP) { if (!free_input) { /* Duplicate the returned data, as the original data will be destroyed */ if (!converter->target_format->copy_func) { /* There is nothing we can do, here. The returned data is a reference, but * we can't copy the data before destroying it */ osync_debug("OSYNC", 0, "Format %s don't have a copy function, but a no-copy converter was registered", converter->target_format->name); osync_error_set(error, OSYNC_ERROR_GENERIC, "Format %s don't have a copy function, but a no-copy converter was registered", converter->target_format->name); osync_trace(TRACE_EXIT_ERROR, "osync_converter_invoke: %s", osync_error_print(error)); return FALSE; } converter->target_format->copy_func(data, datasize, &data, &datasize); } } /* Free the data, unless the converter took the ownership of the data */ if (free_input) { if (converter->source_format->destroy_func) { converter->source_format->destroy_func(change->data, change->size); } else osync_debug("OSYNC", 1, "Format %s don't have a destroy function. Possible memory leak", converter->source_format->name); } change->data = data; change->size = datasize; //osync_conv_invoke_extensions(converter->target_format, TRUE, change); } } if (ret) { osync_debug("OSYNC", 3, "Converting! replacing format %s with %s", converter->source_format->name, converter->target_format->name); change->format = converter->target_format; change->objtype = osync_change_get_objformat(change)->objtype; osync_trace(TRACE_EXIT, "osync_converter_invoke: TRUE"); } else osync_trace(TRACE_EXIT_ERROR, "osync_converter_invoke: %s", osync_error_print(error)); return ret; } OSyncChange *osync_converter_invoke_decap(OSyncFormatConverter *converter, OSyncChange *change, osync_bool *free_output) { osync_trace(TRACE_ENTRY, "osync_converter_invoke_decap(%p, %p, %p)", converter, change, free_output); *free_output = FALSE; if (!converter->convert_func) { osync_trace(TRACE_EXIT_ERROR, "osync_converter_invoke_decap: No convert function"); return NULL; } if (converter->type != CONVERTER_DECAP) { osync_trace(TRACE_EXIT_ERROR, "osync_converter_invoke_decap: Not a decap"); return NULL; } OSyncChange *new_change = osync_change_new(); if (change->changetype != CHANGE_DELETED && change->data) { //Invoke the converter and all extensions OSyncError *error = NULL; if (!converter->convert_func(NULL, change->data, change->size, &(new_change->data), &(new_change->size), free_output, &error)) { osync_trace(TRACE_EXIT_ERROR, "osync_converter_invoke_decap: %s", osync_error_print(&error)); osync_error_free(&error); return NULL; } new_change->has_data = change->has_data; } osync_debug("OSYNC", 3, "Converting! replacing format %s with %s", converter->source_format->name, converter->target_format->name); new_change->format = converter->target_format; new_change->objtype = osync_change_get_objformat(new_change)->objtype; new_change->changetype = change->changetype; osync_trace(TRACE_EXIT, "osync_converter_invoke_decap: %p", new_change); return new_change; } /** Compare the distance of two vertices * * First, try to minimize the losses. Then, * try to minimize the conversions between * different objtypes. Then, try to minimize * the total number of conversions. */ int compare_vertice_distance(const void *a, const void *b) { const vertice *va = a; const vertice *vb = b; if (va->losses < vb->losses) return -1; else if (va->losses > vb->losses) return 1; else if (va->objtype_changes < vb->objtype_changes) return -1; else if (va->objtype_changes > vb->objtype_changes) return 1; else if (va->conversions < vb->conversions) return -1; else if (va->conversions > vb->conversions) return 1; else return 0; } /** Increment a vertice reference count */ /*static void ref_vertice(vertice *v) { v->references++; }*/ /** Dereference an vertice */ static void deref_vertice(vertice *vertice) { /* Decrement the reference count, * and just return if we still * have a reference */ if (--vertice->references > 0) return; g_list_free(vertice->path); if (vertice->change && vertice->free_change) { if (vertice->free_change_data) osync_change_free_data(vertice->change); osync_change_free(vertice->change); } g_free(vertice); } /** Returns a neighbour of the vertice ve * * Returns a new reference to te vertice. The reference * should be dropped using deref_vertice(), later. */ vertice *get_next_vertice_neighbour(OSyncFormatEnv *env, conv_tree *tree, vertice *ve) { GList *c = NULL; osync_trace(TRACE_ENTRY, "get_next_vertice_neighbour(%p, %p, %p:%s)", env, tree, ve, ve->format ? ve->format->name : "None"); for (c = tree->unused; c; c = c->next) { OSyncFormatConverter *converter = c->data; OSyncObjFormat *fmt_target = converter->target_format; /* Check only valid converters, from the right format */ if (strcmp(converter->source_format->name, ve->format->name)) continue; // If the converter type is a detector we need to know wether the input is correct if (converter->detect_func) { if (!ve->change) { osync_trace(TRACE_INTERNAL, "We would call a converter to %s, but there is no change data on vertice", fmt_target->name); continue; } if (ve->change->changetype != CHANGE_DELETED) { if (!converter->detect_func(env, ve->change->data, ve->change->size)) { osync_trace(TRACE_INTERNAL, "Invoked detector for converter from %s to %s: FALSE", converter->source_format->name, converter->target_format->name); continue; } } osync_trace(TRACE_INTERNAL, "Invoked detector for converter from %s to %s: TRUE", converter->source_format->name, converter->target_format->name); } OSyncChange *new_change = NULL; osync_bool free_output = TRUE; if (converter->type == CONVERTER_DECAP) { if (!ve->change) { osync_trace(TRACE_INTERNAL, "A desencapsulator to %s would be called, but we can't because the data on this vertice wasn't converted", fmt_target->name); continue; } if (!(new_change = osync_converter_invoke_decap(converter, ve->change, &free_output))) continue; } /* From this point, we already found an edge (i.e. a converter) that may * be used */ /* Remove the converter from the unused list */ tree->unused = g_list_remove(tree->unused, converter); /* Allocate the new neighbour */ vertice *neigh = g_malloc0(sizeof(vertice)); /* Start with a reference count = 1 */ neigh->references = 1; neigh->format = fmt_target; neigh->path = g_list_copy(ve->path); neigh->path = g_list_append(neigh->path, converter); if (new_change) { neigh->change = new_change; neigh->free_change = TRUE; neigh->free_change_data = free_output; } else { neigh->change = NULL; neigh->free_change = FALSE; neigh->free_change_data = FALSE; } /* Distance calculation */ neigh->conversions = ve->conversions + 1; neigh->losses = ve->losses; if (converter->type == CONVERTER_DECAP) neigh->losses++; neigh->objtype_changes = ve->objtype_changes; if (converter->source_format->objtype != converter->target_format->objtype) neigh->objtype_changes++; osync_trace(TRACE_EXIT, "get_next_vertice_neighbour: %p:%s", neigh, neigh->format ? neigh->format->name : "None"); return neigh; } osync_trace(TRACE_EXIT, "get_next_vertice_neighbour: None found"); return NULL; } /** Search for the shortest path of conversions to one or more formats * * This function search for the shortest path of conversions * that can be made to a change, considering possible detections * that may be necessary. The target is given by a function * that check if a given format is a 'target vertice' or not. The * function is used to allow the search path code to be used * to search for 'objtype detection', search for the path * for an available sink for a format, and maybe other uses. * * The list returned on path_edges should be freed by the caller. * * Note: NEVER use the detection/conversion functions on * CHANGE_DELETED changes. Converting and detecting data * on changes that have no data doesn't make sense * * @see osync_conv_convert_fn(), osync_change_convert(), * osync_conv_convert_fmtlist(), osync_change_convert_member_sink() * * @see target_fn_fmtlist(), target_fn_fmtnames(), * target_fn_simple(), target_fn_fmtname(), * target_fn_membersink(), target_fn_no_any() */ static osync_bool osync_conv_find_path_fn(OSyncFormatEnv *env, OSyncChange *start, OSyncPathTargetFn target_fn, const void *fndata, GList/* OSyncConverter * */ **path_edges) { osync_trace(TRACE_ENTRY, "osync_conv_find_path_fn(%p, %p(%s, %s), %p, %p, %p)", env, start, start ? start->uid : "None", start ? start->format->name : "None", target_fn, fndata, path_edges); g_assert(start->format); *path_edges = NULL; osync_bool ret = FALSE; vertice *result = NULL; //Vertice = Spitze = Format //edge = Kante = Converter //Make a new search tree conv_tree *tree = g_malloc0(sizeof(conv_tree)); tree->unused = g_list_copy(env->converters); //We make our starting point (which is the current format of the //change of course vertice *begin = g_malloc0(sizeof(vertice)); begin->format = start->format; begin->path = NULL; begin->references = 1; begin->change = start; begin->free_change_data = FALSE; begin->free_change = FALSE; tree->search = g_list_append(NULL, begin); while (g_list_length(tree->search)) { vertice *neighbour = NULL; //Get the first vertice and remove it from the queue vertice *current = tree->search->data; tree->search = g_list_remove(tree->search, current); osync_debug("OSCONV", 4, "Next vertice: %s.", current->format->name); /* Check if we have reached a target format */ if (target_fn(fndata, current->format)) { /* Done. return the result */ result = current; break; } osync_debug("OSCONV", 4, "Looking at %s's neighbours.", current->format->name); while ((neighbour = get_next_vertice_neighbour(env, tree, current))) { osync_debug("OSCONV", 4, "%s's neighbour: %s", current->format->name, neighbour->format->name); tree->search = g_list_insert_sorted(tree->search, neighbour, compare_vertice_distance); } /* Done, drop the reference to the vertice */ deref_vertice(current); } /* Remove the references on the search queue */ g_list_foreach(tree->search, (GFunc)deref_vertice, NULL); if (result) { /* Found it. Copy the conversion path */ *path_edges = g_list_copy(result->path); /* Drop the reference to the result vertice */ deref_vertice(result); ret = TRUE; goto free_tree; } free_tree: g_list_free(tree->unused); g_list_free(tree->search); g_free(tree); if (ret) osync_trace(TRACE_EXIT, "osync_conv_find_path_fn: TRUE"); else osync_trace(TRACE_EXIT_ERROR, "osync_conv_find_path_fn: FALSE"); return ret; } osync_bool osync_conv_convert_fn(OSyncFormatEnv *env, OSyncChange *change, OSyncPathTargetFn target_fn, const void *fndata, const char *extension_name, OSyncError **error) { osync_trace(TRACE_ENTRY, "osync_conv_convert_fn(%p, %p, %p, %p, %p)", env, change, target_fn, fndata, error); g_assert(change); g_assert(target_fn); OSyncObjFormat *source = osync_change_get_objformat(change); osync_assert_msg(source, "Cannot convert! change has no objformat!"); GList *path = NULL; osync_bool ret = TRUE; /* Optimization: check if the format is already valid */ if (target_fn(fndata, source)) { osync_trace(TRACE_EXIT, "osync_conv_convert_fn: Target already valid"); return TRUE; } //We can convert the deleted change directly since it has no data /*if (change->changetype == CHANGE_DELETED) { change->format = osync_change_get_initial_objformat(change); change->objtype = osync_change_get_objformat(change)->objtype; if (!target_fn(fndata, source)) { osync_error_set(error, OSYNC_ERROR_GENERIC, "converted delete target would not be valid"); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } osync_trace(TRACE_EXIT, "osync_conv_convert_fn: converted deleted change"); return TRUE; }*/ ret = FALSE; if (!osync_conv_find_path_fn(env, change, target_fn, fndata, &path)) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to find a conversion path to the format requested"); osync_trace(TRACE_EXIT_ERROR, "osync_conv_convert_fn: %s", osync_error_print(error)); goto out; } if (change->changetype == CHANGE_DELETED) { OSyncFormatConverter *converter = g_list_last(path)->data; change->format = converter->target_format; change->objtype = osync_change_get_objformat(change)->objtype; } else { for (; path; path = path->next) { OSyncFormatConverter *converter = path->data; osync_trace(TRACE_INTERNAL, "initialize converter: %p", converter->init_func); //Initialize the converter void *converter_data = NULL; if (converter->init_func) converter_data = converter->init_func(); if (extension_name) { osync_trace(TRACE_INTERNAL, "initialize extension: %s", extension_name); //Initialize the requested extension OSyncFormatExtension *extension = osync_conv_find_extension(env, converter->source_format, converter->target_format, extension_name); osync_trace(TRACE_INTERNAL, "extension: %p", extension); if (extension) extension->init_func(converter_data); } else { osync_trace(TRACE_INTERNAL, "initialize all extensions"); //Initialize all available from extensions GList *e; for (e = env->extensions; e; e = e->next) { OSyncFormatExtension *extension = e->data; osync_trace(TRACE_INTERNAL, "extension: %s", extension->name); osync_trace(TRACE_INTERNAL, "%p:%p %p:%p", extension->from_format, converter->source_format, extension->to_format, converter->target_format); if (extension->from_format == converter->source_format && extension->to_format == converter->target_format) extension->init_func(converter_data); } } if (!osync_converter_invoke(converter, change, converter_data, error)) { osync_trace(TRACE_EXIT_ERROR, "osync_conv_convert_fn: %s", osync_error_print(error)); goto out_free_path; } //Finalize the converter data if (converter->fin_func) converter->fin_func(converter_data); } } ret = TRUE; osync_trace(TRACE_EXIT, "osync_conv_convert_fn: TRUE"); out_free_path: g_list_free(path); out: return ret; } /** Function used on path searchs for a format list * * @see osync_conv_find_path_fn(), osync_conv_convert_fmtlist() */ static osync_bool target_fn_fmtlist(const void *data, OSyncObjFormat *fmt) { const GList/*OSyncObjFormat * */ *l = data; const GList *i; for (i = l; i; i = i->next) { OSyncObjFormat *f = i->data; if (!strcmp(fmt->name, f->name)) return TRUE; } /* else */ return FALSE; } /** Convert a change to the nearest format on a list of formats */ osync_bool osync_conv_convert_fmtlist(OSyncFormatEnv *env, OSyncChange *change, GList/*OSyncObjFormat * */ *targets) { return osync_conv_convert_fn(env, change, target_fn_fmtlist, targets, NULL, NULL); } osync_bool osync_conv_find_path_fmtlist(OSyncFormatEnv *env, OSyncChange *start, GList/*OSyncObjFormat * */ *targets, GList **retlist) { return osync_conv_find_path_fn(env, start, target_fn_fmtlist, targets, retlist); } osync_bool osync_conv_objtype_is_any(const char *objstr) { if (!strcmp(objstr, "data")) return TRUE; return FALSE; } /*@}*/ /** * @defgroup OSyncConvAPI OpenSync Conversion * @ingroup OSyncPublic * @brief Used to convert, compare and detect changes * */ /*@{*/ /*! @brief This will create a new opensync format environment * * The environment will hold all information about plugins, formats etc * * @returns A pointer to a newly allocated environment. NULL on error. * */ OSyncFormatEnv *osync_conv_env_new(OSyncEnv *env) { osync_trace(TRACE_ENTRY, "%s(%p)", __func__, env); OSyncFormatEnv *conv_env = g_malloc0(sizeof(OSyncFormatEnv)); GList *o; //Now we resolve all format plugin stuff for the conv env //First the objecttypes OSyncObjType *type = NULL; for (o = env->objtype_templates; o; o = o->next) { OSyncObjTypeTemplate *otempl = o->data; type = g_malloc0(sizeof(OSyncObjType)); type->name = g_strdup(otempl->name); type->env = conv_env; conv_env->objtypes = g_list_append(conv_env->objtypes, type); } //The formats GList *f = NULL; for (f = env->format_templates; f; f = f->next) { OSyncObjFormatTemplate *ftempl = f->data; OSyncObjType *type = osync_conv_find_objtype(conv_env, ftempl->objtype); g_assert(type); OSyncObjFormat *format = g_malloc0(sizeof(OSyncObjFormat)); format->env = conv_env; format->name = g_strdup(ftempl->name); format->objtype = type; format->cmp_func = ftempl->cmp_func; format->merge_func = ftempl->merge_func; format->duplicate_func = ftempl->duplicate_func; format->copy_func = ftempl->copy_func; format->create_func = ftempl->create_func; format->destroy_func = ftempl->destroy_func; format->print_func = ftempl->print_func; format->revision_func = ftempl->revision_func; format->marshall_func = ftempl->marshall_func; format->demarshall_func = ftempl->demarshall_func; type->formats = g_list_append(type->formats, format); conv_env->objformats = g_list_append(conv_env->objformats, format); } //The extension GList *i; for (i = env->extension_templates; i; i = i->next) { OSyncFormatExtensionTemplate *extension_template = i->data; OSyncObjFormat *from_format = osync_conv_find_objformat(conv_env, extension_template->from_formatname); OSyncObjFormat *to_format = osync_conv_find_objformat(conv_env, extension_template->to_formatname); if (!from_format || !to_format) continue; OSyncFormatExtension *extension = g_malloc0(sizeof(OSyncFormatExtension)); extension->name = g_strdup(extension_template->name); extension->init_func = extension_template->init_func; extension->from_format = from_format; extension->to_format = to_format; conv_env->extensions = g_list_append(conv_env->extensions, extension); } //Converter templates for (i = env->converter_templates; i; i = i->next) { OSyncConverterTemplate *convtmpl = i->data; osync_trace(TRACE_INTERNAL, "New converter from %s to %s", convtmpl->source_format, convtmpl->target_format); OSyncObjFormat *fmt_src = osync_conv_find_objformat(conv_env, convtmpl->source_format); OSyncObjFormat *fmt_trg = osync_conv_find_objformat(conv_env, convtmpl->target_format); if (!fmt_src || !fmt_trg) continue; OSyncFormatConverter *converter = g_malloc0(sizeof(OSyncFormatConverter)); converter->source_format = fmt_src; converter->target_format = fmt_trg; converter->convert_func = convtmpl->convert_func; converter->type = convtmpl->type; converter->init_func = convtmpl->init_func; conv_env->converters = g_list_append(conv_env->converters, converter); } //The detectors for (i = env->data_detectors; i; i = i->next) { OSyncDataDetector *detector = i->data; OSyncFormatConverter *converter = osync_conv_find_converter(conv_env, detector->sourceformat, detector->targetformat); if (!converter) { OSyncObjFormat *fmt_src = osync_conv_find_objformat(conv_env, detector->sourceformat); OSyncObjFormat *fmt_trg = osync_conv_find_objformat(conv_env, detector->targetformat); if (!fmt_src || !fmt_trg) continue; converter = g_malloc0(sizeof(OSyncFormatConverter)); converter->source_format = fmt_src; converter->target_format = fmt_trg; converter->type = CONVERTER_DETECTOR; } converter->detect_func = detector->detect_func; conv_env->converters = g_list_append(conv_env->converters, converter); } //The filters conv_env->filter_functions = g_list_copy(env->filter_functions); osync_conv_set_common_format(conv_env, "contact", "xml-contact", NULL); osync_conv_set_common_format(conv_env, "event", "xml-event", NULL); osync_conv_set_common_format(conv_env, "todo", "xml-todo", NULL); osync_conv_set_common_format(conv_env, "note", "xml-note", NULL); osync_trace(TRACE_EXIT, "%s: %p", __func__, conv_env); return conv_env; } /*! @brief Frees a osync format environment * * Frees a osync format environment and all resources. * * @param env Pointer to the environment to free * */ void osync_conv_env_free(OSyncFormatEnv *env) { g_assert(env); //We need to go through the loaded objtypes and free them. g_free(env); } /*! @brief Sets the common format for a object type * * @param env Pointer to the environment * @param objtypestr The object type name for which to set the common format * @param formatname The name of the format * @param error Pointer to a error struct * @returns TRUE if the format was successfully set * */ osync_bool osync_conv_set_common_format(OSyncFormatEnv *env, const char *objtypestr, const char *formatname, OSyncError **error) { OSyncObjType *type = osync_conv_find_objtype(env, objtypestr); if (!type) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to set a common format: Unable to find the object-type \"%s\"", objtypestr); return FALSE; } OSyncObjFormat *format = osync_conv_find_objformat(env, formatname); if (!format) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to set a common format: Unable to find the format \"%s\"", formatname); return FALSE; } type->common_format = format; return TRUE; } /*! @brief Finds the object type with the given name * * @param env Pointer to the environment * @param name Name of the object type to find * @returns The object type, or NULL if not found * */ OSyncObjType *osync_conv_find_objtype(OSyncFormatEnv *env, const char *name) { g_assert(env); g_assert(name); GList *element = NULL; for (element = env->objtypes; element; element = element->next) { OSyncObjType *type = element->data; if (!strcmp(type->name, name)) return type; } osync_debug("CONV", 1, "Unable to find the requested objtype \"%s\"", name); return NULL; } /*! @brief Returns the number of available object types * * @param env Pointer to the environment * @returns The number of object types * */ int osync_conv_num_objtypes(OSyncFormatEnv *env) { g_assert(env); return g_list_length(env->objtypes); } /*! @brief Gets the nth object type * * @param env Pointer to the environment * @param nth The number * @returns The object type, or NULL if there is no such object type * */ OSyncObjType *osync_conv_nth_objtype(OSyncFormatEnv *env, int nth) { g_assert(env); return g_list_nth_data(env->objtypes, nth); } /*! @brief Finds the object format with the given name * * @param env Pointer to the environment * @param name Name of the format type to find * @returns The object format, or NULL if not found * */ OSyncObjFormat *osync_conv_find_objformat(OSyncFormatEnv *env, const char *name) { g_assert(env); g_assert(name); GList *element = NULL; for (element = env->objformats; element; element = element->next) { OSyncObjFormat *format = element->data; if (!strcmp(format->name, name)) return format; } return NULL; } /*! @brief Returns the number of available object formats * * @param type The object type for whih to lookup the formats * @returns The number of object formats * */ int osync_conv_num_objformats(OSyncObjType *type) { g_assert(type); return g_list_length(type->formats); } /*! @brief Gets the nth object format * * @param type The object for which to get the nth format * @param nth The number * @returns The object format, or NULL if there is no such object type * */ OSyncObjFormat *osync_conv_nth_objformat(OSyncObjType *type, int nth) { g_assert(type); return g_list_nth_data(type->formats, nth); } /*! @brief Finds the converter with the given source and target format * * @param env Pointer to the environment * @param sourcename Name of the source format * @param targetname Name of the target format * @returns The converter, or NULL if not found * */ OSyncFormatConverter *osync_conv_find_converter(OSyncFormatEnv *env, const char *sourcename, const char *targetname) { g_assert(env); g_assert(sourcename); g_assert(targetname); OSyncObjFormat *fmt_src = osync_conv_find_objformat(env, sourcename); if (!fmt_src) return NULL; OSyncObjFormat *fmt_trg = osync_conv_find_objformat(env, targetname); if (!fmt_trg) return NULL; return osync_conv_find_converter_objformat(env, fmt_src, fmt_trg); } /*! @brief Finds the extension that will be invoked when going from the given source to the target format with the given name * * @param env Pointer to the environment * @param from_format From Format * @param to_format To Format * @param extension_name The name of the extension to search * @returns The extension, or NULL if not found * */ OSyncFormatExtension *osync_conv_find_extension(OSyncFormatEnv *env, OSyncObjFormat *from_format, OSyncObjFormat *to_format, const char *extension_name) { g_assert(env); g_assert(extension_name); GList *i = NULL; for (i = env->extensions; i; i = i->next) { OSyncFormatExtension *extension = i->data; osync_trace(TRACE_INTERNAL, "comparing format %p:%p %p:%p name %s:%s", extension->from_format, from_format, extension->to_format, to_format, extension->name, extension_name); if ((extension->from_format == from_format || !from_format) && (extension->to_format == to_format || !to_format) && !strcmp(extension->name, extension_name)) return extension; } return NULL; } /*! @brief Returns the name of a object type * * @param type The object type * @returns The name of the object type * */ const char *osync_objtype_get_name(OSyncObjType *type) { g_assert(type); return type->name; } /*! @brief Returns the name of a object format * * @param format The object format * @returns The name of the object format * */ const char *osync_objformat_get_name(OSyncObjFormat *format) { g_assert(format); return format->name; } /*! @brief Returns the object type of a format * * @param format The object format * @returns The object type * */ OSyncObjType *osync_objformat_get_objtype(OSyncObjFormat *format) { g_assert(format); return format->objtype; } /*@}*/