Permalink
376d314 Oct 1, 2017
@ximion @lucasmoura @tintou @iainlane
5713 lines (5051 sloc) 155 KB
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2012-2016 Matthias Klumpp <matthias@tenstral.net>
*
* Licensed under the GNU Lesser General Public License Version 2.1
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "as-component.h"
#include "as-component-private.h"
#include <glib.h>
#include <glib-object.h>
#include "as-utils.h"
#include "as-utils-private.h"
#include "as-stemmer.h"
#include "as-variant-cache.h"
#include "as-icon-private.h"
#include "as-screenshot-private.h"
#include "as-bundle-private.h"
#include "as-release-private.h"
#include "as-translation-private.h"
#include "as-suggested-private.h"
#include "as-content-rating-private.h"
#include "as-launchable-private.h"
#include "as-provided-private.h"
/**
* SECTION:as-component
* @short_description: Object representing a software component
* @include: appstream.h
*
* This object represents an Appstream software component which is associated
* to a package in the distribution's repositories.
* A component can be anything, ranging from an application to a font, a codec or
* even a non-visual software project providing libraries and python-modules for
* other applications to use.
*
* The type of the component is stored as #AsComponentKind and can be queried to
* find out which kind of component we're dealing with.
*
* See also: #AsProvidesKind, #AsDatabase
*/
typedef struct
{
AsComponentKind kind;
AsComponentScope scope;
AsOriginKind origin_kind;
AsContext *context; /* the document context associated with this component */
gchar *active_locale_override;
gchar *id;
gchar *data_id;
gchar *origin;
gchar **pkgnames;
gchar *source_pkgname;
GHashTable *name; /* localized entry */
GHashTable *summary; /* localized entry */
GHashTable *description; /* localized entry */
GHashTable *keywords; /* localized entry, value:strv */
GHashTable *developer_name; /* localized entry */
gchar *metadata_license;
gchar *project_license;
gchar *project_group;
GPtrArray *launchables; /* of #AsLaunchable */
GPtrArray *categories; /* of utf8 */
GPtrArray *compulsory_for_desktops; /* of utf8 */
GPtrArray *extends; /* of utf8 */
GPtrArray *addons; /* of AsComponent */
GPtrArray *screenshots; /* of AsScreenshot elements */
GPtrArray *releases; /* of AsRelease elements */
GPtrArray *provided; /* of AsProvided */
GPtrArray *bundles; /* of AsBundle */
GPtrArray *suggestions; /* of AsSuggested elements */
GPtrArray *content_ratings; /* of AsContentRating */
GHashTable *urls; /* of int:utf8 */
GHashTable *languages; /* of utf8:utf8 */
GPtrArray *translations; /* of AsTranslation */
GPtrArray *icons; /* of AsIcon elements */
gchar *arch; /* the architecture this data was generated from */
gint priority; /* used internally */
AsMergeKind merge_kind; /* whether and how the component data should be merged */
guint sort_score; /* used to priorize components in listings */
gsize token_cache_valid;
GHashTable *token_cache; /* of utf8:AsTokenType* */
AsValueFlags value_flags;
gboolean ignored; /* whether we should ignore this component */
GHashTable *custom; /* free-form user-defined custom data */
} AsComponentPrivate;
typedef enum {
AS_TOKEN_MATCH_NONE = 0,
AS_TOKEN_MATCH_MIMETYPE = 1 << 0,
AS_TOKEN_MATCH_PKGNAME = 1 << 1,
AS_TOKEN_MATCH_DESCRIPTION = 1 << 2,
AS_TOKEN_MATCH_SUMMARY = 1 << 3,
AS_TOKEN_MATCH_KEYWORD = 1 << 4,
AS_TOKEN_MATCH_NAME = 1 << 5,
AS_TOKEN_MATCH_ID = 1 << 6,
AS_TOKEN_MATCH_LAST
} AsTokenMatch;
G_DEFINE_TYPE_WITH_PRIVATE (AsComponent, as_component, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (as_component_get_instance_private (o))
enum {
AS_COMPONENT_DUMMY_PROPERTY,
AS_COMPONENT_KIND,
AS_COMPONENT_PKGNAMES,
AS_COMPONENT_ID,
AS_COMPONENT_NAME,
AS_COMPONENT_SUMMARY,
AS_COMPONENT_DESCRIPTION,
AS_COMPONENT_KEYWORDS,
AS_COMPONENT_ICONS,
AS_COMPONENT_URLS,
AS_COMPONENT_CATEGORIES,
AS_COMPONENT_PROJECT_LICENSE,
AS_COMPONENT_PROJECT_GROUP,
AS_COMPONENT_DEVELOPER_NAME,
AS_COMPONENT_SCREENSHOTS
};
/**
* as_component_kind_get_type:
*
* Defines registered component types.
*/
GType
as_component_kind_get_type (void)
{
static volatile gsize as_component_kind_type_id__volatile = 0;
if (g_once_init_enter (&as_component_kind_type_id__volatile)) {
static const GEnumValue values[] = {
{AS_COMPONENT_KIND_UNKNOWN, "AS_COMPONENT_KIND_UNKNOWN", "unknown"},
{AS_COMPONENT_KIND_GENERIC, "AS_COMPONENT_KIND_GENERIC", "generic"},
{AS_COMPONENT_KIND_DESKTOP_APP, "AS_COMPONENT_KIND_DESKTOP_APP", "desktop-app"},
{AS_COMPONENT_KIND_CONSOLE_APP, "AS_COMPONENT_KIND_CONSOLE_APP", "console-app"},
{AS_COMPONENT_KIND_WEB_APP, "AS_COMPONENT_KIND_WEB_APP", "web-app"},
{AS_COMPONENT_KIND_ADDON, "AS_COMPONENT_KIND_ADDON", "addon"},
{AS_COMPONENT_KIND_FONT, "AS_COMPONENT_KIND_FONT", "font"},
{AS_COMPONENT_KIND_CODEC, "AS_COMPONENT_KIND_CODEC", "codec"},
{AS_COMPONENT_KIND_INPUTMETHOD, "AS_COMPONENT_KIND_INPUTMETHOD", "inputmethod"},
{AS_COMPONENT_KIND_FIRMWARE, "AS_COMPONENT_KIND_FIRMWARE", "firmware"},
{AS_COMPONENT_KIND_DRIVER, "AS_COMPONENT_KIND_DRIVER", "driver"},
{AS_COMPONENT_KIND_LOCALIZATION, "AS_COMPONENT_KIND_LOCALIZATION", "localization"},
{AS_COMPONENT_KIND_SERVICE, "AS_COMPONENT_KIND_SERVICE", "service"},
{0, NULL, NULL}
};
GType as_component_type_type_id;
as_component_type_type_id = g_enum_register_static ("AsComponentKind", values);
g_once_init_leave (&as_component_kind_type_id__volatile, as_component_type_type_id);
}
return as_component_kind_type_id__volatile;
}
/**
* as_component_kind_to_string:
* @kind: the #AsComponentKind.
*
* Converts the enumerated value to an text representation.
*
* Returns: string version of @kind
**/
const gchar*
as_component_kind_to_string (AsComponentKind kind)
{
if (kind == AS_COMPONENT_KIND_GENERIC)
return "generic";
if (kind == AS_COMPONENT_KIND_DESKTOP_APP)
return "desktop-application";
if (kind == AS_COMPONENT_KIND_CONSOLE_APP)
return "console-application";
if (kind == AS_COMPONENT_KIND_WEB_APP)
return "web-application";
if (kind == AS_COMPONENT_KIND_ADDON)
return "addon";
if (kind == AS_COMPONENT_KIND_FONT)
return "font";
if (kind == AS_COMPONENT_KIND_CODEC)
return "codec";
if (kind == AS_COMPONENT_KIND_INPUTMETHOD)
return "inputmethod";
if (kind == AS_COMPONENT_KIND_FIRMWARE)
return "firmware";
if (kind == AS_COMPONENT_KIND_DRIVER)
return "driver";
if (kind == AS_COMPONENT_KIND_LOCALIZATION)
return "localization";
if (kind == AS_COMPONENT_KIND_SERVICE)
return "service";
return "unknown";
}
/**
* as_component_kind_from_string:
* @kind_str: the string.
*
* Converts the text representation to an enumerated value.
*
* Returns: a #AsComponentKind or %AS_COMPONENT_KIND_UNKNOWN for unknown
**/
AsComponentKind
as_component_kind_from_string (const gchar *kind_str)
{
if (kind_str == NULL)
return AS_COMPONENT_KIND_GENERIC;
if (g_strcmp0 (kind_str, "generic") == 0)
return AS_COMPONENT_KIND_GENERIC;
if (g_strcmp0 (kind_str, "desktop-application") == 0)
return AS_COMPONENT_KIND_DESKTOP_APP;
if (g_strcmp0 (kind_str, "console-application") == 0)
return AS_COMPONENT_KIND_CONSOLE_APP;
if (g_strcmp0 (kind_str, "web-application") == 0)
return AS_COMPONENT_KIND_WEB_APP;
if (g_strcmp0 (kind_str, "addon") == 0)
return AS_COMPONENT_KIND_ADDON;
if (g_strcmp0 (kind_str, "font") == 0)
return AS_COMPONENT_KIND_FONT;
if (g_strcmp0 (kind_str, "codec") == 0)
return AS_COMPONENT_KIND_CODEC;
if (g_strcmp0 (kind_str, "inputmethod") == 0)
return AS_COMPONENT_KIND_INPUTMETHOD;
if (g_strcmp0 (kind_str, "firmware") == 0)
return AS_COMPONENT_KIND_FIRMWARE;
if (g_strcmp0 (kind_str, "driver") == 0)
return AS_COMPONENT_KIND_DRIVER;
if (g_strcmp0 (kind_str, "localization") == 0)
return AS_COMPONENT_KIND_LOCALIZATION;
if (g_strcmp0 (kind_str, "service") == 0)
return AS_COMPONENT_KIND_SERVICE;
/* legacy */
if (g_strcmp0 (kind_str, "desktop") == 0)
return AS_COMPONENT_KIND_DESKTOP_APP;
if (g_strcmp0 (kind_str, "desktop-app") == 0)
return AS_COMPONENT_KIND_DESKTOP_APP;
return AS_COMPONENT_KIND_UNKNOWN;
}
/**
* as_merge_kind_to_string:
* @kind: the #AsMergeKind.
*
* Converts the enumerated value to an text representation.
*
* Returns: string version of @kind
**/
const gchar*
as_merge_kind_to_string (AsMergeKind kind)
{
if (kind == AS_MERGE_KIND_NONE)
return "none";
if (kind == AS_MERGE_KIND_REPLACE)
return "replace";
if (kind == AS_MERGE_KIND_APPEND)
return "append";
return "unknown";
}
/**
* as_merge_kind_from_string:
* @kind_str: the string.
*
* Converts the text representation to an enumerated value.
*
* Returns: a #AsMergeKind or %AS_MERGE_KIND_NONE for unknown
**/
AsMergeKind
as_merge_kind_from_string (const gchar *kind_str)
{
if (g_strcmp0 (kind_str, "replace") == 0)
return AS_MERGE_KIND_REPLACE;
if (g_strcmp0 (kind_str, "append") == 0)
return AS_MERGE_KIND_APPEND;
return AS_MERGE_KIND_NONE;
}
/**
* as_component_scope_to_string:
* @scope: the #AsComponentScope.
*
* Converts the enumerated value to an text representation.
*
* Returns: string version of @scope
**/
const gchar*
as_component_scope_to_string (AsComponentScope scope)
{
if (scope == AS_COMPONENT_SCOPE_SYSTEM)
return "system";
if (scope == AS_COMPONENT_SCOPE_USER)
return "user";
return "unknown";
}
/**
* as_component_scope_from_string:
* @scope_str: the string.
*
* Converts the text representation to an enumerated value.
*
* Returns: a #AsComponentScope or %AS_COMPONENT_SCOPE_UNKNOWN for unknown
**/
AsMergeKind
as_component_scope_from_string (const gchar *scope_str)
{
if (g_strcmp0 (scope_str, "system") == 0)
return AS_COMPONENT_SCOPE_SYSTEM;
if (g_strcmp0 (scope_str, "user") == 0)
return AS_COMPONENT_SCOPE_USER;
return AS_COMPONENT_SCOPE_UNKNOWN;
}
/**
* as_component_init:
**/
static void
as_component_init (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
/* translatable entities */
priv->name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->summary = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->description = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->developer_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->keywords = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
/* lists */
priv->launchables = g_ptr_array_new_with_free_func (g_object_unref);
priv->categories = g_ptr_array_new_with_free_func (g_free);
priv->compulsory_for_desktops = g_ptr_array_new_with_free_func (g_free);
priv->screenshots = g_ptr_array_new_with_free_func (g_object_unref);
priv->releases = g_ptr_array_new_with_free_func (g_object_unref);
priv->provided = g_ptr_array_new_with_free_func (g_object_unref);
priv->bundles = g_ptr_array_new_with_free_func (g_object_unref);
priv->extends = g_ptr_array_new_with_free_func (g_free);
priv->addons = g_ptr_array_new_with_free_func (g_object_unref);
priv->suggestions = g_ptr_array_new_with_free_func (g_object_unref);
priv->icons = g_ptr_array_new_with_free_func (g_object_unref);
/* others */
priv->urls = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
priv->languages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
priv->custom = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->content_ratings = g_ptr_array_new_with_free_func (g_object_unref);
priv->token_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
priv->priority = 0;
}
/**
* as_component_finalize:
*/
static void
as_component_finalize (GObject* object)
{
AsComponent *cpt = AS_COMPONENT (object);
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->id);
g_free (priv->data_id);
g_strfreev (priv->pkgnames);
g_free (priv->metadata_license);
g_free (priv->project_license);
g_free (priv->project_group);
g_free (priv->active_locale_override);
g_free (priv->arch);
g_hash_table_unref (priv->name);
g_hash_table_unref (priv->summary);
g_hash_table_unref (priv->description);
g_hash_table_unref (priv->developer_name);
g_hash_table_unref (priv->keywords);
g_ptr_array_unref (priv->launchables);
g_ptr_array_unref (priv->categories);
g_ptr_array_unref (priv->compulsory_for_desktops);
g_ptr_array_unref (priv->screenshots);
g_ptr_array_unref (priv->releases);
g_ptr_array_unref (priv->provided);
g_ptr_array_unref (priv->bundles);
g_ptr_array_unref (priv->extends);
g_ptr_array_unref (priv->addons);
g_ptr_array_unref (priv->suggestions);
g_hash_table_unref (priv->urls);
g_hash_table_unref (priv->languages);
g_hash_table_unref (priv->custom);
g_ptr_array_unref (priv->content_ratings);
g_ptr_array_unref (priv->icons);
if (priv->translations != NULL)
g_ptr_array_unref (priv->translations);
g_hash_table_unref (priv->token_cache);
if (priv->context != NULL)
g_object_unref (priv->context);
G_OBJECT_CLASS (as_component_parent_class)->finalize (object);
}
/**
* as_component_invalidate_data_id:
*
* Internal method to mark the metadata-ID as outdated, so
* it will be regenerated next time.
*/
static void
as_component_invalidate_data_id (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (priv->data_id == NULL)
return;
g_free (priv->data_id);
priv->data_id = NULL;
}
/**
* as_component_is_valid:
* @cpt: a #AsComponent instance.
*
* Check if the essential properties of this Component are
* populated with useful data.
*
* Returns: TRUE if the component data was validated successfully.
*/
gboolean
as_component_is_valid (AsComponent *cpt)
{
const gchar *cname;
const gchar *csummary;
AsComponentKind ctype;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
ctype = priv->kind;
if (ctype == AS_COMPONENT_KIND_UNKNOWN)
return FALSE;
if (priv->merge_kind != AS_MERGE_KIND_NONE) {
/* merge components only need an ID to be valid */
return !as_str_empty (priv->id);
}
cname = as_component_get_name (cpt);
csummary = as_component_get_summary (cpt);
if ((!as_str_empty (priv->id)) &&
(!as_str_empty (cname)) &&
(!as_str_empty (csummary))) {
return TRUE;
}
return FALSE;
}
/**
* as_component_to_string:
* @cpt: a #AsComponent instance.
*
* Returns a string identifying this component.
* (useful for debugging)
*
* Returns: (transfer full): A descriptive string
**/
gchar*
as_component_to_string (AsComponent *cpt)
{
gchar* res = NULL;
g_autofree gchar *pkgs = NULL;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (as_component_has_package (cpt))
pkgs = g_strjoinv (",", priv->pkgnames);
else
pkgs = g_strdup ("<none>");
res = g_strdup_printf ("[%s::%s]> name: %s | summary: %s | package: %s",
as_component_kind_to_string (priv->kind),
as_component_get_data_id (cpt),
as_component_get_name (cpt),
as_component_get_summary (cpt),
pkgs);
return res;
}
/**
* as_component_add_screenshot:
* @cpt: a #AsComponent instance.
* @sshot: The #AsScreenshot to add
*
* Add an #AsScreenshot to this component.
**/
void
as_component_add_screenshot (AsComponent *cpt, AsScreenshot* sshot)
{
GPtrArray* sslist;
sslist = as_component_get_screenshots (cpt);
g_ptr_array_add (sslist, g_object_ref (sshot));
}
/**
* as_component_get_releases:
* @cpt: a #AsComponent instance.
*
* Get an array of the #AsRelease items this component
* provides.
*
* Return value: (element-type AsRelease) (transfer none): A list of releases
**/
GPtrArray*
as_component_get_releases (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->releases;
}
/**
* as_component_add_release:
* @cpt: a #AsComponent instance.
* @release: The #AsRelease to add
*
* Add an #AsRelease to this component.
**/
void
as_component_add_release (AsComponent *cpt, AsRelease* release)
{
GPtrArray* releases;
releases = as_component_get_releases (cpt);
g_ptr_array_add (releases, g_object_ref (release));
}
/**
* as_component_get_url:
* @cpt: a #AsComponent instance.
* @url_kind: the URL kind, e.g. %AS_URL_KIND_HOMEPAGE.
*
* Gets a URL.
*
* Returns: (nullable): string, or %NULL if unset
*
* Since: 0.6.2
**/
const gchar*
as_component_get_url (AsComponent *cpt, AsUrlKind url_kind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return g_hash_table_lookup (priv->urls,
GINT_TO_POINTER (url_kind));
}
/**
* as_component_add_url:
* @cpt: a #AsComponent instance.
* @url_kind: the URL kind, e.g. %AS_URL_KIND_HOMEPAGE
* @url: the full URL.
*
* Adds some URL data to the component.
*
* Since: 0.6.2
**/
void
as_component_add_url (AsComponent *cpt, AsUrlKind url_kind, const gchar *url)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_hash_table_insert (priv->urls,
GINT_TO_POINTER (url_kind),
g_strdup (url));
}
/**
* as_component_get_extends:
* @cpt: an #AsComponent instance.
*
* Returns a string list of IDs of components which
* are extended by this addon.
*
* See %as_component_get_extends() for the reverse.
*
* Returns: (element-type utf8) (transfer none): A #GPtrArray or %NULL if not set.
*
* Since: 0.7.0
**/
GPtrArray*
as_component_get_extends (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->extends;
}
/**
* as_component_add_extends:
* @cpt: a #AsComponent instance.
* @cpt_id: The id of a component which is extended by this component
*
* Add a reference to the extended component
*
* Since: 0.7.0
**/
void
as_component_add_extends (AsComponent* cpt, const gchar* cpt_id)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (as_flags_contains (priv->value_flags, AS_VALUE_FLAG_DUPLICATE_CHECK)) {
/* check for duplicates */
if (as_ptr_array_find_string (priv->extends, cpt_id) != NULL)
return;
}
g_ptr_array_add (priv->extends,
g_strdup (cpt_id));
}
/**
* as_component_get_addons:
* @cpt: an #AsComponent instance.
*
* Returns a list of #AsComponent objects which
* are addons extending this component in functionality.
*
* This is the reverse of %as_component_get_extends()
*
* Returns: (transfer none) (element-type AsComponent): An array of #AsComponent.
*
* Since: 0.9.2
**/
GPtrArray*
as_component_get_addons (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->addons;
}
/**
* as_component_add_addon:
* @cpt: a #AsComponent instance.
* @addon: The #AsComponent that extends @cpt
*
* Add a reference to the addon that is enhancing this component.
*
* Since: 0.9.2
**/
void
as_component_add_addon (AsComponent* cpt, AsComponent *addon)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_add (priv->addons, g_object_ref (addon));
}
/**
* as_component_get_bundles:
* @cpt: a #AsComponent instance.
*
* Get a list of all software bundles associated with this component.
*
* Returns: (transfer none) (element-type AsBundle): A list of #AsBundle.
*
* Since: 0.10
**/
GPtrArray*
as_component_get_bundles (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->bundles;
}
/**
* as_component_set_bundles_array:
* @cpt: a #AsComponent instance.
*
* Internal helper.
**/
void
as_component_set_bundles_array (AsComponent *cpt, GPtrArray *bundles)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_unref (priv->bundles);
priv->bundles = g_ptr_array_ref (bundles);
as_component_invalidate_data_id (cpt);
}
/**
* as_component_get_bundle:
* @cpt: a #AsComponent instance.
* @bundle_kind: the bundle kind, e.g. %AS_BUNDLE_KIND_LIMBA.
*
* Gets a bundle identifier string.
*
* Returns: (transfer none): An #AsBundle, or %NULL if not set.
*
* Since: 0.8.0
**/
AsBundle*
as_component_get_bundle (AsComponent *cpt, AsBundleKind bundle_kind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
for (i = 0; i < priv->bundles->len; i++) {
AsBundle *bundle = AS_BUNDLE (g_ptr_array_index (priv->bundles, i));
if (as_bundle_get_kind (bundle) == bundle_kind)
return bundle;
}
return NULL;
}
/**
* as_component_add_bundle:
* @cpt: a #AsComponent instance.
* @bundle: The #AsBundle to add.
*
* Adds a bundle to the component.
*
* Since: 0.8.0
**/
void
as_component_add_bundle (AsComponent *cpt, AsBundle *bundle)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_add (priv->bundles,
g_object_ref (bundle));
as_component_invalidate_data_id (cpt);
}
/**
* as_component_has_bundle:
* @cpt: a #AsComponent instance.
*
* Returns: %TRUE if this component has a bundle associated.
**/
gboolean
as_component_has_bundle (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->bundles->len > 0;
}
/**
* as_component_has_package:
* @cpt: a #AsComponent instance.
*
* Internal function.
**/
gboolean
as_component_has_package (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return (priv->pkgnames != NULL) && (priv->pkgnames[0] != NULL);
}
/**
* as_component_has_install_candidate:
* @cpt: a #AsComponent instance.
*
* Returns: %TRUE if the component has an installable component (package, bundle, ...) set.
**/
gboolean
as_component_has_install_candidate (AsComponent *cpt)
{
return as_component_has_bundle (cpt) || as_component_has_package (cpt);
}
/**
* as_component_get_kind:
* @cpt: a #AsComponent instance.
*
* Returns the #AsComponentKind of this component.
*
* Returns: the kind of #this.
*/
AsComponentKind
as_component_get_kind (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->kind;
}
/**
* as_component_set_kind:
* @cpt: a #AsComponent instance.
* @value: the #AsComponentKind.
*
* Sets the #AsComponentKind of this component.
*/
void
as_component_set_kind (AsComponent *cpt, AsComponentKind value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->kind = value;
g_object_notify ((GObject *) cpt, "kind");
}
/**
* as_component_get_pkgnames:
* @cpt: a #AsComponent instance.
*
* Get a list of package names which this component consists of.
* This usually is just one package name.
*
* Returns: (transfer none): String array of package names
*/
gchar**
as_component_get_pkgnames (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->pkgnames;
}
/**
* as_component_get_pkgname:
* @cpt: a #AsComponent instance.
*
* Get the first package name of the list of packages that need to be installed
* for this component to be present on the system.
* Since most components consist of only one package, this is safe to use for
* about 90% of all cases.
*
* However, to support a component fully, please use %as_component_get_pkgnames() for
* getting all packages that need to be installed, and use this method only to
* e.g. get the main package to perform a quick "is it installed?" check.
*
* Returns: (transfer none): String array of package names
*/
gchar*
as_component_get_pkgname (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if ((priv->pkgnames == NULL) || (priv->pkgnames[0] == NULL))
return NULL;
return priv->pkgnames[0];
}
/**
* as_component_set_pkgnames:
* @cpt: a #AsComponent instance.
* @packages: (array zero-terminated=1):
*
* Set a list of package names this component consists of.
* (This should usually be just one package name)
*/
void
as_component_set_pkgnames (AsComponent *cpt, gchar **packages)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_strfreev (priv->pkgnames);
priv->pkgnames = g_strdupv (packages);
g_object_notify ((GObject *) cpt, "pkgnames");
}
/**
* as_component_get_source_pkgname:
* @cpt: a #AsComponent instance.
*
* Returns: the source package name.
*/
const gchar*
as_component_get_source_pkgname (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->source_pkgname;
}
/**
* as_component_set_source_pkgname:
* @cpt: a #AsComponent instance.
* @spkgname: the source package name.
*/
void
as_component_set_source_pkgname (AsComponent *cpt, const gchar* spkgname)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->source_pkgname);
priv->source_pkgname = g_strdup (spkgname);
}
/**
* as_component_get_id:
* @cpt: a #AsComponent instance.
*
* Get the unique AppStream identifier for this component.
* This ID is unique for the described component, but does
* not uniquely identify the metadata set.
*
* For a unique ID for this metadata set in the current
* session, use %as_component_get_data_id()
*
* Returns: the unique AppStream identifier.
*/
const gchar*
as_component_get_id (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->id;
}
/**
* as_component_set_id:
* @cpt: a #AsComponent instance.
* @value: the unique identifier.
*
* Set the AppStream identifier for this component.
*/
void
as_component_set_id (AsComponent *cpt, const gchar* value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->id);
priv->id = g_strdup (value);
g_object_notify ((GObject *) cpt, "id");
as_component_invalidate_data_id (cpt);
}
/**
* as_component_get_data_id:
* @cpt: a #AsComponent instance.
*
* Get a unique identifier for this metadata set.
* This unique ID is only valid for the current session,
* as opposed to the AppStream ID which uniquely identifies
* a software component.
*
* The format of the unique id usually is:
* %{scope}/%{origin}/%{distribution_system}/%{appstream_id}
*
* For example:
* system/os/package/org.example.FooBar
*
* Returns: the unique session-specific identifier.
*/
const gchar*
as_component_get_data_id (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (priv->data_id == NULL)
priv->data_id = as_utils_build_data_id_for_cpt (cpt);
return priv->data_id;
}
/**
* as_component_set_data_id:
* @cpt: a #AsComponent instance.
* @value: the unique session-specific identifier.
*
* Set the session-specific unique metadata identifier for this
* component.
* If two components have a different data_id but the same ID,
* they will be treated as independent sets of metadata describing
* the same component type.
*/
void
as_component_set_data_id (AsComponent *cpt, const gchar* value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->data_id);
priv->data_id = g_strdup (value);
}
/**
* as_component_get_origin:
* @cpt: a #AsComponent instance.
*/
const gchar*
as_component_get_origin (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if ((priv->context != NULL) && (priv->origin == NULL))
return as_context_get_origin (priv->context);
return priv->origin;
}
/**
* as_component_set_origin:
* @cpt: a #AsComponent instance.
* @origin: the origin.
*/
void
as_component_set_origin (AsComponent *cpt, const gchar *origin)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->origin);
priv->origin = g_strdup (origin);
as_component_invalidate_data_id (cpt);
}
/**
* as_component_get_architecture:
* @cpt: a #AsComponent instance.
*
* The architecture of the software component this data was generated from.
*/
const gchar*
as_component_get_architecture (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if ((priv->context != NULL) && (priv->arch == NULL))
return as_context_get_architecture (priv->context);
return priv->arch;
}
/**
* as_component_set_architecture:
* @cpt: a #AsComponent instance.
* @arch: the architecture string.
*/
void
as_component_set_architecture (AsComponent *cpt, const gchar *arch)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->arch);
priv->arch = g_strdup (arch);
}
/**
* as_component_get_active_locale:
* @cpt: a #AsComponent instance.
*
* Get the current active locale for this component, which
* is used to get localized messages.
*
* Returns: the current active locale.
*/
const gchar*
as_component_get_active_locale (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
const gchar *locale;
/* return context locale, if the locale isn't explicitly overridden for this component */
if ((priv->context != NULL) && (priv->active_locale_override == NULL)) {
locale = as_context_get_locale (priv->context);
} else {
locale = priv->active_locale_override;
}
if (locale == NULL)
return "C";
else
return locale;
}
/**
* as_component_set_active_locale:
* @cpt: a #AsComponent instance.
* @locale: (nullable): the locale, or %NULL. e.g. "en_GB"
*
* Set the current active locale for this component, which
* is used to get localized messages.
* If the #AsComponent was fetched from a localized database, usually only
* one locale is available.
*/
void
as_component_set_active_locale (AsComponent *cpt, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->active_locale_override);
priv->active_locale_override = g_strdup (locale);
}
/**
* as_component_localized_get:
* @cpt: a #AsComponent instance.
* @lht: (element-type utf8 utf8): the #GHashTable on which the value will be retreived.
*
* Helper function to get a localized property using the current
* active locale for this component.
*/
static const gchar*
as_component_localized_get (AsComponent *cpt, GHashTable *lht)
{
gchar *msg;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
msg = g_hash_table_lookup (lht, as_component_get_active_locale (cpt));
if ((msg == NULL) && (!as_flags_contains (priv->value_flags, AS_VALUE_FLAG_NO_TRANSLATION_FALLBACK))) {
/* fall back to untranslated / default */
msg = g_hash_table_lookup (lht, "C");
}
return msg;
}
/**
* as_component_localized_set:
* @cpt: a #AsComponent instance.
* @lht: (element-type utf8 utf8): the #GHashTable on which the value will be added.
* @value: the value to add.
* @locale: (nullable): the locale, or %NULL. e.g. "en_GB".
*
* Helper function to set a localized property.
*/
static void
as_component_localized_set (AsComponent *cpt, GHashTable *lht, const gchar* value, const gchar *locale)
{
/* if no locale was specified, we assume the default locale */
/* CAVE: %NULL does NOT mean lang=C! */
if (locale == NULL)
locale = as_component_get_active_locale (cpt);
g_hash_table_insert (lht,
as_locale_strip_encoding (g_strdup (locale)),
g_strdup (value));
}
/**
* as_component_get_name:
* @cpt: a #AsComponent instance.
*
* A human-readable name for this component.
*
* Returns: the name.
*/
const gchar*
as_component_get_name (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return as_component_localized_get (cpt, priv->name);
}
/**
* as_component_set_name:
* @cpt: A valid #AsComponent
* @value: The name
* @locale: (nullable): The locale the used for this value, or %NULL to use the current active one.
*
* Set a human-readable name for this component.
*/
void
as_component_set_name (AsComponent *cpt, const gchar* value, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
as_component_localized_set (cpt, priv->name, value, locale);
g_object_notify ((GObject *) cpt, "name");
}
/**
* as_component_get_summary:
* @cpt: a #AsComponent instance.
*
* Get a short description of this component.
*
* Returns: the summary.
*/
const gchar*
as_component_get_summary (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return as_component_localized_get (cpt, priv->summary);
}
/**
* as_component_set_summary:
* @cpt: A valid #AsComponent
* @value: The summary
* @locale: (nullable): The locale the used for this value, or %NULL to use the current active one.
*
* Set a short description for this component.
*/
void
as_component_set_summary (AsComponent *cpt, const gchar* value, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
as_component_localized_set (cpt, priv->summary, value, locale);
g_object_notify ((GObject *) cpt, "summary");
}
/**
* as_component_get_description:
* @cpt: a #AsComponent instance.
*
* Get the localized long description of this component.
*
* Returns: the description.
*/
const gchar*
as_component_get_description (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return as_component_localized_get (cpt, priv->description);
}
/**
* as_component_set_description:
* @cpt: A valid #AsComponent
* @value: The long description
* @locale: (nullable): The locale the used for this value, or %NULL to use the current active one.
*
* Set long description for this component.
*/
void
as_component_set_description (AsComponent *cpt, const gchar* value, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
as_component_localized_set (cpt, priv->description, value, locale);
g_object_notify ((GObject *) cpt, "description");
}
/**
* as_component_get_keywords:
* @cpt: a #AsComponent instance.
*
* Returns: (transfer none): String array of keywords
*/
gchar**
as_component_get_keywords (AsComponent *cpt)
{
gchar **strv;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
strv = g_hash_table_lookup (priv->keywords, as_component_get_active_locale (cpt));
if (strv == NULL) {
/* fall back to untranslated */
strv = g_hash_table_lookup (priv->keywords, "C");
}
return strv;
}
/**
* as_component_set_keywords:
* @cpt: a #AsComponent instance.
* @value: (array zero-terminated=1): String-array of keywords
* @locale: (nullable): Locale of the values, or %NULL to use current locale.
*
* Set keywords for this component.
*/
void
as_component_set_keywords (AsComponent *cpt, gchar **value, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
/* if no locale was specified, we assume the default locale */
if (locale == NULL)
locale = as_component_get_active_locale (cpt);
g_hash_table_insert (priv->keywords,
g_strdup (locale),
g_strdupv (value));
g_object_notify ((GObject *) cpt, "keywords");
}
/**
* as_component_get_icons:
* @cpt: an #AsComponent instance
*
* Returns: (element-type AsIcon) (transfer none): A #GPtrArray of all icons for this component.
*/
GPtrArray*
as_component_get_icons (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->icons;
}
/**
* as_component_get_icon_by_size:
* @cpt: an #AsComponent instance
* @width: The icon width in pixels.
* @height: the icon height in pixels.
*
* Gets an icon matching the size constraints.
* The icons are not filtered by type, and the first icon
* which matches the size is returned.
* If you want more control over which icons you use for displaying,
* use the %as_component_get_icons() function to get a list of all icons.
*
* Note that this function is not HiDPI aware! It will never return an icon with
* a scaling factor > 1.
*
* Returns: (transfer none): An icon matching the given width/height, or %NULL if not found.
*/
AsIcon*
as_component_get_icon_by_size (AsComponent *cpt, guint width, guint height)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
for (i = 0; i < priv->icons->len; i++) {
AsIcon *icon = AS_ICON (g_ptr_array_index (priv->icons, i));
/* ignore scaled icons */
if (as_icon_get_scale (icon) > 1)
continue;
if ((as_icon_get_width (icon) == width) && (as_icon_get_height (icon) == height))
return icon;
}
return NULL;
}
/**
* as_component_add_icon:
* @cpt: an #AsComponent instance
* @icon: the valid #AsIcon instance to add.
*
* Add an icon to this component.
*/
void
as_component_add_icon (AsComponent *cpt, AsIcon *icon)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_add (priv->icons, g_object_ref (icon));
}
/**
* as_component_get_categories:
* @cpt: a #AsComponent instance.
*
* Returns: (transfer none) (element-type utf8): String array of categories
*/
GPtrArray*
as_component_get_categories (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->categories;
}
/**
* as_component_add_category:
* @cpt: a #AsComponent instance.
* @category: the categories name to add.
*
* Add a category.
*/
void
as_component_add_category (AsComponent *cpt, const gchar *category)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (as_flags_contains (priv->value_flags, AS_VALUE_FLAG_DUPLICATE_CHECK)) {
/* check for duplicates */
if (as_ptr_array_find_string (priv->categories, category) != NULL)
return;
}
g_ptr_array_add (priv->categories,
g_strdup (category));
}
/**
* as_component_has_category:
* @cpt: an #AsComponent object
* @category: the specified category to check
*
* Check if component is in the specified category.
*
* Returns: %TRUE if the component is in the specified category.
**/
gboolean
as_component_has_category (AsComponent *cpt, const gchar *category)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return as_ptr_array_find_string (priv->categories, category) != NULL;
}
/**
* as_component_get_metadata_license:
* @cpt: a #AsComponent instance.
*
* The license the metadata iself is subjected to.
*
* Returns: the license.
*/
const gchar*
as_component_get_metadata_license (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->metadata_license;
}
/**
* as_component_set_metadata_license:
* @cpt: a #AsComponent instance.
* @value: the metadata license.
*
* Set the license this metadata is licensed under.
*/
void
as_component_set_metadata_license (AsComponent *cpt, const gchar *value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->metadata_license);
priv->metadata_license = g_strdup (value);
}
/**
* as_component_get_project_license:
* @cpt: a #AsComponent instance.
*
* Get the license of the project this component belongs to.
*
* Returns: the license.
*/
const gchar*
as_component_get_project_license (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->project_license;
}
/**
* as_component_set_project_license:
* @cpt: a #AsComponent instance.
* @value: the project license.
*
* Set the project license.
*/
void
as_component_set_project_license (AsComponent *cpt, const gchar *value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->project_license);
priv->project_license = g_strdup (value);
g_object_notify ((GObject *) cpt, "project-license");
}
/**
* as_component_get_project_group:
* @cpt: a #AsComponent instance.
*
* Get the component's project group.
*
* Returns: the project group.
*/
const gchar*
as_component_get_project_group (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->project_group;
}
/**
* as_component_set_project_group:
* @cpt: a #AsComponent instance.
* @value: the project group.
*
* Set the component's project group.
*/
void
as_component_set_project_group (AsComponent *cpt, const gchar *value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_free (priv->project_group);
priv->project_group = g_strdup (value);
}
/**
* as_component_get_developer_name:
* @cpt: a #AsComponent instance.
*
* Get the component's developer or development team name.
*
* Returns: the developer name.
*/
const gchar*
as_component_get_developer_name (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return as_component_localized_get (cpt, priv->developer_name);
}
/**
* as_component_set_developer_name:
* @cpt: a #AsComponent instance.
* @value: the developer or developer team name
* @locale: (nullable): the locale, or %NULL. e.g. "en_GB"
*
* Set the the component's developer or development team name.
*/
void
as_component_set_developer_name (AsComponent *cpt, const gchar *value, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
as_component_localized_set (cpt, priv->developer_name, value, locale);
}
/**
* as_component_get_screenshots:
* @cpt: a #AsComponent instance.
*
* Get a list of associated screenshots.
*
* Returns: (element-type AsScreenshot) (transfer none): an array of #AsScreenshot instances
*/
GPtrArray*
as_component_get_screenshots (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->screenshots;
}
/**
* as_component_get_compulsory_for_desktops:
* @cpt: a #AsComponent instance.
*
* Return value: (transfer none) (element-type utf8): A list of desktops where this component is compulsory
**/
GPtrArray*
as_component_get_compulsory_for_desktops (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->compulsory_for_desktops;
}
/**
* as_component_set_compulsory_for_desktop:
* @cpt: a #AsComponent instance.
* @desktop: The name of the desktop.
*
* Mark this component to be compulsory for the specified desktop environment.
**/
void
as_component_set_compulsory_for_desktop (AsComponent *cpt, const gchar *desktop)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_return_if_fail (desktop != NULL);
if (as_flags_contains (priv->value_flags, AS_VALUE_FLAG_DUPLICATE_CHECK)) {
/* check for duplicates */
if (as_ptr_array_find_string (priv->compulsory_for_desktops, desktop) != NULL)
return;
}
g_ptr_array_add (priv->compulsory_for_desktops,
g_strdup (desktop));
}
/**
* as_component_is_compulsory_for_desktop:
* @cpt: an #AsComponent object
* @desktop: the desktop-id to test for
*
* Check if this component is compulsory for the given desktop.
*
* Returns: %TRUE if compulsory, %FALSE otherwise.
**/
gboolean
as_component_is_compulsory_for_desktop (AsComponent *cpt, const gchar *desktop)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return as_ptr_array_find_string (priv->compulsory_for_desktops, desktop) != NULL;
}
/**
* as_component_get_provided_for_kind:
* @cpt: a #AsComponent instance.
* @kind: kind of the provided item, e.g. %AS_PROVIDED_KIND_MIMETYPE
*
* Get an #AsProvided object for the given interface type,
* containing information about the public interfaces (mimetypes, firmware, DBus services, ...)
* this component provides.
*
* Returns: (transfer none): #AsProvided containing the items this component provides, or %NULL.
**/
AsProvided*
as_component_get_provided_for_kind (AsComponent *cpt, AsProvidedKind kind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
for (i = 0; i < priv->provided->len; i++) {
AsProvided *prov = AS_PROVIDED (g_ptr_array_index (priv->provided, i));
if (as_provided_get_kind (prov) == kind)
return prov;
}
return NULL;
}
/**
* as_component_get_provided:
* @cpt: a #AsComponent instance.
*
* Get a list of #AsProvided objects associated with this component.
*
* Returns: (transfer none) (element-type AsProvided): A list of #AsProvided objects.
**/
GPtrArray*
as_component_get_provided (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->provided;
}
/**
* as_component_add_provided:
* @cpt: a #AsComponent instance.
* @prov: a #AsProvided instance.
*
* Add a set of provided items to this component.
*
* Since: 0.6.2
**/
void
as_component_add_provided (AsComponent *cpt, AsProvided *prov)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (as_flags_contains (priv->value_flags, AS_VALUE_FLAG_DUPLICATE_CHECK)) {
guint i;
for (i = 0; i < priv->provided->len; i++) {
AsProvided *eprov = AS_PROVIDED (g_ptr_array_index (priv->provided, i));
if (as_provided_get_kind (prov) == as_provided_get_kind (eprov)) {
/* replace existing entry */
g_ptr_array_remove_index (priv->provided, i);
g_ptr_array_add (priv->provided,
g_object_ref (prov));
return;
}
}
}
g_ptr_array_add (priv->provided,
g_object_ref (prov));
}
/**
* as_component_add_provided_item:
* @cpt: a #AsComponent instance.
* @kind: the kind of the provided item (e.g. %AS_PROVIDED_KIND_MIMETYPE)
* @item: the item to add.
*
* Adds a provided item to the component.
*
* Internal convenience function for use by the metadata reading classes.
**/
void
as_component_add_provided_item (AsComponent *cpt, AsProvidedKind kind, const gchar *item)
{
AsProvided *prov;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
/* we just skip empty items */
if (as_str_empty (item))
return;
prov = as_component_get_provided_for_kind (cpt, kind);
if (prov == NULL) {
prov = as_provided_new ();
as_provided_set_kind (prov, kind);
g_ptr_array_add (priv->provided, prov);
}
as_provided_add_item (prov, item);
}
/**
* as_component_add_suggested:
* @cpt: a #AsComponent instance.
* @suggested: The #AsSuggested
*
* Add an #AsSuggested to this component.
**/
void
as_component_add_suggested (AsComponent *cpt, AsSuggested *suggested)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_add (priv->suggestions,
g_object_ref (suggested));
}
/**
* as_component_get_suggested:
* @cpt: a #AsComponent instance.
*
* Get a list of associated suggestions.
*
* Returns: (transfer none) (element-type AsSuggested): an array of #AsSuggested instances
*/
GPtrArray*
as_component_get_suggested (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->suggestions;
}
/**
* as_component_get_merge_kind:
* @cpt: a #AsComponent instance.
*
* Get the merge method which should apply to duplicate components
* with this ID.
*
* Returns: the #AsMergeKind of this component.
*
* Since: 0.9.8
*/
AsMergeKind
as_component_get_merge_kind (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->merge_kind;
}
/**
* as_component_set_merge_kind:
* @cpt: a #AsComponent instance.
* @kind: the #AsMergeKind.
*
* Sets the #AsMergeKind for this component.
*
* Since: 0.9.8
*/
void
as_component_set_merge_kind (AsComponent *cpt, AsMergeKind kind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->merge_kind = kind;
}
/**
* as_component_get_priority:
* @cpt: a #AsComponent instance.
*
* Returns the priority of this component.
* This method is used internally.
*
* Since: 0.6.1
*/
gint
as_component_get_priority (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->priority;
}
/**
* as_component_set_priority:
* @cpt: a #AsComponent instance.
* @priority: the given priority
*
* Sets the priority of this component.
* This method is used internally.
*
* Since: 0.6.1
*/
void
as_component_set_priority (AsComponent *cpt, gint priority)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->priority = priority;
}
/**
* as_component_get_sort_score:
* @cpt: a #AsComponent instance.
*
* Returns the sorting priority of this component.
*
* Since: 0.9.8
*/
guint
as_component_get_sort_score (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->sort_score;
}
/**
* as_component_set_sort_score:
* @cpt: a #AsComponent instance.
* @score: the given sorting score
*
* Sets the sorting score of this component.
*
* Since: 0.9.8
*/
void
as_component_set_sort_score (AsComponent *cpt, guint score)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->sort_score = score;
}
/**
* as_component_add_language:
* @cpt: an #AsComponent instance.
* @locale: (nullable): the locale, or %NULL. e.g. "en_GB"
* @percentage: the percentage completion of the translation, 0 for locales with unknown amount of translation
*
* Adds a language to the component.
*
* Since: 0.7.0
**/
void
as_component_add_language (AsComponent *cpt, const gchar *locale, gint percentage)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (locale == NULL)
locale = "C";
g_hash_table_insert (priv->languages,
g_strdup (locale),
GINT_TO_POINTER (percentage));
}
/**
* as_component_get_language:
* @cpt: an #AsComponent instance.
* @locale: (nullable): the locale, or %NULL. e.g. "en_GB"
*
* Gets the translation coverage in percent for a specific locale
*
* Returns: a percentage value, -1 if locale was not found
*
* Since: 0.7.0
**/
gint
as_component_get_language (AsComponent *cpt, const gchar *locale)
{
gboolean ret;
gpointer value = NULL;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (locale == NULL)
locale = "C";
ret = g_hash_table_lookup_extended (priv->languages,
locale, NULL, &value);
if (!ret)
return -1;
return GPOINTER_TO_INT (value);
}
/**
* as_component_get_languages:
* @cpt: an #AsComponent instance.
*
* Get a list of all languages.
*
* Returns: (transfer container) (element-type utf8): list of locales
*
* Since: 0.7.0
**/
GList*
as_component_get_languages (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return g_hash_table_get_keys (priv->languages);
}
/**
* as_component_get_languages_table:
* @cpt: an #AsComponent instance.
*
* Get a HashMap mapping languages to their completion percentage
*
* Returns: (transfer none): locales map
*
* Since: 0.7.0
**/
GHashTable*
as_component_get_languages_table (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->languages;
}
/**
* as_component_get_translations:
* @cpt: an #AsComponent instance.
*
* Get a #GPtrArray of #AsTranslation objects describing the
* translation systems and translation-ids (e.g. Gettext domains) used
* by this software component.
*
* Only set for metainfo files.
*
* Returns: (transfer none) (element-type AsTranslation): An array of #AsTranslation objects.
*
* Since: 0.9.2
*/
GPtrArray*
as_component_get_translations (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (priv->translations == NULL)
priv->translations = g_ptr_array_new_with_free_func (g_object_unref);
return priv->translations;
}
/**
* as_component_add_translation:
* @cpt: an #AsComponent instance.
* @tr: an #AsTranslation instance.
*
* Assign an #AsTranslation object describing the translation system used
* by this component.
*
* Since: 0.9.2
*/
void
as_component_add_translation (AsComponent *cpt, AsTranslation *tr)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (priv->translations == NULL)
priv->translations = g_ptr_array_new_with_free_func (g_object_unref);
g_ptr_array_add (priv->translations, g_object_ref (tr));
}
/**
* as_component_get_scope:
* @cpt: a #AsComponent instance.
*
* Returns: the #AsComponentScope of this component.
*
* Since: 0.10.2
*/
AsComponentScope
as_component_get_scope (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->scope;
}
/**
* as_component_set_scope:
* @cpt: a #AsComponent instance.
* @scope: the #AsComponentKind.
*
* Sets the #AsComponentScope of this component.
*/
void
as_component_set_scope (AsComponent *cpt, AsComponentScope scope)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->scope = scope;
}
/**
* as_component_get_origin_kind:
* @cpt: a #AsComponent instance.
*
* Returns: the #AsOriginKind of this component.
*
* Since: 0.10.2
*/
AsOriginKind
as_component_get_origin_kind (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->origin_kind;
}
/**
* as_component_set_origin_kind:
* @cpt: a #AsComponent instance.
* @okind: the #AsOriginKind.
*
* Sets the #AsOriginKind of this component.
*/
void
as_component_set_origin_kind (AsComponent *cpt, AsOriginKind okind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->origin_kind = okind;
}
/**
* as_component_add_icon_full:
*
* Internal helper function for as_component_refine_icons()
*/
static void
as_component_add_icon_full (AsComponent *cpt, AsIconKind kind, const gchar *size_str, const gchar *fname)
{
g_autoptr(AsIcon) icon = NULL;
icon = as_icon_new ();
as_icon_set_kind (icon, kind);
as_icon_set_filename (icon, fname);
if (g_strcmp0 (size_str, "128x128") == 0) {
as_icon_set_width (icon, 128);
as_icon_set_height (icon, 128);
} else {
/* it's either "64x64", emptystring or NULL, in any case we assume 64x64
* This has to be adapted as soon as we support more than 2 icon sizes, but
* we are lazy here to not hurt performance too much. */
as_icon_set_width (icon, 64);
as_icon_set_height (icon, 64);
}
as_component_add_icon (cpt, icon);
}
/**
* as_component_refine_icons:
* @cpt: a #AsComponent instance.
*
* We use this method to ensure the "icon" and "icon_url" properties of
* a component are properly set, by finding the icons in default directories.
*/
static void
as_component_refine_icons (AsComponent *cpt, GPtrArray *icon_paths)
{
const gchar *extensions[] = { "png",
"svg",
"svgz",
"gif",
"ico",
"xcf",
NULL };
const gchar *sizes[] = { "", "64x64", "128x128", NULL };
const gchar *icon_fname = NULL;
const gchar *origin;
guint i, j, k, l;
g_autoptr(GPtrArray) icons = NULL;
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (priv->icons->len == 0)
return;
/* take control of the old icon list and rewrite it */
icons = priv->icons;
priv->icons = g_ptr_array_new_with_free_func (g_object_unref);
origin = as_component_get_origin (cpt);
/* Process the icons we have and extract sizes */
for (i = 0; i < icons->len; i++) {
AsIconKind ikind;
AsIcon *icon = AS_ICON (g_ptr_array_index (icons, i));
ikind = as_icon_get_kind (icon);
if (ikind == AS_ICON_KIND_REMOTE) {
/* no processing / icon-search is needed (and possible) for remote icons */
as_component_add_icon (cpt, icon);
continue;
} else if (ikind == AS_ICON_KIND_STOCK) {
/* since AppStream 0.9, we are not expected to find stock icon names in cache
* directories anymore, so we can just add it without changes */
as_component_add_icon (cpt, icon);
continue;
}
if ((ikind != AS_ICON_KIND_CACHED) && (ikind != AS_ICON_KIND_LOCAL)) {
g_warning ("Found icon of unknown type, skipping it: %s", as_icon_kind_to_string (ikind));
continue;
}
/* get icon name we want to resolve
* (it's "filename", because we should only get here if we have
* a CACHED or LOCAL icon) */
icon_fname = as_icon_get_filename (icon);
if (g_str_has_prefix (icon_fname, "/")) {
/* looks like this component already has a full icon path */
as_component_add_icon (cpt, icon);
/* assume 64x64px icon, if not defined otherwise */
if ((as_icon_get_width (icon) == 0) && (as_icon_get_height (icon) == 0)) {
as_icon_set_width (icon, 64);
as_icon_set_height (icon, 64);
}
continue;
}
/* skip the full cache search if we already have size information */
if ((ikind == AS_ICON_KIND_CACHED) && (as_icon_get_width (icon) > 0)) {
for (l = 0; l < icon_paths->len; l++) {
g_autofree gchar *tmp_icon_path_wh = NULL;
const gchar *icon_path = (const gchar*) g_ptr_array_index (icon_paths, l);
if (as_icon_get_scale (icon) <= 1) {
tmp_icon_path_wh = g_strdup_printf ("%s/%s/%ix%i/%s",
icon_path,
origin,
as_icon_get_width (icon),
as_icon_get_height (icon),
icon_fname);
} else {
tmp_icon_path_wh = g_strdup_printf ("%s/%s/%ix%i@%i/%s",
icon_path,
origin,
as_icon_get_width (icon),
as_icon_get_height (icon),
as_icon_get_scale (icon),
icon_fname);
}
if (g_file_test (tmp_icon_path_wh, G_FILE_TEST_EXISTS)) {
as_icon_set_filename (icon, tmp_icon_path_wh);
as_component_add_icon (cpt, icon);
break;
}
}
/* we don't need a full search anymore - the icon having size information means that
* it will be in the "origin" subdirectory with the appropriate size, so there is no
* reason to start a big icon-hunt */
continue;
}
/* search local icon path */
for (l = 0; l < icon_paths->len; l++) {
const gchar *icon_path = (const gchar*) g_ptr_array_index (icon_paths, l);
for (j = 0; sizes[j] != NULL; j++) {
g_autofree gchar *tmp_icon_path = NULL;
/* sometimes, the file already has an extension */
tmp_icon_path = g_strdup_printf ("%s/%s/%s/%s",
icon_path,
origin,
sizes[j],
icon_fname);
if (g_file_test (tmp_icon_path, G_FILE_TEST_EXISTS)) {
/* we have an icon! */
if (g_strcmp0 (sizes[j], "") == 0) {
/* old icon directory, so assume 64x64 */
as_component_add_icon_full (cpt,
as_icon_get_kind (icon),
"64x64",
tmp_icon_path);
} else {
as_component_add_icon_full (cpt,
as_icon_get_kind (icon),
sizes[j],
tmp_icon_path);
}
continue;
}
/* file not found, try extensions (we will not do this forever, better fix AppStream data!) */
for (k = 0; extensions[k] != NULL; k++) {
g_autofree gchar *tmp_icon_path_ext = NULL;
tmp_icon_path_ext = g_strdup_printf ("%s/%s/%s/%s.%s",
icon_path,
origin,
sizes[j],
icon_fname,
extensions[k]);
if (g_file_test (tmp_icon_path_ext, G_FILE_TEST_EXISTS)) {
/* we have an icon! */
if (g_strcmp0 (sizes[j], "") == 0) {
/* old icon directory, so assume 64x64 */
as_component_add_icon_full (cpt,
as_icon_get_kind (icon),
"64x64",
tmp_icon_path_ext);
} else {
as_component_add_icon_full (cpt,
as_icon_get_kind (icon),
sizes[j],
tmp_icon_path_ext);
}
}
}
}
}
}
}
/**
* as_component_complete:
* @cpt: a #AsComponent instance.
* @scr_service_url: Base url for screenshot-service, obtain via #AsDistroDetails
* @icon_paths: String array of possible (cached) icon locations
*
* Private function to complete a AsComponent with
* additional data found on the system.
*
* INTERNAL
*/
void
as_component_complete (AsComponent *cpt, gchar *scr_service_url, GPtrArray *icon_paths)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
/* improve icon paths */
as_component_refine_icons (cpt, icon_paths);
/* "fake" a launchable entry for desktop-apps that failed to include one. This is used for legacy compatibility */
if ((priv->kind == AS_COMPONENT_KIND_DESKTOP_APP) && (priv->launchables->len <= 0)) {
if (g_str_has_suffix (priv->id, ".desktop")) {
g_autoptr(AsLaunchable) launchable = as_launchable_new ();
as_launchable_set_kind (launchable, AS_LAUNCHABLE_KIND_DESKTOP_ID);
as_launchable_add_entry (launchable, priv->id);
as_component_add_launchable (cpt, launchable);
}
}
/* if there is no screenshot service URL, there is nothing left to do for us */
if (scr_service_url == NULL)
return;
/* we want screenshot data from 3rd-party screenshot servers, if the component doesn't have screenshots defined already */
if ((priv->screenshots->len == 0) && (as_component_has_package (cpt))) {
gchar *url;
AsImage *img;
g_autoptr(AsScreenshot) sshot = NULL;
url = g_build_filename (scr_service_url, "screenshot", priv->pkgnames[0], NULL);
/* screenshots.debian.net-like services dont specify a size, so we choose the default sizes
* (800x600 for source-type images, 160x120 for thumbnails)
*/
/* add main image */
img = as_image_new ();
as_image_set_url (img, url);
as_image_set_width (img, 800);
as_image_set_height (img, 600);
as_image_set_kind (img, AS_IMAGE_KIND_SOURCE);
sshot = as_screenshot_new ();
/* propagate locale */
as_screenshot_set_active_locale (sshot, as_component_get_active_locale (cpt));
as_screenshot_add_image (sshot, img);
as_screenshot_set_kind (sshot, AS_SCREENSHOT_KIND_DEFAULT);
g_object_unref (img);
g_free (url);
/* add thumbnail */
url = g_build_filename (scr_service_url, "thumbnail", priv->pkgnames[0], NULL);
img = as_image_new ();
as_image_set_url (img, url);
as_image_set_width (img, 160);
as_image_set_height (img, 120);
as_image_set_kind (img, AS_IMAGE_KIND_THUMBNAIL);
as_screenshot_add_image (sshot, img);
/* add screenshot to component */
as_component_add_screenshot (cpt, sshot);
g_object_unref (img);
g_free (url);
}
}
/**
* as_component_add_token_helper:
*/
static void
as_component_add_token_helper (AsComponent *cpt,
const gchar *value,
AsTokenMatch match_flag,
AsStemmer *stemmer)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
AsTokenType *match_pval;
g_autofree gchar *token_stemmed = NULL;
/* invalid */
if (!as_utils_search_token_valid (value))
return;
/* create a stemmed version of our token */
token_stemmed = as_stemmer_stem (stemmer, value);
/* does the token already exist */
match_pval = g_hash_table_lookup (priv->token_cache, token_stemmed);
if (match_pval != NULL) {
*match_pval |= match_flag;
return;
}
/* create and add */
match_pval = g_new0 (AsTokenType, 1);
*match_pval = match_flag;
g_hash_table_insert (priv->token_cache,
g_steal_pointer (&token_stemmed),
match_pval);
}
/**
* as_component_add_token:
*/
static void
as_component_add_token (AsComponent *cpt,
const gchar *value,
gboolean allow_split,
AsTokenMatch match_flag)
{
g_autoptr(AsStemmer) stemmer = NULL;
stemmer = g_object_ref (as_stemmer_get ());
/* add extra tokens for names like x-plane or half-life */
if (allow_split && g_strstr_len (value, -1, "-") != NULL) {
guint i;
g_auto(GStrv) split = g_strsplit (value, "-", -1);
for (i = 0; split[i] != NULL; i++)
as_component_add_token_helper (cpt, split[i], match_flag, stemmer);
}
/* add the whole token always, even when we split on hyphen */
as_component_add_token_helper (cpt, value, match_flag, stemmer);
}
/**
* as_component_value_tokenize:
*
* Split a component value string into tokens.
*/
static gboolean
as_component_value_tokenize (AsComponent *cpt, const gchar *value, gchar ***tokens_utf8, gchar ***tokens_ascii)
{
/* tokenize with UTF-8 fallbacks */
if (g_strstr_len (value, -1, "+") == NULL &&
g_strstr_len (value, -1, "-") == NULL) {
(*tokens_utf8) = g_str_tokenize_and_fold (value,
as_component_get_active_locale (cpt),
tokens_ascii);
}
/* we might still be able to extract tokens if g_str_tokenize_and_fold() can't do it or +/- were found */
if ((*tokens_utf8) == NULL) {
g_autofree gchar *delim = NULL;
delim = g_utf8_strdown (value, -1);
g_strdelimit (delim, "/,.;:", ' ');
(*tokens_utf8) = g_strsplit (delim, " ", -1);
}
if (tokens_ascii == NULL)
return (*tokens_utf8) != NULL;
else
return ((*tokens_utf8) != NULL) || ((*tokens_ascii) != NULL);
}
/**
* as_component_add_tokens:
*/
static void
as_component_add_tokens (AsComponent *cpt,
const gchar *value,
gboolean allow_split,
AsTokenMatch match_flag)
{
guint i;
g_auto(GStrv) values_utf8 = NULL;
g_auto(GStrv) values_ascii = NULL;
/* sanity check */
if (value == NULL) {
g_critical ("trying to add NULL search token to %s",
as_component_get_id (cpt));
return;
}
/* create a set of tokens from the value string */
if (!as_component_value_tokenize (cpt, value, &values_utf8, &values_ascii))
return;
/* add each token */
for (i = 0; values_utf8 != NULL && values_utf8[i] != NULL; i++)
as_component_add_token (cpt, values_utf8[i], allow_split, match_flag);
for (i = 0; values_ascii != NULL && values_ascii[i] != NULL; i++)
as_component_add_token (cpt, values_ascii[i], allow_split, match_flag);
}
/**
* as_component_create_token_cache_target:
*/
static void
as_component_create_token_cache_target (AsComponent *cpt, AsComponent *donor)
{
AsComponentPrivate *priv = GET_PRIVATE (donor);
const gchar *tmp;
gchar **keywords;
AsProvided *prov;
guint i;
/* tokenize all the data we have */
if (priv->id != NULL) {
as_component_add_token (cpt, priv->id, FALSE,
AS_TOKEN_MATCH_ID);
}
tmp = as_component_get_name (cpt);
if (tmp != NULL) {
as_component_add_tokens (cpt, tmp, TRUE, AS_TOKEN_MATCH_NAME);
}
tmp = as_component_get_summary (cpt);
if (tmp != NULL) {
as_component_add_tokens (cpt, tmp, TRUE, AS_TOKEN_MATCH_SUMMARY);
}
tmp = as_component_get_description (cpt);
if (tmp != NULL) {
as_component_add_tokens (cpt, tmp, FALSE, AS_TOKEN_MATCH_DESCRIPTION);
}
keywords = as_component_get_keywords (cpt);
if (keywords != NULL) {
for (i = 0; keywords[i] != NULL; i++)
as_component_add_tokens (cpt, keywords[i], FALSE, AS_TOKEN_MATCH_KEYWORD);
}
prov = as_component_get_provided_for_kind (donor, AS_PROVIDED_KIND_MIMETYPE);
if (prov != NULL) {
GPtrArray *items = as_provided_get_items (prov);
for (i = 0; i < items->len; i++)
as_component_add_token (cpt,
(const gchar*) g_ptr_array_index (items, i),
FALSE,
AS_TOKEN_MATCH_MIMETYPE);
}
if (priv->pkgnames != NULL) {
for (i = 0; priv->pkgnames[i] != NULL; i++)
as_component_add_token (cpt, priv->pkgnames[i], FALSE, AS_TOKEN_MATCH_PKGNAME);
}
}
/**
* as_component_create_token_cache:
*/
void
as_component_create_token_cache (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
as_component_create_token_cache_target (cpt, cpt);
for (i = 0; i < priv->addons->len; i++) {
AsComponent *donor = g_ptr_array_index (priv->addons, i);
as_component_create_token_cache_target (cpt, donor);
}
}
/**
* as_component_search_matches:
* @cpt: a #AsComponent instance.
* @term: the search term.
*
* Searches component data for a specific keyword.
*
* Returns: a match scrore, where 0 is no match and 100 is the best match.
*
* Since: 0.9.7
**/
guint
as_component_search_matches (AsComponent *cpt, const gchar *term)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
AsTokenType *match_pval;
GList *l;
AsTokenMatch result = 0;
g_autoptr(GList) keys = NULL;
/* nothing to do */
if (term == NULL)
return 0;
/* ensure the token cache is created */
if (g_once_init_enter (&priv->token_cache_valid)) {
as_component_create_token_cache (cpt);
g_once_init_leave (&priv->token_cache_valid, TRUE);
}
/* find the exact match (which is more awesome than a partial match) */
match_pval = g_hash_table_lookup (priv->token_cache, term);
if (match_pval != NULL)
return *match_pval << 2;
/* need to do partial match */
keys = g_hash_table_get_keys (priv->token_cache);
for (l = keys; l != NULL; l = l->next) {
const gchar *key = l->data;
if (g_str_has_prefix (key, term)) {
match_pval = g_hash_table_lookup (priv->token_cache, key);
result |= *match_pval;
}
}
return result;
}
/**
* as_component_search_matches_all:
* @cpt: a #AsComponent instance.
* @terms: the search terms.
*
* Searches component data for all the specific keywords.
*
* Returns: a match score, where 0 is no match and larger numbers are better
* matches.
*
* Since: 0.9.8
*/
guint
as_component_search_matches_all (AsComponent *cpt, gchar **terms)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
guint matches_sum = 0;
guint tmp;
priv->sort_score = 0;
if (terms == NULL) {
/* if the terms list is NULL, we usually had a too short search term when
* tokenizing the search string. In any case, we treat NULL as match-all
* value.
* (users will see a full list of all entries that way, which they will
* recognize as hint to make their search more narrow) */
priv->sort_score = 1;
return priv->sort_score;
}
/* do *all* search keywords match */
for (i = 0; terms[i] != NULL; i++) {
tmp = as_component_search_matches (cpt, terms[i]);
if (tmp == 0)
return 0;
matches_sum |= tmp;
}
priv->sort_score = matches_sum;
return priv->sort_score;
}
/**
* as_component_get_search_tokens:
* @cpt: a #AsComponent instance.
*
* Returns all search tokens for this component.
*
* Returns: (transfer container) (element-type utf8): The string search tokens
*
* Since: 0.9.7
*/
GPtrArray*
as_component_get_search_tokens (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GList *l;
GPtrArray *array;
g_autoptr(GList) keys = NULL;
/* ensure the token cache is created */
if (g_once_init_enter (&priv->token_cache_valid)) {
as_component_create_token_cache (cpt);
g_once_init_leave (&priv->token_cache_valid, TRUE);
}
/* return all the token cache */
keys = g_hash_table_get_keys (priv->token_cache);
array = g_ptr_array_new_with_free_func (g_free);
for (l = keys; l != NULL; l = l->next)
g_ptr_array_add (array, g_strdup (l->data));
return array;
}
/**
* as_component_get_token_cache_table:
* @cpt: a #AsComponent instance.
*
* Get the raw token table.
*
* This is internal API.
**/
GHashTable*
as_component_get_token_cache_table (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->token_cache;
}
/**
* as_component_set_token_cache_valid:
* @cpt: a #AsComponent instance.
* @valid: Whether the token cache is considered to be valid.
*
* This is internal API.
**/
void
as_component_set_token_cache_valid (AsComponent *cpt, gboolean valid)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->token_cache_valid = valid;
}
/**
* as_component_set_value_flags:
* @cpt: a #AsComponent instance.
* @flags: #AsValueFlags to set on @cpt.
*
*/
void
as_component_set_value_flags (AsComponent *cpt, AsValueFlags flags)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->value_flags = flags;
}
/**
* as_component_get_value_flags:
* @cpt: a #AsComponent instance.
*
* Returns: The #AsValueFlags that are set on @cpt.
*
*/
AsValueFlags
as_component_get_value_flags (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->value_flags;
}
/**
* as_component_has_desktop_group:
*
* Internal helper method for %as_component_is_member_of_category
*/
static gboolean
as_component_has_desktop_group (AsComponent *cpt, const gchar *desktop_group)
{
guint i;
g_auto(GStrv) split = g_strsplit (desktop_group, "::", -1);
for (i = 0; split[i] != NULL; i++) {
if (!as_component_has_category (cpt, split[i]))
return FALSE;
}
return TRUE;
}
/**
* as_component_is_member_of_category:
* @cpt: a #AsComponent instance.
* @category: The category to test.
*
* Test if the component @cpt is a member of category @category.
*/
gboolean
as_component_is_member_of_category (AsComponent *cpt, AsCategory *category)
{
GPtrArray *cdesktop_groups;
guint i;
cdesktop_groups = as_category_get_desktop_groups (category);
for (i = 0; i < cdesktop_groups->len; i++) {
const gchar *cdg_name = (const gchar*) g_ptr_array_index (cdesktop_groups, i);
if (as_component_has_desktop_group (cpt, cdg_name))
return TRUE;
}
return FALSE;
}
/**
* as_component_set_ignored:
* @cpt: a #AsComponent instance.
* @ignore: %TRUE if the metadata in @cpt should be ignored.
*
* Since: 0.10.2
*/
void
as_component_set_ignored (AsComponent *cpt, gboolean ignore)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
priv->ignored = ignore;
}
/**
* as_component_is_ignored:
* @cpt: a #AsComponent instance.
*
* Returns: Whether this component's metadata should be ignored.
*
* Since: 0.10.2
*/
gboolean
as_component_is_ignored (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->ignored;
}
/**
* as_component_get_custom:
* @cpt: An #AsComponent.
*
* Returns: (transfer none): Hash table of custom user defined data fields.
*
* Since: 0.10.5
*/
GHashTable*
as_component_get_custom (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->custom;
}
/**
* as_component_get_custom_value:
* @cpt: An #AsComponent.
* @key: Field name.
*
* Retrieve value for a custom data entry with the given key.
*
* Since: 0.10.5
*/
gchar*
as_component_get_custom_value (AsComponent *cpt, const gchar *key)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (key == NULL)
return NULL;
return g_hash_table_lookup (priv->custom, key);
}
/**
* as_component_insert_custom_value:
* @cpt: An #AsComponent.
* @key: Key name.
* @value: A string value.
*
* Add a key and value pair to the custom data table.
*
* Returns: %TRUE if the key did not exist yet.
*
* Since: 0.10.5
*/
gboolean
as_component_insert_custom_value (AsComponent *cpt, const gchar *key, const gchar *value)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (key == NULL)
return FALSE;
return g_hash_table_insert (priv->custom,
g_strdup (key),
g_strdup (value));
}
/**
* as_component_get_content_ratings:
* @cpt: a #AsComponent instance.
*
* Gets all content ratings defined for this software.
*
* Returns: (element-type AsContentRating) (transfer none): an array
*
* Since: 0.11.0
**/
GPtrArray*
as_component_get_content_ratings (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->content_ratings;
}
/**
* as_component_get_content_rating:
* @cpt: a #AsComponent instance.
* @kind: a ratings kind, e.g. "oars-1.0"
*
* Gets a content ratings of a specific type that are defined for this component.
*
* Returns: (transfer none): a #AsContentRating or %NULL if not found
*
* Since: 0.11.0
**/
AsContentRating*
as_component_get_content_rating (AsComponent *cpt, const gchar *kind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
for (i = 0; i < priv->content_ratings->len; i++) {
AsContentRating *content_rating = AS_CONTENT_RATING (g_ptr_array_index (priv->content_ratings, i));
if (g_strcmp0 (as_content_rating_get_kind (content_rating), kind) == 0)
return content_rating;
}
return NULL;
}
/**
* as_component_add_content_rating:
* @cpt: a #AsComponent instance.
* @content_rating: a #AsContentRating instance.
*
* Adds a content rating to this component.
*
* Since: 0.11.0
**/
void
as_component_add_content_rating (AsComponent *cpt, AsContentRating *content_rating)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_add (priv->content_ratings,
g_object_ref (content_rating));
}
/**
* as_component_get_launchable:
* @cpt: a #AsComponent instance.
* @kind: a launch kind, e.g. %AS_LAUNCHABLE_KIND_DESKTOP_ID
*
* Gets a #AsLaunchable of a specific type that contains launchable entries for
* this component.
*
* Returns: (transfer none): a #AsLaunchable or %NULL if not found
*
* Since: 0.11.0
**/
AsLaunchable*
as_component_get_launchable (AsComponent *cpt, AsLaunchableKind kind)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
for (i = 0; i < priv->launchables->len; i++) {
AsLaunchable *launch = AS_LAUNCHABLE (g_ptr_array_index (priv->launchables, i));
if (as_launchable_get_kind (launch) == kind)
return launch;
}
return NULL;
}
/**
* as_component_get_launchables:
* @cpt: a #AsComponent instance.
*
* Returns: (transfer none) (element-type #AsLaunchable): an array
*
* Since: 0.11.0
**/
GPtrArray*
as_component_get_launchables (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->launchables;
}
/**
* as_component_add_launchable:
* @cpt: a #AsComponent instance.
* @launchable: a #AsLaunchable instance.
*
* Adds a #AsLaunchable containing launchables entries for this component.
*
* Since: 0.11.0
**/
void
as_component_add_launchable (AsComponent *cpt, AsLaunchable *launchable)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_ptr_array_add (priv->launchables,
g_object_ref (launchable));
}
/**
* as_component_get_context:
* @cpt: a #AsComponent instance.
*
* Returns: the #AsContext associated with this component.
* This function may return %NULL if no context is set.
*
* Since: 0.11.2
*/
AsContext*
as_component_get_context (AsComponent *cpt)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
return priv->context;
}
/**
* as_component_set_context:
* @cpt: a #AsComponent instance.
* @context: the #AsContext.
*
* Sets the document context this component is associated
* with.
*
* Since: 0.11.2
*/
void
as_component_set_context (AsComponent *cpt, AsContext *context)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
if (priv->context != NULL)
g_object_unref (priv->context);
priv->context = g_object_ref (context);
/* reset individual properties, so the new context overrides them */
g_free (priv->active_locale_override);
priv->active_locale_override = NULL;
g_free (priv->origin);
priv->origin = NULL;
g_free (priv->arch);
priv->arch = NULL;
}
/**
* as_copy_l10n_hashtable_hfunc:
*/
static void
as_copy_l10n_hashtable_hfunc (gpointer key, gpointer value, gpointer user_data)
{
GHashTable *dest = (GHashTable*) user_data;
g_hash_table_insert (dest, g_strdup (key), g_strdup (value));
}
/**
* as_copy_l10n_hashtable:
*
* Helper for as_component_merge_with_mode()
*/
static void
as_copy_l10n_hashtable (GHashTable *src, GHashTable *dest)
{
/* don't copy if there is nothing to copy */
if (g_hash_table_size (src) <= 0)
return;
/* clear our destination table */
g_hash_table_remove_all (dest);
/* copy */
g_hash_table_foreach (src,
&as_copy_l10n_hashtable_hfunc,
dest);
}
/**
* as_copy_gobject_array:
*
* Helper for as_component_merge_with_mode()
*
* NOTE: Only the object references are copied.
*/
static void
as_copy_gobject_array (GPtrArray *src, GPtrArray *dest)
{
guint i;
/* don't copy if there is nothing to copy */
if (src->len <= 0)
return;
/* clear our destination table */
g_ptr_array_remove_range (dest, 0, dest->len);
/* copy */
for (i = 0; i < src->len; i++) {
GObject *obj = G_OBJECT (g_ptr_array_index (src, i));
g_ptr_array_add (dest,
g_object_ref (obj));
}
}
/**
* as_component_merge_with_mode:
* @cpt: An #AsComponent.
* @source: An #AsComponent to merge into @cpt.
* @merge_kind: How
*
* Merge data from component @source into @cpt, respecting the
* merge parameter @merge_kind.
*/
void
as_component_merge_with_mode (AsComponent *cpt, AsComponent *source, AsMergeKind merge_kind)
{
guint i;
AsComponent *dest_cpt = cpt;
AsComponent *src_cpt = source;
AsComponentPrivate *dest_priv = GET_PRIVATE (dest_cpt);
AsComponentPrivate *src_priv = GET_PRIVATE (src_cpt);
/* FIXME/TODO: We need to merge more attributes */
/* merge stuff in append mode */
if (merge_kind == AS_MERGE_KIND_APPEND) {
GPtrArray *suggestions;
GPtrArray *cats;
/* merge categories */
cats = as_component_get_categories (src_cpt);
if (cats->len > 0) {
g_autoptr(GHashTable) cat_table = NULL;
GPtrArray *dest_categories;
cat_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (i = 0; i < cats->len; i++) {
const gchar *cat = (const gchar*) g_ptr_array_index (cats, i);
g_hash_table_add (cat_table, g_strdup (cat));
}
dest_categories = as_component_get_categories (dest_cpt);
if (dest_categories->len > 0) {
for (i = 0; i < dest_categories->len; i++) {
const gchar *cat = (const gchar*) g_ptr_array_index (dest_categories, i);
g_hash_table_add (cat_table, g_strdup (cat));
}
}
g_ptr_array_set_size (dest_categories, 0);
as_hash_table_string_keys_to_array (cat_table, dest_categories);
}
/* merge suggestions */
suggestions = as_component_get_suggested (src_cpt);
if (suggestions != NULL) {
for (i = 0; i < suggestions->len; i++) {
as_component_add_suggested (dest_cpt,
AS_SUGGESTED (g_ptr_array_index (suggestions, i)));
}
}
/* merge icons */
for (i = 0; i < src_priv->icons->len; i++) {
AsIcon *icon = AS_ICON (g_ptr_array_index (src_priv->icons, i));
/* this function will not replace existing icons */
as_component_add_icon (dest_cpt, icon);
}
}
/* merge stuff in replace mode */
if (merge_kind == AS_MERGE_KIND_REPLACE) {
/* names */
as_copy_l10n_hashtable (src_priv->name, dest_priv->name);
/* summary */
as_copy_l10n_hashtable (src_priv->summary, dest_priv->summary);
/* description */
as_copy_l10n_hashtable (src_priv->description, dest_priv->description);
/* merge package names */
if ((src_priv->pkgnames != NULL) && (src_priv->pkgnames[0] != NULL))
as_component_set_pkgnames (dest_cpt, src_priv->pkgnames);
/* merge bundles */
if (as_component_has_bundle (src_cpt))
as_component_set_bundles_array (dest_cpt, as_component_get_bundles (src_cpt));
/* merge icons */
as_copy_gobject_array (src_priv->icons, src_priv->icons);
/* merge provided items */
as_copy_gobject_array (src_priv->provided, src_priv->provided);
}
/* the resulting component gets the origin of the highet value of both */
if (as_component_get_origin_kind (src_cpt) > as_component_get_origin_kind (dest_cpt))
as_component_set_origin_kind (dest_cpt, as_component_get_origin_kind (src_cpt));
g_debug ("Merged data for '%s'", as_component_get_data_id (dest_cpt));
}
/**
* as_component_merge:
* @cpt: An #AsComponent.
* @source: An #AsComponent with a merge type.
*
* Merge data from component @source into @cpt, respecting the
* merge mode of @source.
*
* Returns: %TRUE if the merge was successful.
*/
gboolean
as_component_merge (AsComponent *cpt, AsComponent *source)
{
AsMergeKind merge_kind = as_component_get_merge_kind (source);
g_return_val_if_fail (merge_kind != AS_MERGE_KIND_NONE, FALSE);
as_component_merge_with_mode (cpt, source, merge_kind);
return TRUE;
}
/**
* as_component_set_kind_from_node:
*/
static void
as_component_set_kind_from_node (AsComponent *cpt, xmlNode *node)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
g_autofree gchar *cpttype = NULL;
/* find out which kind of component we are dealing with */
cpttype = (gchar*) xmlGetProp (node, (xmlChar*) "type");
if ((cpttype == NULL) || (g_strcmp0 (cpttype, "generic") == 0)) {
priv->kind = AS_COMPONENT_KIND_GENERIC;
} else {
priv->kind = as_component_kind_from_string (cpttype);
if (priv->kind == AS_COMPONENT_KIND_UNKNOWN)
g_debug ("Found unknown component type: %s", cpttype);
}
}
/**
* as_xml_metainfo_description_to_cpt:
*
* Helper function for GHashTable
*/
static void
as_xml_metainfo_description_to_cpt (gchar *key, GString *value, AsComponent *cpt)
{
as_component_set_description (cpt, value->str, key);
g_string_free (value, TRUE);
}
/**
* as_component_load_provides_from_xml:
*/
static void
as_component_load_provides_from_xml (AsComponent *cpt, xmlNode *node)
{
xmlNode *iter;
gchar *node_name;
for (iter = node->children; iter != NULL; iter = iter->next) {
g_autofree gchar *content = NULL;
/* discard spaces */
if (iter->type != XML_ELEMENT_NODE)
continue;
node_name = (gchar*) iter->name;
content = as_xml_get_node_value (iter);
if (content == NULL)
continue;
if (g_strcmp0 (node_name, "library") == 0) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_LIBRARY, content);
} else if (g_strcmp0 (node_name, "binary") == 0) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_BINARY, content);
} else if (g_strcmp0 (node_name, "font") == 0) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_FONT, content);
} else if (g_strcmp0 (node_name, "modalias") == 0) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_MODALIAS, content);
} else if (g_strcmp0 (node_name, "firmware") == 0) {
g_autofree gchar *fwtype = NULL;
fwtype = (gchar*) xmlGetProp (iter, (xmlChar*) "type");
if (fwtype != NULL) {
if (g_strcmp0 (fwtype, "runtime") == 0)
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_FIRMWARE_RUNTIME, content);
else if (g_strcmp0 (fwtype, "flashed") == 0)
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_FIRMWARE_FLASHED, content);
}
} else if (g_strcmp0 (node_name, "python2") == 0) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_PYTHON_2, content);
} else if (g_strcmp0 (node_name, "python3") == 0) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_PYTHON, content);
} else if (g_strcmp0 (node_name, "dbus") == 0) {
g_autofree gchar *dbustype = NULL;
dbustype = (gchar*) xmlGetProp (iter, (xmlChar*) "type");
if (g_strcmp0 (dbustype, "system") == 0)
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_DBUS_SYSTEM, content);
else if ((g_strcmp0 (dbustype, "user") == 0) || (g_strcmp0 (dbustype, "session") == 0))
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_DBUS_USER, content);
}
}
}
/**
* as_component_xml_parse_languages_node:
*/
static void
as_component_xml_parse_languages_node (AsComponent* cpt, xmlNode* node)
{
xmlNode *iter;
for (iter = node->children; iter != NULL; iter = iter->next) {
/* discard spaces */
if (iter->type != XML_ELEMENT_NODE)
continue;
if (g_strcmp0 ((gchar*) iter->name, "lang") == 0) {
guint64 percentage = 0;
g_autofree gchar *locale = NULL;
g_autofree gchar *prop = NULL;
prop = (gchar*) xmlGetProp (iter, (xmlChar*) "percentage");
if (prop != NULL)
percentage = g_ascii_strtoll (prop, NULL, 10);
locale = as_xml_get_node_value (iter);
as_component_add_language (cpt, locale, percentage);
}
}
}
/**
* as_component_load_launchable_from_xml:
*
* Loads <launchable/> data from an XML node.
**/
static void
as_component_load_launchable_from_xml (AsComponent *cpt, xmlNode *node)
{
AsLaunchableKind lkind;
AsLaunchable *launchable;
g_autofree gchar *value = NULL;
lkind = as_launchable_kind_from_string ((gchar*) xmlGetProp (node, (xmlChar*) "type"));
if (lkind == AS_LAUNCHABLE_KIND_UNKNOWN)
return;
launchable = as_component_get_launchable (cpt, lkind);
if (launchable == NULL) {
launchable = as_launchable_new ();
as_launchable_set_kind (launchable, lkind);
as_component_add_launchable (cpt, launchable);
g_object_unref (launchable);
}
value = as_xml_get_node_value (node);
if (value == NULL)
return;
as_launchable_add_entry (launchable, value);
}
/**
* as_component_xml_parse_custom_node:
*/
static void
as_component_xml_parse_custom_node (AsComponent *cpt, xmlNode *node)
{
xmlNode *iter;
GHashTable *custom;
custom = as_component_get_custom (cpt);
for (iter = node->children; iter != NULL; iter = iter->next) {
gchar *key_str = NULL;
if (iter->type != XML_ELEMENT_NODE)
continue;
if (g_strcmp0 ((gchar*) iter->name, "value") != 0)
continue;
key_str = (gchar*) xmlGetProp (iter, (xmlChar*) "key");
if (key_str == NULL)
continue;
g_hash_table_insert (custom,
key_str,
as_xml_get_node_value (iter));
}
}
/**
* as_component_load_from_xml:
* @cpt: An #AsComponent.
* @ctx: the AppStream document context.
* @node: the XML node.
* @error: a #GError.
*
* Loads data from an XML node.
**/
gboolean
as_component_load_from_xml (AsComponent *cpt, AsContext *ctx, xmlNode *node, GError **error)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
xmlNode *iter;
const gchar *node_name;
g_autoptr(GPtrArray) pkgnames = NULL;
g_autofree gchar *priority_str = NULL;
g_autofree gchar *merge_str = NULL;
/* sanity check */
if ((g_strcmp0 ((gchar*) node->name, "component") != 0) && (g_strcmp0 ((gchar*) node->name, "application") != 0)) {
g_set_error (error,
AS_METADATA_ERROR,
AS_METADATA_ERROR_FAILED,
"Expected 'component' tag, but got '%s' instead.", (gchar*) node->name);
return FALSE;
}
pkgnames = g_ptr_array_new_with_free_func (g_free);
/* set component kind */
as_component_set_kind_from_node (cpt, node);
/* set the priority for this component */
priority_str = (gchar*) xmlGetProp (node, (xmlChar*) "priority");
if (priority_str == NULL) {
priv->priority = as_context_get_priority (ctx);
} else {
priv->priority = g_ascii_strtoll (priority_str, NULL, 10);
}
/* set the merge method for this component */
merge_str = (gchar*) xmlGetProp (node, (xmlChar*) "merge");
if (merge_str != NULL) {
priv->merge_kind = as_merge_kind_from_string (merge_str);
}
/* set context for this component */
as_component_set_context (cpt, ctx);
for (iter = node->children; iter != NULL; iter = iter->next) {
g_autofree gchar *content = NULL;
g_autofree gchar *lang = NULL;
/* discard spaces */
if (iter->type != XML_ELEMENT_NODE)
continue;
node_name = (const gchar*) iter->name;
content = as_xml_get_node_value (iter);
lang = as_xmldata_get_node_locale (ctx, iter);
if (g_strcmp0 (node_name, "id") == 0) {
as_component_set_id (cpt, content);
if ((as_context_get_style (ctx) == AS_FORMAT_STYLE_METAINFO) && (priv->kind == AS_COMPONENT_KIND_GENERIC)) {
/* parse legacy component type information */
as_component_set_kind_from_node (cpt, iter);
}
} else if (g_strcmp0 (node_name, "pkgname") == 0) {
if (content != NULL)
g_ptr_array_add (pkgnames, g_strdup (content));
} else if (g_strcmp0 (node_name, "source_pkgname") == 0) {
as_component_set_source_pkgname (cpt, content);
} else if (g_strcmp0 (node_name, "name") == 0) {
if (lang != NULL)
as_component_set_name (cpt, content, lang);
} else if (g_strcmp0 (node_name, "summary") == 0) {
if (lang != NULL)
as_component_set_summary (cpt, content, lang);
} else if (g_strcmp0 (node_name, "description") == 0) {
if (as_context_get_style (ctx) == AS_FORMAT_STYLE_COLLECTION) {
/* for collection XML, the "description" tag has a language property, so parsing it is simple */
if (lang != NULL) {
gchar *desc;
desc = as_xml_dump_node_children (iter);
as_component_set_description (cpt, desc, lang);
g_free (desc);
}
} else {
as_xml_parse_metainfo_description_node (ctx,
iter,
(GHFunc) as_xml_metainfo_description_to_cpt,
cpt);
}
} else if (g_strcmp0 (node_name, "icon") == 0) {
g_autoptr(AsIcon) icon = NULL;
if (content == NULL)
continue;
icon = as_icon_new ();
if (as_icon_load_from_xml (icon, ctx, iter, NULL))
as_component_add_icon (cpt, icon);
} else if (g_strcmp0 (node_name, "url") == 0) {
if (content != NULL) {
g_autofree gchar *urltype_str = NULL;
AsUrlKind url_kind;
urltype_str = (gchar*) xmlGetProp (iter, (xmlChar*) "type");
url_kind = as_url_kind_from_string (urltype_str);
if (url_kind != AS_URL_KIND_UNKNOWN)
as_component_add_url (cpt, url_kind, content);
}
} else if (g_strcmp0 (node_name, "categories") == 0) {
as_xml_add_children_values_to_array (iter,
"category",
priv->categories);
} else if (g_strcmp0 (node_name, "keywords") == 0) {
if (lang != NULL) {
g_auto(GStrv) kw_array = NULL;
kw_array = as_xml_get_children_as_strv (iter, "keyword");
as_component_set_keywords (cpt, kw_array, lang);
}
} else if (g_strcmp0 (node_name, "mimetypes") == 0) {
g_autoptr(GPtrArray) mime_list = NULL;
guint i;
/* Mimetypes are essentially provided interfaces, that's why they belong into AsProvided.
* However, due to historic reasons, the spec has an own toplevel tag for them, so we need
* to parse them here. */
mime_list = as_xml_get_children_as_string_list (iter, "mimetype");
for (i = 0; i < mime_list->len; i++) {
as_component_add_provided_item (cpt,
AS_PROVIDED_KIND_MIMETYPE,
(const gchar*) g_ptr_array_index (mime_list, i));
}
} else if (g_strcmp0 (node_name, "provides") == 0) {
as_component_load_provides_from_xml (cpt, iter);
} else if (g_strcmp0 (node_name, "screenshots") == 0) {
xmlNode *iter2;
for (iter2 = iter->children; iter2 != NULL; iter2 = iter2->next) {
if (iter2->type != XML_ELEMENT_NODE)
continue;
if (g_strcmp0 ((const gchar*) iter2->name, "screenshot") == 0) {
g_autoptr(AsScreenshot) screenshot = as_screenshot_new ();
if (as_screenshot_load_from_xml (screenshot, ctx, iter2, NULL))
as_component_add_screenshot (cpt, screenshot);
}
}
} else if (g_strcmp0 (node_name, "metadata_license") == 0) {
as_component_set_metadata_license (cpt, content);
} else if (g_strcmp0 (node_name, "project_license") == 0) {
as_component_set_project_license (cpt, content);
} else if (g_strcmp0 (node_name, "project_group") == 0) {
as_component_set_project_group (cpt, content);
} else if (g_strcmp0 (node_name, "developer_name") == 0) {
if (lang != NULL)
as_component_set_developer_name (cpt, content, lang);
} else if (g_strcmp0 (node_name, "compulsory_for_desktop") == 0) {
if (content != NULL)
as_component_set_compulsory_for_desktop (cpt, content);
} else if (g_strcmp0 (node_name, "releases") == 0) {
xmlNode *iter2;
for (iter2 = iter->children; iter2 != NULL; iter2 = iter2->next) {
if (iter2->type != XML_ELEMENT_NODE)
continue;
if (g_strcmp0 ((const gchar*) iter2->name, "release") == 0) {
g_autoptr(AsRelease) release = as_release_new ();
if (as_release_load_from_xml (release, ctx, iter2, NULL))
as_component_add_release (cpt, release);
}
}
} else if (g_strcmp0 (node_name, "extends") == 0) {
as_component_add_extends (cpt, content);
} else if (g_strcmp0 (node_name, "languages") == 0) {
as_component_xml_parse_languages_node (cpt, iter);
} else if (g_strcmp0 (node_name, "launchable") == 0) {
as_component_load_launchable_from_xml (cpt, iter);
} else if (g_strcmp0 (node_name, "bundle") == 0) {
g_autoptr(AsBundle) bundle = as_bundle_new ();
if (as_bundle_load_from_xml (bundle, ctx, iter, NULL))
as_component_add_bundle (cpt, bundle);
} else if (g_strcmp0 (node_name, "translation") == 0) {
if (content != NULL) {
g_autoptr(AsTranslation) tr = as_translation_new ();
if (as_translation_load_from_xml (tr, ctx, iter, NULL))
as_component_add_translation (cpt, tr);
}
} else if (g_strcmp0 (node_name, "suggests") == 0) {
g_autoptr(AsSuggested) suggested = as_suggested_new ();
if (as_suggested_load_from_xml (suggested, ctx, iter, NULL))
as_component_add_suggested (cpt, suggested);
} else if (g_strcmp0 (node_name, "custom") == 0) {
as_component_xml_parse_custom_node (cpt, iter);
} else if (g_strcmp0 (node_name, "content_rating") == 0) {
g_autoptr(AsContentRating) ctrating = as_content_rating_new ();
if (as_content_rating_load_from_xml (ctrating, ctx, iter, NULL))
as_component_add_content_rating (cpt, ctrating);
}
}
/* add package name information to component */
{
g_auto(GStrv) strv = as_ptr_array_to_strv (pkgnames);
as_component_set_pkgnames (cpt, strv);
}
return TRUE;
}
/**
* as_component_xml_keywords_to_node:
*/
static void
as_component_xml_keywords_to_node (AsComponent *cpt, xmlNode *root)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, priv->keywords);
while (g_hash_table_iter_next (&iter, &key, &value)) {
xmlNode *node;
const gchar *locale = (const gchar*) key;
gchar **kws = (gchar**) value;
/* skip cruft */
if (as_is_cruft_locale (locale))
continue;
node = as_xml_add_node_list_strv (root, "keywords", "keyword", kws);
if (node == NULL)
continue;
if (g_strcmp0 (locale, "C") != 0) {
xmlNewProp (node,
(xmlChar*) "xml:lang",
(xmlChar*) locale);
}
}
}
/**
* as_component_xml_serialize_provides:
*/
static void
as_component_xml_serialize_provides (AsComponent *cpt, xmlNode *cnode)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
xmlNode *node;
GPtrArray *items;
guint i;
AsProvided *prov_mime;
if (priv->provided->len == 0)
return;
prov_mime = as_component_get_provided_for_kind (cpt, AS_PROVIDED_KIND_MIMETYPE);
if (prov_mime != NULL) {
/* mimetypes get special treatment in XML for historical reasons */
node = xmlNewChild (cnode, NULL, (xmlChar*) "mimetypes", NULL);
items = as_provided_get_items (prov_mime);
for (i = 0; i < items->len; i++) {
xmlNewTextChild (node,
NULL,
(xmlChar*) "mimetype",
(xmlChar*) g_ptr_array_index (items, i));
}
/* check if we only had mimetype provided items, in that case we don't need to continue */
if (priv->provided->len == 1)
return;
}
node = xmlNewChild (cnode, NULL, (xmlChar*) "provides", NULL);
for (i = 0; i < priv->provided->len; i++) {
guint j;
AsProvided *prov = AS_PROVIDED (g_ptr_array_index (priv->provided, i));
items = as_provided_get_items (prov);
switch (as_provided_get_kind (prov)) {
case AS_PROVIDED_KIND_MIMETYPE:
/* we already handled those */
break;
case AS_PROVIDED_KIND_LIBRARY:
as_xml_add_node_list (node, NULL,
"library",
items);
break;
case AS_PROVIDED_KIND_BINARY:
as_xml_add_node_list (node, NULL,
"binary",
items);
break;
case AS_PROVIDED_KIND_MODALIAS:
as_xml_add_node_list (node, NULL,
"modalias",
items);
break;
case AS_PROVIDED_KIND_PYTHON_2:
as_xml_add_node_list (node, NULL,
"python2",
items);
break;
case AS_PROVIDED_KIND_PYTHON:
as_xml_add_node_list (node, NULL,
"python3",
items);
break;
case AS_PROVIDED_KIND_FONT:
as_xml_add_node_list (node, NULL,
"font",
items);
break;
case AS_PROVIDED_KIND_FIRMWARE_RUNTIME:
for (j = 0; j < items->len; j++) {
xmlNode *n;
n = xmlNewTextChild (node, NULL,
(xmlChar*) "firmware",
(xmlChar*) g_ptr_array_index (items, j));
xmlNewProp (n,
(xmlChar*) "type",
(xmlChar*) "runtime");
}
break;
case AS_PROVIDED_KIND_FIRMWARE_FLASHED:
for (j = 0; j < items->len; j++) {
xmlNode *n;
n = xmlNewTextChild (node, NULL,
(xmlChar*) "firmware",
(xmlChar*) g_ptr_array_index (items, j));
xmlNewProp (n,
(xmlChar*) "type",
(xmlChar*) "runtime");
}
break;
case AS_PROVIDED_KIND_DBUS_SYSTEM:
for (j = 0; j < items->len; j++) {
xmlNode *n;
n = xmlNewTextChild (node, NULL,
(xmlChar*) "dbus",
(xmlChar*) g_ptr_array_index (items, j));
xmlNewProp (n,
(xmlChar*) "type",
(xmlChar*) "system");
}
break;
case AS_PROVIDED_KIND_DBUS_USER:
for (j = 0; j < items->len; j++) {
xmlNode *n;
n = xmlNewTextChild (node, NULL,
(xmlChar*) "dbus",
(xmlChar*) g_ptr_array_index (items, j));
xmlNewProp (n,
(xmlChar*) "type",
(xmlChar*) "user");
}
break;
default:
g_debug ("Couldn't serialize provided-item type '%s'", as_provided_kind_to_string (as_provided_get_kind (prov)));
break;
}
}
}
/**
* as_component_xml_serialize_languages:
*/
static void
as_component_xml_serialize_languages (AsComponent *cpt, xmlNode *cptnode)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
xmlNode *node;
GHashTableIter iter;
gpointer key, value;
if (g_hash_table_size (priv->languages) == 0)
return;
node = xmlNewChild (cptnode, NULL, (xmlChar*) "languages", NULL);
g_hash_table_iter_init (&iter, priv->languages);
while (g_hash_table_iter_next (&iter, &key, &value)) {
guint percentage;
const gchar *locale;
xmlNode *l_node;
g_autofree gchar *percentage_str = NULL;
locale = (const gchar*) key;
percentage = GPOINTER_TO_INT (value);
percentage_str = g_strdup_printf("%i", percentage);
l_node = xmlNewTextChild (node,
NULL,
(xmlChar*) "lang",
(xmlChar*) locale);
xmlNewProp (l_node,
(xmlChar*) "percentage",
(xmlChar*) percentage_str);
}
}
/**
* as_component_xml_serialize_custom:
*/
static void
as_component_xml_serialize_custom (AsComponent *cpt, xmlNode *cptnode)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
xmlNode *node;
GHashTableIter iter;
gpointer key, value;
if (g_hash_table_size (priv->custom) == 0)
return;
node = xmlNewChild (cptnode, NULL, (xmlChar*) "custom", NULL);
g_hash_table_iter_init (&iter, priv->custom);
while (g_hash_table_iter_next (&iter, &key, &value)) {
xmlNode *snode;
snode = xmlNewTextChild (node,
NULL,
(xmlChar*) "value",
(xmlChar*) value);
xmlNewProp (snode,
(xmlChar*) "key",
(xmlChar*) key);
}
}
/**
* as_component_to_xml_node:
* @cpt: an #AsComponent.
* @ctx: the AppStream document context.
* @root: XML node to attach the new nodes to.
*
* Serializes the data to an XML node.
**/
xmlNode*
as_component_to_xml_node (AsComponent *cpt, AsContext *ctx, xmlNode *root)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
xmlNode *cnode;
guint i;
/* define component root node properties */
if (root == NULL)
cnode = xmlNewNode (NULL, (xmlChar*) "component");
else
cnode = xmlNewChild (root, NULL, (xmlChar*) "component", NULL);
if ((priv->kind != AS_COMPONENT_KIND_GENERIC) && (priv->kind != AS_COMPONENT_KIND_UNKNOWN)) {
const gchar *kind_str;
if ((as_context_get_format_version (ctx) < AS_FORMAT_VERSION_V0_10) && (priv->kind == AS_COMPONENT_KIND_DESKTOP_APP))
kind_str = "desktop";
else
kind_str = as_component_kind_to_string (priv->kind);
xmlNewProp (cnode, (xmlChar*) "type",
(xmlChar*) kind_str);
}
if (as_context_get_style (ctx) == AS_FORMAT_STYLE_COLLECTION) {
/* write some propties which only exist in collection XML */
if (priv->merge_kind != AS_MERGE_KIND_NONE) {
xmlNewProp (cnode, (xmlChar*) "merge",
(xmlChar*) as_merge_kind_to_string (priv->merge_kind));
}
if (priv->priority != 0) {
g_autofree gchar *priority_str = g_strdup_printf ("%i", priv->priority);
xmlNewProp (cnode, (xmlChar*) "priority", (xmlChar*) priority_str);
}
}
/* component tags */
as_xml_add_text_node (cnode, "id", as_component_get_id (cpt));
as_xml_add_localized_text_node (cnode, "name", priv->name);
as_xml_add_localized_text_node (cnode, "summary", priv->summary);
/* order license and project group after name/summary */
if (as_context_get_style (ctx) == AS_FORMAT_STYLE_METAINFO)
as_xml_add_text_node (cnode, "metadata_license", priv->metadata_license);
as_xml_add_text_node (cnode, "project_license", priv->project_license);
as_xml_add_text_node (cnode, "project_group", priv->project_group);
/* developer name */
as_xml_add_localized_text_node (cnode, "developer_name", priv->developer_name);
/* long description */
as_xml_add_description_node (ctx, cnode, priv->description);
as_xml_add_node_list_strv (cnode, NULL, "pkgname", priv->pkgnames);
as_xml_add_node_list (cnode, NULL, "extends", priv->extends);
as_xml_add_node_list (cnode, NULL, "compulsory_for_desktop", priv->compulsory_for_desktops);
as_xml_add_node_list (cnode, "categories", "category", priv->categories);
/* keywords */
as_component_xml_keywords_to_node (cpt, cnode);
/* urls */
for (i = AS_URL_KIND_UNKNOWN; i < AS_URL_KIND_LAST; i++) {
xmlNode *n;
const gchar *value;
value = as_component_get_url (cpt, i);
if (value == NULL)
continue;
n = xmlNewTextChild (cnode, NULL, (xmlChar*) "url", (xmlChar*) value);
xmlNewProp (n, (xmlChar*) "type",
(xmlChar*) as_url_kind_to_string (i));
}
/* icons */
for (i = 0; i < priv->icons->len; i++) {
AsIcon *icon = AS_ICON (g_ptr_array_index (priv->icons, i));
as_icon_to_xml_node (icon, ctx, cnode);
}
/* bundles */
for (i = 0; i < priv->bundles->len; i++) {
AsBundle *bundle = AS_BUNDLE (g_ptr_array_index (priv->bundles, i));
as_bundle_to_xml_node (bundle, ctx, cnode);
}
/* launchables */
for (i = 0; i < priv->launchables->len; i++) {
AsLaunchable *launchable = AS_LAUNCHABLE (g_ptr_array_index (priv->launchables, i));
as_launchable_to_xml_node (launchable, ctx, cnode);
}
/* translations */
if (priv->translations != NULL) {
for (i = 0; i < priv->translations->len; i++) {
AsTranslation *tr = AS_TRANSLATION (g_ptr_array_index (priv->translations, i));
as_translation_to_xml_node (tr, ctx, cnode);
}
}
/* screenshots */
if (priv->screenshots->len > 0) {
xmlNode *rnode = xmlNewChild (cnode, NULL, (xmlChar*) "screenshots", NULL);
for (i = 0; i < priv->screenshots->len; i++) {
AsScreenshot *scr = AS_SCREENSHOT (g_ptr_array_index (priv->screenshots, i));
as_screenshot_to_xml_node (scr, ctx, rnode);
}
}
/* releases */
if (priv->releases->len > 0) {
xmlNode *rnode = xmlNewChild (cnode, NULL, (xmlChar*) "releases", NULL);
for (i = 0; i < priv->releases->len; i++) {
AsRelease *rel = AS_RELEASE (g_ptr_array_index (priv->releases, i));
as_release_to_xml_node (rel, ctx, rnode);
}
}
/* provides & mimetypes node */
as_component_xml_serialize_provides (cpt, cnode);
/* languages node */
as_component_xml_serialize_languages (cpt, cnode);
/* suggests nodes */
for (i = 0; i < priv->suggestions->len; i++) {
AsSuggested *suggested = AS_SUGGESTED (g_ptr_array_index (priv->suggestions, i));
as_suggested_to_xml_node (suggested, ctx, cnode);
}
/* content_rating nodes */
for (i = 0; i < priv->content_ratings->len; i++) {
AsContentRating *ctrating = AS_CONTENT_RATING (g_ptr_array_index (priv->content_ratings, i));
as_content_rating_to_xml_node (ctrating, ctx, cnode);
}
/* custom node */
as_component_xml_serialize_custom (cpt, cnode);
return cnode;
}
/**
* as_component_yaml_parse_keywords:
*
* Process a keywords node and add the data to an #AsComponent
*/
static void
as_component_yaml_parse_keywords (AsComponent *cpt, AsContext *ctx, GNode *node)
{
GNode *tnode;
g_autoptr(GPtrArray) keywords = NULL;
g_auto(GStrv) strv = NULL;
keywords = g_ptr_array_new_with_free_func (g_free);
tnode = as_yaml_get_localized_node (ctx, node, NULL);
/* no node found? */
if (tnode == NULL)
return;
as_yaml_list_to_str_array (tnode, keywords);
strv = as_ptr_array_to_strv (keywords);
as_component_set_keywords (cpt,
strv,
as_yaml_node_get_key (tnode));
}
/**
* as_component_yaml_parse_urls:
*/
static void
as_component_yaml_parse_urls (AsComponent *cpt, GNode *node)
{
GNode *n;
AsUrlKind url_kind;
for (n = node->children; n != NULL; n = n->next) {
const gchar *key = as_yaml_node_get_key (n);
const gchar *value = as_yaml_node_get_value (n);
url_kind = as_url_kind_from_string (key);
if ((url_kind != AS_URL_KIND_UNKNOWN) && (value != NULL))
as_component_add_url (cpt, url_kind, value);
}
}
/**
* as_component_yaml_parse_icon:
*/
static void
as_component_yaml_parse_icon (AsComponent *cpt, AsContext *ctx, GNode *node, AsIconKind kind)
{
GNode *n;
guint64 size;
guint scale;
g_autoptr(AsIcon) icon = NULL;
icon = as_icon_new ();
as_icon_set_kind (icon, kind);
for (n = node->children; n != NULL; n = n->next) {
const gchar *key = as_yaml_node_get_key (n);
const gchar *value = as_yaml_node_get_value (n);
if (g_strcmp0 (key, "width") == 0) {
size = g_ascii_strtoull (value, NULL, 10);
as_icon_set_width (icon, size);
} else if (g_strcmp0 (key, "height") == 0) {
size = g_ascii_strtoull (value, NULL, 10);
as_icon_set_height (icon, size);
} else if (g_strcmp0 (key, "scale") == 0) {
scale = g_ascii_strtoull (value, NULL, 10);
as_icon_set_scale (icon, scale);
} else {
if (kind == AS_ICON_KIND_REMOTE) {
if (g_strcmp0 (key, "url") == 0) {
if (as_context_has_media_baseurl (ctx)) {
/* handle the media baseurl */
g_autofree gchar *url = NULL;
url = g_build_filename (as_context_get_media_baseurl (ctx), value, NULL);
as_icon_set_url (icon, url);
} else {
/* no baseurl, we can just set the value as URL */
as_icon_set_url (icon, value);
}
}
} else {
if (g_strcmp0 (key, "name") == 0) {
as_icon_set_filename (icon, value);
}
}
}
}
as_component_add_icon (cpt, icon);
}
/**
* as_component_yaml_parse_icons:
*/
static void
as_component_yaml_parse_icons (AsComponent *cpt, AsContext *ctx, GNode *node)
{
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
const gchar *key = as_yaml_node_get_key (n);
const gchar *value = as_yaml_node_get_value (n);
if (g_strcmp0 (key, "stock") == 0) {
g_autoptr(AsIcon) icon = as_icon_new ();
as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
as_icon_set_name (icon, value);
as_component_add_icon (cpt, icon);
} else if (g_strcmp0 (key, "cached") == 0) {
if (value != NULL) {
g_autoptr(AsIcon) icon = as_icon_new ();
/* we have a legacy YAML file */
as_icon_set_kind (icon, AS_ICON_KIND_CACHED);
as_icon_set_filename (icon, value);
as_component_add_icon (cpt, icon);
} else {
GNode *sn;
/* we have a recent YAML file */
for (sn = n->children; sn != NULL; sn = sn->next)
as_component_yaml_parse_icon (cpt, ctx, sn, AS_ICON_KIND_CACHED);
}
} else if (g_strcmp0 (key, "local") == 0) {
GNode *sn;
for (sn = n->children; sn != NULL; sn = sn->next)
as_component_yaml_parse_icon (cpt, ctx, sn, AS_ICON_KIND_LOCAL);
} else if (g_strcmp0 (key, "remote") == 0) {
GNode *sn;
for (sn = n->children; sn != NULL; sn = sn->next)
as_component_yaml_parse_icon (cpt, ctx, sn, AS_ICON_KIND_REMOTE);
}
}
}
/**
* as_component_yaml_parse_provides:
*/
static void
as_component_yaml_parse_provides (AsComponent *cpt, GNode *node)
{
GNode *n;
GNode *sn;
for (n = node->children; n != NULL; n = n->next) {
const gchar *key = as_yaml_node_get_key (n);
if (g_strcmp0 (key, "libraries") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_LIBRARY, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "binaries") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_BINARY, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "fonts") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_FONT, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "modaliases") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_MODALIAS, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "firmware") == 0) {
GNode *dn;
for (sn = n->children; sn != NULL; sn = sn->next) {
gchar *kind = NULL;
gchar *fwdata = NULL;
for (dn = sn->children; dn != NULL; dn = dn->next) {
gchar *dkey;
gchar *dvalue;
dkey = (gchar*) dn->data;
if (dn->children)
dvalue = (gchar*) dn->children->data;
else
continue;
if (g_strcmp0 (dkey, "type") == 0) {
kind = dvalue;
} else if ((g_strcmp0 (dkey, "guid") == 0) || (g_strcmp0 (dkey, "file") == 0)) {
fwdata = dvalue;
}
}
/* we don't add malformed provides types */
if ((kind == NULL) || (fwdata == NULL))
continue;
if (g_strcmp0 (kind, "runtime") == 0)
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_FIRMWARE_RUNTIME, fwdata);
else if (g_strcmp0 (kind, "flashed") == 0)
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_FIRMWARE_FLASHED, fwdata);
}
} else if (g_strcmp0 (key, "python2") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_PYTHON_2, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "python3") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_PYTHON, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "mimetypes") == 0) {
for (sn = n->children; sn != NULL; sn = sn->next) {
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_MIMETYPE, (gchar*) sn->data);
}
} else if (g_strcmp0 (key, "dbus") == 0) {
GNode *dn;
for (sn = n->children; sn != NULL; sn = sn->next) {
gchar *kind = NULL;
gchar *service = NULL;
for (dn = sn->children; dn != NULL; dn = dn->next) {
gchar *dkey;
gchar *dvalue;
dkey = (gchar*) dn->data;
if (dn->children)
dvalue = (gchar*) dn->children->data;
else
dvalue = NULL;
if (g_strcmp0 (dkey, "type") == 0) {
kind = dvalue;
} else if (g_strcmp0 (dkey, "service") == 0) {
service = dvalue;
}
}
/* we don't add malformed provides types */
if ((kind == NULL) || (service == NULL))
continue;
if (g_strcmp0 (kind, "system") == 0)
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_DBUS_SYSTEM, service);
else if ((g_strcmp0 (kind, "user") == 0) || (g_strcmp0 (kind, "session") == 0))
as_component_add_provided_item (cpt, AS_PROVIDED_KIND_DBUS_USER, service);
}
}
}
}
/**
* as_component_yaml_parse_languages:
*/
static void
as_component_yaml_parse_languages (AsComponent *cpt, GNode *node)
{
GNode *sn;
for (sn = node->children; sn != NULL; sn = sn->next) {
GNode *n;
g_autofree gchar *locale = NULL;
g_autofree gchar *percentage_str = NULL;
for (n = sn->children; n != NULL; n = n->next) {
const gchar *key = as_yaml_node_get_key (n);
const gchar *value = as_yaml_node_get_value (n);
if (g_strcmp0 (key, "locale") == 0) {
if (locale == NULL)
locale = g_strdup (value);
} else if (g_strcmp0 (key, "percentage") == 0) {
if (percentage_str == NULL)
percentage_str = g_strdup (value);
} else {
as_yaml_print_unknown ("Languages", key);
}
}
if ((locale != NULL) && (percentage_str != NULL))
as_component_add_language (cpt,
locale,
g_ascii_strtoll (percentage_str, NULL, 10));
}
}
/**
* as_component_yaml_parse_custom:
*/
static void
as_component_yaml_parse_custom (AsComponent *cpt, GNode *node)
{
GNode *sn;
for (sn = node->children; sn != NULL; sn = sn->next) {
const gchar *key = as_yaml_node_get_key (sn);
const gchar *value = as_yaml_node_get_value (sn);
as_component_insert_custom_value (cpt, key, value);
}
}
/**
* as_component_load_from_yaml:
* @cpt: an #AsComponent.
* @ctx: the AppStream document context.
* @root: the YAML node.
* @error: a #GError.
*
* Loads data from a YAML field.
**/
gboolean
as_component_load_from_yaml (AsComponent *cpt, AsContext *ctx, GNode *root, GError **error)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GNode *node;
/* set context for this component */
as_component_set_context (cpt, ctx);
/* set component default priority */
priv->priority = as_context_get_priority (ctx);
for (node = root->children; node != NULL; node = node->next) {
const gchar *key;
const gchar *value;
gchar *lvalue;
if (node->children == NULL)
continue;
key = as_yaml_node_get_key (node);
value = as_yaml_node_get_value (node);
if (g_strcmp0 (key, "Type") == 0) {
if (g_strcmp0 (value, "generic") == 0)
priv->kind = AS_COMPONENT_KIND_GENERIC;
else
priv->kind = as_component_kind_from_string (value);
} else if (g_strcmp0 (key, "ID") == 0) {
as_component_set_id (cpt, value);
} else if (g_strcmp0 (key, "Priority") == 0) {
priv->priority = g_ascii_strtoll (value, NULL, 10);
} else if (g_strcmp0 (key, "Merge") == 0) {
priv->merge_kind = as_merge_kind_from_string (value);
} else if (g_strcmp0 (key, "Package") == 0) {
g_auto(GStrv) strv = NULL;
strv = g_new0 (gchar*, 1 + 1);
strv[0] = g_strdup (value);
strv[1] = NULL;
as_component_set_pkgnames (cpt, strv);
} else if (g_strcmp0 (key, "SourcePackage") == 0) {
as_component_set_source_pkgname (cpt, value);
} else if (g_strcmp0 (key, "Name") == 0) {
lvalue = as_yaml_get_localized_value (ctx, node, "C");
if (lvalue != NULL) {
as_component_set_name (cpt, lvalue, "C"); /* Unlocalized */
g_free (lvalue);
}
lvalue = as_yaml_get_localized_value (ctx, node, NULL);
as_component_set_name (cpt, lvalue, NULL);
g_free (lvalue);
} else if (g_strcmp0 (key, "Summary") == 0) {
lvalue = as_yaml_get_localized_value (ctx, node, NULL);
as_component_set_summary (cpt, lvalue, NULL);
g_free (lvalue);
} else if (g_strcmp0 (key, "Description") == 0) {
lvalue = as_yaml_get_localized_value (ctx, node, NULL);
as_component_set_description (cpt, lvalue, NULL);
g_free (lvalue);
} else if (g_strcmp0 (key, "DeveloperName") == 0) {
lvalue = as_yaml_get_localized_value (ctx, node, NULL);
as_component_set_developer_name (cpt, lvalue, NULL);
g_free (lvalue);
} else if (g_strcmp0 (key, "ProjectLicense") == 0) {
as_component_set_project_license (cpt, value);
} else if (g_strcmp0 (key, "ProjectGroup") == 0) {
as_component_set_project_group (cpt, value);
} else if (g_strcmp0 (key, "Categories") == 0) {
as_yaml_list_to_str_array (node, priv->categories);
} else if (g_strcmp0 (key, "CompulsoryForDesktops") == 0) {
as_yaml_list_to_str_array (node, priv->compulsory_for_desktops);
} else if (g_strcmp0 (key, "Extends") == 0) {
as_yaml_list_to_str_array (node, priv->extends);
} else if (g_strcmp0 (key, "Keywords") == 0) {
as_component_yaml_parse_keywords (cpt, ctx, node);
} else if (g_strcmp0 (key, "Url") == 0) {
as_component_yaml_parse_urls (cpt, node);
} else if (g_strcmp0 (key, "Icon") == 0) {
as_component_yaml_parse_icons (cpt, ctx, node);
} else if (g_strcmp0 (key, "Bundles") == 0) {
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
g_autoptr(AsBundle) bundle = as_bundle_new ();
if (as_bundle_load_from_yaml (bundle, ctx, n, NULL))
as_component_add_bundle (cpt, bundle);
}
} else if (g_strcmp0 (key, "Launchable") == 0) {
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
g_autoptr(AsLaunchable) launch = as_launchable_new ();
if (as_launchable_load_from_yaml (launch, ctx, n, NULL))
as_component_add_launchable (cpt, launch);
}
} else if (g_strcmp0 (key, "Provides") == 0) {
as_component_yaml_parse_provides (cpt, node);
} else if (g_strcmp0 (key, "Screenshots") == 0) {
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
g_autoptr(AsScreenshot) scr = as_screenshot_new ();
if (as_screenshot_load_from_yaml (scr, ctx, n, NULL))
as_component_add_screenshot (cpt, scr);
}
} else if (g_strcmp0 (key, "Languages") == 0) {
as_component_yaml_parse_languages (cpt, node);
} else if (g_strcmp0 (key, "Releases") == 0) {
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
g_autoptr(AsRelease) release = as_release_new ();
if (as_release_load_from_yaml (release, ctx, n, NULL))
as_component_add_release (cpt, release);
}
} else if (g_strcmp0 (key, "Suggests") == 0) {
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
g_autoptr(AsSuggested) suggested = as_suggested_new ();
if (as_suggested_load_from_yaml (suggested, ctx, n, NULL))
as_component_add_suggested (cpt, suggested);
}
} else if (g_strcmp0 (key, "ContentRating") == 0) {
GNode *n;
for (n = node->children; n != NULL; n = n->next) {
g_autoptr(AsContentRating) rating = as_content_rating_new ();
if (as_content_rating_load_from_yaml (rating, ctx, n, NULL))
as_component_add_content_rating (cpt, rating);
}
} else if (g_strcmp0 (key, "Custom") == 0) {
as_component_yaml_parse_custom (cpt, node);
} else {
as_yaml_print_unknown ("root", key);
}
}
return TRUE;
}
/**
* as_component_yaml_emit_icons:
*/
static void
as_component_yaml_emit_icons (AsComponent* cpt, yaml_emitter_t *emitter, GPtrArray *icons)
{
guint i;
GHashTableIter iter;
gpointer key, value;
gboolean stock_icon_added = FALSE;
g_autoptr(GHashTable) icons_table = NULL;
/* we need to emit the icons in order, so we first need to sort them properly */
icons_table = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify) g_ptr_array_unref);
for (i = 0; i < icons->len; i++) {
GPtrArray *ilist;
AsIconKind ikind;
AsIcon *icon = AS_ICON (g_ptr_array_index (icons, i));
ikind = as_icon_get_kind (icon);
ilist = g_hash_table_lookup (icons_table, GINT_TO_POINTER (ikind));
if (ilist == NULL) {
ilist = g_ptr_array_new ();
g_hash_table_insert (icons_table, GINT_TO_POINTER (ikind), ilist);
}
g_ptr_array_add (ilist, icon);
}
g_hash_table_iter_init (&iter, icons_table);
while (g_hash_table_iter_next (&iter, &key, &value)) {
GPtrArray *ilist;
AsIconKind ikind;
ikind = (AsIconKind) GPOINTER_TO_INT (key);
ilist = (GPtrArray*) value;
if (ikind == AS_ICON_KIND_STOCK) {
/* there can always be only one stock icon, so this is easy */
if (!stock_icon_added)
as_yaml_emit_entry (emitter,
as_icon_kind_to_string (ikind),
as_icon_get_name (AS_ICON (g_ptr_array_index (ilist, 0))));
stock_icon_added = TRUE;
} else {
as_yaml_emit_scalar (emitter, as_icon_kind_to_string (ikind));
as_yaml_sequence_start (emitter);
for (i = 0; i < ilist->len; i++) {
AsIcon *icon = AS_ICON (g_ptr_array_index (ilist, i));
as_yaml_mapping_start (emitter);
if (ikind == AS_ICON_KIND_REMOTE)
as_yaml_emit_entry (emitter, "url", as_icon_get_url (icon));
else if (ikind == AS_ICON_KIND_LOCAL)
as_yaml_emit_entry (emitter, "name", as_icon_get_filename (icon));
else
as_yaml_emit_entry (emitter, "name", as_icon_get_name (icon));
if (as_icon_get_width (icon) > 0) {
as_yaml_emit_entry_uint (emitter,
"width",
as_icon_get_width (icon));
}
if (as_icon_get_height (icon) > 0) {
as_yaml_emit_entry_uint (emitter,
"height",
as_icon_get_height (icon));
}
if (as_icon_get_scale (icon) > 1) {
as_yaml_emit_entry_uint (emitter,
"scale",
as_icon_get_scale (icon));
}
as_yaml_mapping_end (emitter);
}
as_yaml_sequence_end (emitter);
}
}
}
/**
* as_component_yaml_emit_provides:
*/
static void
as_component_yaml_emit_provides (AsComponent *cpt, yaml_emitter_t *emitter)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
g_autoptr(GPtrArray) dbus_system = NULL;
g_autoptr(GPtrArray) dbus_user = NULL;
g_autoptr(GPtrArray) fw_runtime = NULL;
g_autoptr(GPtrArray) fw_flashed = NULL;
if (priv->provided->len == 0)
return;
as_yaml_emit_scalar (emitter, "Provides");
as_yaml_mapping_start (emitter);
for (i = 0; i < priv->provided->len; i++) {
AsProvidedKind kind;
GPtrArray *items;
guint j;
AsProvided *prov = AS_PROVIDED (g_ptr_array_index (priv->provided, i));
items = as_provided_get_items (prov);
if (items->len == 0)
continue;
kind = as_provided_get_kind (prov);
switch (kind) {
case AS_PROVIDED_KIND_LIBRARY:
as_yaml_emit_sequence (emitter,
"libraries",
items);
break;
case AS_PROVIDED_KIND_BINARY:
as_yaml_emit_sequence (emitter,
"binaries",
items);
break;
case AS_PROVIDED_KIND_MIMETYPE:
as_yaml_emit_sequence (emitter,
"mimetypes",
items);
break;
case AS_PROVIDED_KIND_PYTHON_2:
as_yaml_emit_sequence (emitter,
"python2",
items);
break;
case AS_PROVIDED_KIND_PYTHON:
as_yaml_emit_sequence (emitter,
"python3",
items);
break;
case AS_PROVIDED_KIND_MODALIAS:
as_yaml_emit_sequence (emitter,
"modaliases",
items);
break;
case AS_PROVIDED_KIND_FONT:
as_yaml_emit_scalar (emitter, "fonts");
as_yaml_sequence_start (emitter);
for (j = 0; j < items->len; j++) {
as_yaml_mapping_start (emitter);
as_yaml_emit_entry (emitter,
"name",
(const gchar*) g_ptr_array_index (items, j));
as_yaml_mapping_end (emitter);
}
as_yaml_sequence_end (emitter);
break;
case AS_PROVIDED_KIND_DBUS_SYSTEM:
if (dbus_system == NULL) {
dbus_system = g_ptr_array_ref (items);
} else {
g_critical ("Hit dbus:system twice, this should never happen!");
}
break;
case AS_PROVIDED_KIND_DBUS_USER:
if (dbus_user == NULL) {
dbus_user = g_ptr_array_ref (items);
} else {
g_critical ("Hit dbus:user twice, this should never happen!");
}
break;
case AS_PROVIDED_KIND_FIRMWARE_RUNTIME:
if (fw_runtime == NULL) {
fw_runtime = g_ptr_array_ref (items);
} else {
g_critical ("Hit firmware:runtime twice, this should never happen!");
}
break;
case AS_PROVIDED_KIND_FIRMWARE_FLASHED:
if (fw_flashed == NULL) {
fw_flashed = g_ptr_array_ref (items);
} else {
g_critical ("Hit dbus-user twice, this should never happen!");
}
break;
default:
g_warning ("Ignoring unknown type of provided items: %s", as_provided_kind_to_string (kind));
break;
}
}
/* dbus subsection */
if ((dbus_system != NULL) || (dbus_user != NULL)) {
as_yaml_emit_scalar (emitter, "dbus");
as_yaml_sequence_start (emitter);
if (dbus_system != NULL) {
for (i = 0; i < dbus_system->len; i++) {
const gchar *value = (const gchar*) g_ptr_array_index (dbus_system, i);
as_yaml_mapping_start (emitter);
as_yaml_emit_entry (emitter, "type", "system");
as_yaml_emit_entry (emitter, "service", value);
as_yaml_mapping_end (emitter);
}
}
if (dbus_user != NULL) {
for (i = 0; i < dbus_user->len; i++) {
const gchar *value = (const gchar*) g_ptr_array_index (dbus_user, i);
as_yaml_mapping_start (emitter);
as_yaml_emit_entry (emitter, "type", "user");
as_yaml_emit_entry (emitter, "service", value);
as_yaml_mapping_end (emitter);
}
}
as_yaml_sequence_end (emitter);
}
/* firmware subsection */
if ((fw_runtime != NULL) || (fw_flashed != NULL)) {
as_yaml_emit_scalar (emitter, "firmware");
as_yaml_sequence_start (emitter);
if (fw_runtime != NULL) {
for (i = 0; i < fw_runtime->len; i++) {
const gchar *value = g_ptr_array_index (fw_runtime, i);
as_yaml_mapping_start (emitter);
as_yaml_emit_entry (emitter, "type", "runtime");
as_yaml_emit_entry (emitter, "guid", value);
as_yaml_mapping_end (emitter);
}
}
if (fw_flashed != NULL) {
for (i = 0; i < fw_flashed->len; i++) {
const gchar *value = g_ptr_array_index (fw_flashed, i);
as_yaml_mapping_start (emitter);
as_yaml_emit_entry (emitter, "type", "flashed");
as_yaml_emit_entry (emitter, "file", value);
as_yaml_mapping_end (emitter);
}
}
as_yaml_sequence_end (emitter);
}
as_yaml_mapping_end (emitter);
}
/**
* as_component_yaml_emit_languages:
*/
static void
as_component_yaml_emit_languages (AsComponent *cpt, yaml_emitter_t *emitter)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GHashTableIter iter;
gpointer key, value;
if (g_hash_table_size (priv->languages) == 0)
return;
as_yaml_emit_scalar (emitter, "Languages");
as_yaml_sequence_start (emitter);
g_hash_table_iter_init (&iter, priv->languages);
while (g_hash_table_iter_next (&iter, &key, &value)) {
guint percentage;
const gchar *locale;
locale = (const gchar*) key;
percentage = GPOINTER_TO_INT (value);
as_yaml_mapping_start (emitter);
as_yaml_emit_entry (emitter, "locale", locale);
as_yaml_emit_entry_uint (emitter, "percentage", percentage);
as_yaml_mapping_end (emitter);
}
as_yaml_sequence_end (emitter);
}
/**
* as_component_yaml_emit_custom:
*/
static void
as_component_yaml_emit_custom (AsComponent *cpt, yaml_emitter_t *emitter)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GHashTableIter iter;
gpointer key, value;
if (g_hash_table_size (priv->custom) == 0)
return;
as_yaml_emit_scalar (emitter, "Custom");
as_yaml_mapping_start (emitter);
g_hash_table_iter_init (&iter, priv->custom);
while (g_hash_table_iter_next (&iter, &key, &value)) {
as_yaml_emit_entry (emitter,
(const gchar*) key,
(const gchar*) value);
}
as_yaml_mapping_end (emitter);
}
/**
* as_component_emit_yaml:
* @cpt: an #AsComponent.
* @ctx: the AppStream document context.
* @emitter: The YAML emitter to emit data on.
*
* Emit YAML data for this object.
**/
void
as_component_emit_yaml (AsComponent *cpt, AsContext *ctx, yaml_emitter_t *emitter)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
guint i;
gint res;
const gchar *cstr;
yaml_event_t event;
/* new document for this component */
yaml_document_start_event_initialize (&event, NULL, NULL, NULL, FALSE);
res = yaml_emitter_emit (emitter, &event);
g_assert (res);
/* open main mapping */
as_yaml_mapping_start (emitter);
/* write component kind */
if ((as_context_get_format_version (ctx) < AS_FORMAT_VERSION_V0_10) && (priv->kind == AS_COMPONENT_KIND_DESKTOP_APP))
cstr = "desktop-app";
else
cstr = as_component_kind_to_string (priv->kind);
as_yaml_emit_entry (emitter, "Type", cstr);
/* AppStream-ID */
as_yaml_emit_entry (emitter, "ID", priv->id);
/* Priority */
if (priv->priority != 0) {
g_autofree gchar *priority_str = g_strdup_printf ("%i", priv->priority);
as_yaml_emit_entry (emitter,
"Priority",
priority_str);
}
/* merge strategy */
if (priv->merge_kind != AS_MERGE_KIND_NONE) {
as_yaml_emit_entry (emitter,
"Merge",
as_merge_kind_to_string (priv->merge_kind));
}
/* SourcePackage */
as_yaml_emit_entry (emitter, "SourcePackage", priv->source_pkgname);
/* Package */
/* NOTE: a DEP-11 components do *not* support multiple packages per component */
if ((priv->pkgnames != NULL) && (priv->pkgnames[0] != NULL))
as_yaml_emit_entry (emitter, "Package", priv->pkgnames[0]);
/* Extends */
as_yaml_emit_sequence (emitter, "Extends", priv->extends);
/* Name */
as_yaml_emit_localized_entry (emitter, "Name", priv->name);
/* Summary */
as_yaml_emit_localized_entry (emitter, "Summary", priv->summary);
/* Description */
as_yaml_emit_long_localized_entry (emitter, "Description", priv->description);
/* DeveloperName */
as_yaml_emit_localized_entry (emitter, "DeveloperName", priv->developer_name);
/* ProjectGroup */
as_yaml_emit_entry (emitter, "ProjectGroup", priv->project_group);
/* ProjectLicense */
as_yaml_emit_entry (emitter, "ProjectLicense", priv->project_license);
/* CompulsoryForDesktops */
as_yaml_emit_sequence_from_str_array (emitter, "CompulsoryForDesktops", priv->compulsory_for_desktops);
/* Categories */
as_yaml_emit_sequence_from_str_array (emitter, "Categories", priv->categories);
/* Keywords */
as_yaml_emit_localized_strv (emitter, "Keywords", priv->keywords);
/* Urls */
if (g_hash_table_size (priv->urls) > 0) {
GHashTableIter iter;
gpointer key, value;
as_yaml_emit_scalar (emitter, "Url");
as_yaml_mapping_start (emitter);
g_hash_table_iter_init (&iter, priv->urls);
while (g_hash_table_iter_next (&iter, &key, &value)) {
as_yaml_emit_entry (emitter, as_url_kind_to_string (GPOINTER_TO_INT (key)), (const gchar*) value);
}
as_yaml_mapping_end (emitter);
}
/* Icons */
if (priv->icons->len > 0) {
as_yaml_emit_scalar (emitter, "Icon");
as_yaml_mapping_start (emitter);
as_component_yaml_emit_icons (cpt, emitter, priv->icons);
as_yaml_mapping_end (emitter);
}
/* Bundles */
if (priv->bundles->len > 0) {
as_yaml_emit_scalar (emitter, "Bundles");
as_yaml_sequence_start (emitter);
for (i = 0; i < priv->bundles->len; i++) {
AsBundle *bundle = AS_BUNDLE (g_ptr_array_index (priv->bundles, i));
as_bundle_emit_yaml (bundle, ctx, emitter);
}
as_yaml_sequence_end (emitter);
}
/* Launchable */
if (priv->launchables->len > 0) {
as_yaml_emit_scalar (emitter, "Launchable");
as_yaml_mapping_start (emitter);
for (i = 0; i < priv->launchables->len; i++) {
AsLaunchable *launch = AS_LAUNCHABLE (g_ptr_array_index (priv->launchables, i));
as_launchable_emit_yaml (launch, ctx, emitter);
}
as_yaml_mapping_end (emitter);
}
/* Provides */
as_component_yaml_emit_provides (cpt, emitter);
/* Screenshots */
if (priv->screenshots->len > 0) {
as_yaml_emit_scalar (emitter, "Screenshots");
as_yaml_sequence_start (emitter);
for (i = 0; i < priv->screenshots->len; i++) {
AsScreenshot *screenshot = AS_SCREENSHOT (g_ptr_array_index (priv->screenshots, i));
as_screenshot_emit_yaml (screenshot, ctx, emitter);
}
as_yaml_sequence_end (emitter);
}
/* Translation details */
as_component_yaml_emit_languages (cpt, emitter);
/* Releases */
if (priv->releases->len > 0) {
as_yaml_emit_scalar (emitter, "Releases");
as_yaml_sequence_start (emitter);
for (i = 0; i < priv->releases->len; i++) {
AsRelease *release = AS_RELEASE (g_ptr_array_index (priv->releases, i));
as_release_emit_yaml (release, ctx, emitter);
}
as_yaml_sequence_end (emitter);
}
/* Suggests */
if (priv->suggestions->len > 0) {
as_yaml_emit_scalar (emitter, "Suggests");
as_yaml_sequence_start (emitter);
for (i = 0; i < priv->suggestions->len; i++) {
AsSuggested *suggested = AS_SUGGESTED (g_ptr_array_index (priv->suggestions, i));
as_suggested_emit_yaml (suggested, ctx, emitter);
}
as_yaml_sequence_end (emitter);
}
/* ContentRating */
if (priv->content_ratings->len > 0) {
as_yaml_emit_scalar (emitter, "ContentRating");
as_yaml_mapping_start (emitter);
for (i = 0; i < priv->content_ratings->len; i++) {
AsContentRating *content_rating = AS_CONTENT_RATING (g_ptr_array_index (priv->content_ratings, i));
as_content_rating_emit_yaml (content_rating, ctx, emitter);
}
as_yaml_mapping_end (emitter);
}
/* Custom fields */
as_component_yaml_emit_custom (cpt, emitter);
/* close main mapping */
as_yaml_mapping_end (emitter);
/* finalize the document */
yaml_document_end_event_initialize (&event, 1);
res = yaml_emitter_emit (emitter, &event);
g_assert (res);
}
/**
* as_component_to_variant:
* @cpt: an #AsComponent.
* @builder: A #GVariantBuilder
*
* Serialize the current active state of this object to a GVariant
* for use in the on-disk binary cache.
*/
void
as_component_to_variant (AsComponent *cpt, GVariantBuilder *builder)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GVariantBuilder cb;
guint i;
/* start serializing our component */
g_variant_builder_init (&cb, G_VARIANT_TYPE_VARDICT);
/* type */
as_variant_builder_add_kv (&cb, "type",
g_variant_new_uint32 (priv->kind));
/* id */
as_variant_builder_add_kv (&cb, "id",
as_variant_mstring_new (priv->id));
/* name */
as_variant_builder_add_kv (&cb, "name",
as_variant_mstring_new (as_component_get_name (cpt)));
/* summary */
as_variant_builder_add_kv (&cb, "summary",
as_variant_mstring_new (as_component_get_summary (cpt)));
/* source package name */
as_variant_builder_add_kv (&cb, "source_pkgname",
as_variant_mstring_new (priv->source_pkgname));
/* package name */
as_variant_builder_add_kv (&cb, "pkgnames",
g_variant_new_strv ((const gchar* const*) priv->pkgnames, priv->pkgnames? -1 : 0));
/* origin */
as_variant_builder_add_kv (&cb, "origin",
as_variant_mstring_new (as_component_get_origin (cpt)));
/* bundles */
if (priv->bundles->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->bundles->len; i++) {
as_bundle_to_variant (AS_BUNDLE (g_ptr_array_index (priv->bundles, i)), &array_b);
}
as_variant_builder_add_kv (&cb, "bundles", g_variant_builder_end (&array_b));
}
/* launchables */
if (priv->launchables->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->launchables->len; i++) {
as_launchable_to_variant (AS_LAUNCHABLE (g_ptr_array_index (priv->launchables, i)), &array_b);
}
as_variant_builder_add_kv (&cb, "launchables", g_variant_builder_end (&array_b));
}
/* extends */
as_variant_builder_add_kv (&cb, "extends",
as_variant_from_string_ptrarray (priv->extends));
/* URLs */
if (g_hash_table_size (priv->urls) > 0) {
GHashTableIter iter;
gpointer key, value;
GVariantBuilder dict_b;
g_variant_builder_init (&dict_b, G_VARIANT_TYPE_DICTIONARY);
g_hash_table_iter_init (&iter, priv->urls);
while (g_hash_table_iter_next (&iter, &key, &value)) {
g_variant_builder_add (&dict_b,
"{uv}",
(AsUrlKind) GPOINTER_TO_INT (key),
g_variant_new_string ((const gchar*) value));
}
as_variant_builder_add_kv (&cb, "urls",
g_variant_builder_end (&dict_b));
}
/* icons */
if (priv->icons->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->icons->len; i++) {
AsIcon *icon = AS_ICON (g_ptr_array_index (priv->icons, i));
as_icon_to_variant (icon, &array_b);
}
as_variant_builder_add_kv (&cb, "icons",
g_variant_builder_end (&array_b));
}
/* long description */
as_variant_builder_add_kv (&cb, "description",
as_variant_mstring_new (as_component_get_description (cpt)));
/* categories */
as_variant_builder_add_kv (&cb, "categories",
as_variant_from_string_ptrarray (priv->categories));
/* compulsory-for-desktop */
as_variant_builder_add_kv (&cb, "compulsory_for",
as_variant_from_string_ptrarray (priv->compulsory_for_desktops));
/* project license */
as_variant_builder_add_kv (&cb, "project_license",
as_variant_mstring_new (priv->project_license));
/* project group */
as_variant_builder_add_kv (&cb, "project_group",
as_variant_mstring_new (priv->project_group));
/* developer name */
as_variant_builder_add_kv (&cb, "developer_name",
as_variant_mstring_new (as_component_get_developer_name (cpt)));
/* provided items */
if (priv->provided->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->provided->len; i++) {
AsProvided *prov = AS_PROVIDED (g_ptr_array_index (priv->provided, i));
as_provided_to_variant (prov, &array_b);
}
as_variant_builder_add_kv (&cb, "provided",
g_variant_builder_end (&array_b));
}
/* screenshots */
if (priv->screenshots->len > 0) {
GVariantBuilder array_b;
gboolean screenshot_added = FALSE;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->screenshots->len; i++) {
AsScreenshot *scr = AS_SCREENSHOT (g_ptr_array_index (priv->screenshots, i));
if (as_screenshot_to_variant (scr, &array_b))
screenshot_added = TRUE;
}
if (screenshot_added)
as_variant_builder_add_kv (&cb, "screenshots",
g_variant_builder_end (&array_b));
}
/* releases */
if (priv->releases->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->releases->len; i++) {
AsRelease *rel = AS_RELEASE (g_ptr_array_index (priv->releases, i));
as_release_to_variant (rel, &array_b);
}
as_variant_builder_add_kv (&cb, "releases",
g_variant_builder_end (&array_b));
}
/* languages */
if (g_hash_table_size (priv->languages) > 0) {
GHashTableIter iter;
gpointer key, value;
GVariantBuilder dict_b;
g_variant_builder_init (&dict_b, G_VARIANT_TYPE_DICTIONARY);
g_hash_table_iter_init (&iter, priv->languages);
while (g_hash_table_iter_next (&iter, &key, &value)) {
g_variant_builder_add (&dict_b,
"{su}",
(const gchar*) key,
GPOINTER_TO_INT (value));
}
as_variant_builder_add_kv (&cb, "languages",
g_variant_builder_end (&dict_b));
}
/* suggestions */
if (priv->suggestions->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->suggestions->len; i++) {
AsSuggested *suggested = AS_SUGGESTED (g_ptr_array_index (priv->suggestions, i));
as_suggested_to_variant (suggested, &array_b);
}
as_variant_builder_add_kv (&cb, "suggestions",
g_variant_builder_end (&array_b));
}
/* content ratings */
if (priv->content_ratings->len > 0) {
GVariantBuilder array_b;
g_variant_builder_init (&array_b, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < priv->content_ratings->len; i++) {
AsContentRating *rating = AS_CONTENT_RATING (g_ptr_array_index (priv->content_ratings, i));
as_content_rating_to_variant (rating, &array_b);
}
as_variant_builder_add_kv (&cb,
"content_ratings",
g_variant_builder_end (&array_b));
}
/* custom data */
if (g_hash_table_size (priv->custom) > 0) {
GHashTableIter iter;
gpointer key, value;
GVariantBuilder dict_b;
g_variant_builder_init (&dict_b, G_VARIANT_TYPE_DICTIONARY);
g_hash_table_iter_init (&iter, priv->custom);
while (g_hash_table_iter_next (&iter, &key, &value)) {
if ((key == NULL) || (value == NULL))
continue;
g_variant_builder_add (&dict_b,
"{ss}",
(const gchar*) key,
(const gchar*) value);
}
as_variant_builder_add_kv (&cb, "custom",
g_variant_builder_end (&dict_b));
}
/* search tokens */
as_component_create_token_cache (cpt);
if (g_hash_table_size (priv->token_cache) > 0) {
GHashTableIter iter;
gpointer key, value;
GVariantBuilder dict_b;
g_variant_builder_init (&dict_b, G_VARIANT_TYPE_DICTIONARY);
g_hash_table_iter_init (&iter, priv->token_cache);
while (g_hash_table_iter_next (&iter, &key, &value)) {
if (value == NULL)
continue;
g_variant_builder_add (&dict_b, "{su}",
(const gchar*) key, *((AsTokenType*) value));
}
as_variant_builder_add_kv (&cb, "tokens",
g_variant_builder_end (&dict_b));
}
/* add to component list */
g_variant_builder_add_value (builder, g_variant_builder_end (&cb));
}
/**
* as_component_set_from_variant:
* @cpt: an #AsComponent.
* @variant: The #GVariant to read from.
*
* Read the active state of this object from a #GVariant serialization.
* This is used by the on-disk binary cache.
*/
gboolean
as_component_set_from_variant (AsComponent *cpt, GVariant *variant, const gchar *locale)
{
AsComponentPrivate *priv = GET_PRIVATE (cpt);
GVariantDict dict;
const gchar **strv;
GVariant *var;
GVariantIter gvi;
g_variant_dict_init (&dict, variant);
/* type */
priv->kind = as_variant_get_dict_uint32 (&dict, "type");
/* active locale */
as_component_set_active_locale (cpt, locale);
/* id */
as_component_set_id (cpt,
as_variant_get_dict_mstr (&dict, "id", &var));
g_variant_unref (var);
/* name */
as_component_set_name (cpt,
as_variant_get_dict_mstr (&dict, "name", &var),
locale);
g_variant_unref (var);
/* summary */
as_component_set_summary (cpt,
as_variant_get_dict_mstr (&dict, "summary", &var),
locale);
g_variant_unref (var);
/* source package name */
as_component_set_source_pkgname (cpt,
as_variant_get_dict_mstr (&dict, "source_pkgname", &var));
g_variant_unref (var);
/* package names */
strv = as_variant_get_dict_strv (&dict, "pkgnames", &var);
as_component_set_pkgnames (cpt, (gchar **) strv);
g_free (strv);
g_variant_unref (var);
/* origin */
as_component_set_origin (cpt, as_variant_get_dict_mstr (&dict, "origin", &var));
g_variant_unref (var);
/* bundles */
var = g_variant_dict_lookup_value (&dict, "bundles", G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsBundle) bundle = as_bundle_new ();
if (as_bundle_set_from_variant (bundle, child))
as_component_add_bundle (cpt, bundle);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* launchables */
var = g_variant_dict_lookup_value (&dict, "launchables", G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsLaunchable) launch = as_launchable_new ();
if (as_launchable_set_from_variant (launch, child))
as_component_add_launchable (cpt, launch);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* extends */
as_variant_to_string_ptrarray_by_dict (&dict,
"extends",
priv->extends);
/* URLs */
var = g_variant_dict_lookup_value (&dict,
"urls",
G_VARIANT_TYPE_DICTIONARY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
AsUrlKind kind;
g_autoptr(GVariant) url_var = NULL;
g_variant_get (child, "{uv}", &kind, &url_var);
as_component_add_url (cpt,
kind,
g_variant_get_string (url_var, NULL));
g_variant_unref (child);
}
g_variant_unref (var);
}
/* icons */
var = g_variant_dict_lookup_value (&dict,
"icons",
G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsIcon) icon = as_icon_new ();
if (as_icon_set_from_variant (icon, child))
as_component_add_icon (cpt, icon);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* long description */
as_component_set_description (cpt,
as_variant_get_dict_mstr (&dict, "description", &var),
locale);
g_variant_unref (var);
/* categories */
as_variant_to_string_ptrarray_by_dict (&dict,
"categories",
priv->categories);
/* compulsory-for-desktop */
as_variant_to_string_ptrarray_by_dict (&dict,
"compulsory_for",
priv->compulsory_for_desktops);
/* project license */
as_component_set_project_license (cpt, as_variant_get_dict_mstr (&dict, "project_license", &var));
g_variant_unref (var);
/* project group */
as_component_set_project_group (cpt, as_variant_get_dict_mstr (&dict, "project_group", &var));
g_variant_unref (var);
/* developer name */
as_component_set_developer_name (cpt,
as_variant_get_dict_mstr (&dict, "developer_name", &var),
locale);
g_variant_unref (var);
/* provided items */
var = g_variant_dict_lookup_value (&dict,
"provided",
G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsProvided) prov = as_provided_new ();
if (as_provided_set_from_variant (prov, child))
as_component_add_provided (cpt, prov);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* screenshots */
var = g_variant_dict_lookup_value (&dict,
"screenshots",
G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsScreenshot) scr = as_screenshot_new ();
if (as_screenshot_set_from_variant (scr, child, locale))
as_component_add_screenshot (cpt, scr);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* releases */
var = g_variant_dict_lookup_value (&dict,
"releases",
G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsRelease) rel = as_release_new ();
if (as_release_set_from_variant (rel, child, locale))
as_component_add_release (cpt, rel);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* languages */
var = g_variant_dict_lookup_value (&dict,
"languages",
G_VARIANT_TYPE_DICTIONARY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
guint percentage;
g_autofree gchar *lang;
g_variant_get (child, "{su}", &lang, &percentage);
as_component_add_language (cpt, lang, percentage);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* suggestions */
var = g_variant_dict_lookup_value (&dict,
"suggestions",
G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsSuggested) suggested = as_suggested_new ();
if (as_suggested_set_from_variant (suggested, child))
as_component_add_suggested (cpt, suggested);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* content ratings */
var = g_variant_dict_lookup_value (&dict,
"content_ratings",
G_VARIANT_TYPE_ARRAY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autoptr(AsContentRating) rating = as_content_rating_new ();
if (as_content_rating_set_from_variant (rating, child))
as_component_add_content_rating (cpt, rating);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* custom data */
var = g_variant_dict_lookup_value (&dict,
"custom",
G_VARIANT_TYPE_DICTIONARY);
if (var != NULL) {
GVariant *child;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
g_autofree gchar *key = NULL;
g_autofree gchar *value = NULL;
g_variant_get (child, "{ss}", &key, &value);
as_component_insert_custom_value (cpt, key, value);
g_variant_unref (child);
}
g_variant_unref (var);
}
/* search tokens */
var = g_variant_dict_lookup_value (&dict,
"tokens",
G_VARIANT_TYPE_DICTIONARY);
if (var != NULL) {
GVariant *child;
gboolean tokens_added = FALSE;
g_variant_iter_init (&gvi, var);
while ((child = g_variant_iter_next_value (&gvi))) {
guint score;
gchar *token;
AsTokenType *match_pval;
g_variant_get (child, "{su}", &token, &score);
match_pval = g_new0 (AsTokenType, 1);
*match_pval = score;
g_hash_table_insert (priv->token_cache,
token,
match_pval);
tokens_added = TRUE;
g_variant_unref (child);
}
/* we added things to the token cache, so we just assume it's valid */
if (tokens_added)
as_component_set_token_cache_valid (cpt, TRUE);
g_variant_unref (var);
}
return TRUE;
}
/**
* as_component_get_desktop_id:
* @cpt: a #AsComponent instance.
*
* Get the Desktop Entry ID for this component, if it is
* of type "desktop-application".
* For most desktop-application components, this is the name
* of the .desktop file.
*
* Refer to https://specifications.freedesktop.org/desktop-entry-spec/latest/ape.html for more
* information.
*
* Returns: The desktop file id.
*
* Since: 0.9.8
* Deprecated: 0.11.0: Replaced by #AsLaunchable and %as_component_get_launchable
*/
const gchar*
as_component_get_desktop_id (AsComponent *cpt)
{
AsLaunchable *launch;
GPtrArray *entries;
launch = as_component_get_launchable (cpt, AS_LAUNCHABLE_KIND_DESKTOP_ID);
if (launch == NULL)
return NULL;
entries = as_launchable_get_entries (launch);
if (entries->len <= 0)
return 0;
return (const gchar*) g_ptr_array_index (entries, 0);
}
/**
* as_component_get_property:
*/
static void
as_component_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
{
AsComponent *cpt;
cpt = G_TYPE_CHECK_INSTANCE_CAST (object, AS_TYPE_COMPONENT, AsComponent);
AsComponentPrivate *priv = GET_PRIVATE (cpt);
switch (property_id) {
case AS_COMPONENT_KIND:
g_value_set_enum (value, as_component_get_kind (cpt));
break;
case AS_COMPONENT_PKGNAMES:
g_value_set_boxed (value, as_component_get_pkgnames (cpt));
break;
case AS_COMPONENT_ID:
g_value_set_string (value, as_component_get_id (cpt));
break;
case AS_COMPONENT_NAME:
g_value_set_string (value, as_component_get_name (cpt));
break;
case AS_COMPONENT_SUMMARY:
g_value_set_string (value, as_component_get_summary (cpt));
break;
case AS_COMPONENT_DESCRIPTION:
g_value_set_string (value, as_component_get_description (cpt));
break;
case AS_COMPONENT_KEYWORDS:
g_value_set_boxed (value, as_component_get_keywords (cpt));
break;
case AS_COMPONENT_ICONS:
g_value_set_pointer (value, as_component_get_icons (cpt));
break;
case AS_COMPONENT_URLS:
g_value_set_boxed (value, priv->urls);
break;
case AS_COMPONENT_CATEGORIES:
g_value_set_boxed (value, as_component_get_categories (cpt));
break;
case AS_COMPONENT_PROJECT_LICENSE:
g_value_set_string (value, as_component_get_project_license (cpt));
break;
case AS_COMPONENT_PROJECT_GROUP:
g_value_set_string (value, as_component_get_project_group (cpt));
break;
case AS_COMPONENT_DEVELOPER_NAME:
g_value_set_string (value, as_component_get_developer_name (cpt));
break;
case AS_COMPONENT_SCREENSHOTS:
g_value_set_boxed (value, as_component_get_screenshots (cpt));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* as_component_set_property:
*/
static void
as_component_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
{
AsComponent *cpt;
cpt = G_TYPE_CHECK_INSTANCE_CAST (object, AS_TYPE_COMPONENT, AsComponent);
switch (property_id) {
case AS_COMPONENT_KIND:
as_component_set_kind (cpt, g_value_get_enum (value));
break;
case AS_COMPONENT_PKGNAMES:
as_component_set_pkgnames (cpt, g_value_get_boxed (value));
break;
case AS_COMPONENT_ID:
as_component_set_id (cpt, g_value_get_string (value));
break;
case AS_COMPONENT_NAME:
as_component_set_name (cpt, g_value_get_string (value), NULL);
break;
case AS_COMPONENT_SUMMARY:
as_component_set_summary (cpt, g_value_get_string (value), NULL);
break;
case AS_COMPONENT_DESCRIPTION:
as_component_set_description (cpt, g_value_get_string (value), NULL);
break;
case AS_COMPONENT_KEYWORDS:
as_component_set_keywords (cpt, g_value_get_boxed (value), NULL);
break;
case AS_COMPONENT_PROJECT_LICENSE:
as_component_set_project_license (cpt, g_value_get_string (value));
break;
case AS_COMPONENT_PROJECT_GROUP:
as_component_set_project_group (cpt, g_value_get_string (value));
break;
case AS_COMPONENT_DEVELOPER_NAME:
as_component_set_developer_name (cpt, g_value_get_string (value), NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* as_component_class_init:
*/
static void
as_component_class_init (AsComponentClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = as_component_finalize;
object_class->get_property = as_component_get_property;
object_class->set_property = as_component_set_property;
/**
* AsComponent:kind:
*
* the #AsComponentKind of this component
*/
g_object_class_install_property (object_class,
AS_COMPONENT_KIND,
g_param_spec_enum ("kind", "kind", "kind", AS_TYPE_COMPONENT_KIND, 0, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:pkgnames:
*
* string array of packages name
*/
g_object_class_install_property (object_class,
AS_COMPONENT_PKGNAMES,
g_param_spec_boxed ("pkgnames", "pkgnames", "pkgnames", G_TYPE_STRV, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:id:
*
* the unique identifier
*/
g_object_class_install_property (object_class,
AS_COMPONENT_ID,
g_param_spec_string ("id", "id", "id", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:name:
*
* the name
*/
g_object_class_install_property (object_class,
AS_COMPONENT_NAME,
g_param_spec_string ("name", "name", "name", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:summary:
*
* the summary
*/
g_object_class_install_property (object_class,
AS_COMPONENT_SUMMARY,
g_param_spec_string ("summary", "summary", "summary", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:description:
*
* the description
*/
g_object_class_install_property (object_class,
AS_COMPONENT_DESCRIPTION,
g_param_spec_string ("description", "description", "description", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:keywords:
*
* string array of keywords
*/
g_object_class_install_property (object_class,
AS_COMPONENT_KEYWORDS,
g_param_spec_boxed ("keywords", "keywords", "keywords", G_TYPE_STRV, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:icons: (type GList(AsIcon))
*
* hash map of icon urls and sizes
*/
g_object_class_install_property (object_class,
AS_COMPONENT_ICONS,
g_param_spec_pointer ("icons", "icons", "icons", G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
/**
* AsComponent:urls: (type GHashTable(AsUrlKind,utf8))
*
* the urls associated with this component
*/
g_object_class_install_property (object_class,
AS_COMPONENT_URLS,
g_param_spec_boxed ("urls", "urls", "urls", G_TYPE_HASH_TABLE, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
/**
* AsComponent:categories:
*
* string array of categories
*/
g_object_class_install_property (object_class,
AS_COMPONENT_CATEGORIES,
g_param_spec_boxed ("categories", "categories", "categories", G_TYPE_PTR_ARRAY, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
/**
* AsComponent:project-license:
*
* the project license
*/
g_object_class_install_property (object_class,
AS_COMPONENT_PROJECT_LICENSE,
g_param_spec_string ("project-license", "project-license", "project-license", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:project-group:
*
* the project group
*/
g_object_class_install_property (object_class,
AS_COMPONENT_PROJECT_GROUP,
g_param_spec_string ("project-group", "project-group", "project-group", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:developer-name:
*
* the developer name
*/
g_object_class_install_property (object_class,
AS_COMPONENT_DEVELOPER_NAME,
g_param_spec_string ("developer-name", "developer-name", "developer-name", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
/**
* AsComponent:screenshots: (type GPtrArray(AsScreenshot)):
*
* An array of #AsScreenshot instances
*/
g_object_class_install_property (object_class,
AS_COMPONENT_SCREENSHOTS,
g_param_spec_boxed ("screenshots", "screenshots", "screenshots", G_TYPE_PTR_ARRAY, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
}
/**
* as_component_new:
*
* Creates a new #AsComponent.
*
* Returns: (transfer full): a new #AsComponent
**/
AsComponent*
as_component_new (void)
{
AsComponent *cpt;
cpt = g_object_new (AS_TYPE_COMPONENT, NULL);
return AS_COMPONENT (cpt);
}