blob: 7ac97918c25937e40e28f304e17de92fd947a25c [file] [log] [blame]
# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*-
#
# This program 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 of the License, or (at your option) any
# later version.
#
# This program 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 program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright (C) 2012 Lanedo GmbH
# Copyright (C) 2012-2022 Aleksander Morgado <aleksander@aleksander.es>
#
import string
import utils
from FieldResult import FieldResult
from Field import Field
"""
The Container class takes care of handling collections of Input or
Output fields
"""
class Container:
"""
Constructor
"""
def __init__(self, service, prefix, container_type, dictionary, common_objects_dictionary, static, since, compat):
# The current QMI service
self.service = service
# The field container prefix usually contains the name of the Message,
# e.g. "Qmi Message Ctl Something"
self.prefix = prefix
# We may have 'Input' or 'Output' containers
if container_type == 'Input':
self.readonly = False
elif container_type == 'Output':
self.readonly = True
else:
raise ValueError('Cannot handle container type \'%s\'' % container_type)
self.name = container_type
self.static = static
self.since = since
self.compat = compat
# Create the composed full name (prefix + name),
# e.g. "Qmi Message Ctl Something Output"
self.fullname = self.prefix + ' ' + self.name
self.fields = None
if dictionary is not None:
self.fields = []
new_dict = []
# First, look for references to common types
for field_dictionary in dictionary:
if 'common-ref' in field_dictionary:
for common in common_objects_dictionary:
if common['type'] == 'TLV' and \
common['common-ref'] == field_dictionary['common-ref']:
# Replace the reference with a copy of the common dictionary
# If the source reference has prerequisites, add them to the copy
copy = dict(common)
if 'prerequisites' in field_dictionary:
copy['prerequisites'] = field_dictionary['prerequisites']
# If an explicit 'since' is given in the field, prefer it over any other one
if 'since' in field_dictionary:
copy['since'] = field_dictionary['since']
# If the common type does not have any explicit 'since', take the one from the message
# If the common type has a 'since', take it only if it is newer than the one from the message
elif not 'since' in copy or utils.version_compare(copy['since'],self.since) > 0:
copy['since'] = self.since
new_dict.append(copy)
break
else:
raise RuntimeError('Common type \'%s\' not found' % field_dictionary['name'])
else:
new_dict.append(field_dictionary)
dictionary = new_dict
# We need to sort the fields, so that the ones with prerequisites are
# include after the prerequisites themselves. Note: we don't currently
# support complex setups yet.
sorted_dictionary = []
for field_dictionary in dictionary:
if 'prerequisites' in field_dictionary:
sorted_dictionary.append(field_dictionary)
else:
sorted_dictionary.insert(0, field_dictionary)
# Then, really parse each field
for field_dictionary in sorted_dictionary:
if field_dictionary['type'] == 'TLV':
if field_dictionary['format'] == 'sequence' and \
field_dictionary['name'] == 'Result':
self.fields.append(FieldResult(self.service, self.fullname, field_dictionary, common_objects_dictionary, container_type, static))
else:
self.fields.append(Field(self.service, self.fullname, field_dictionary, common_objects_dictionary, container_type, static))
"""
Emit enumeration of TLVs in the container
"""
def __emit_tlv_ids_enum(self, f):
if self.fields is None:
return
f.write('\n')
for tlv in self.fields:
translations = { 'enum_name' : tlv.id_enum_name,
'enum_value' : tlv.id }
template = (
'#define ${enum_name} ${enum_value}\n')
f.write(string.Template(template).substitute(translations))
"""
Emit new container types
"""
def __emit_types(self, hfile, cfile, translations):
translations['type_macro'] = 'QMI_TYPE_' + utils.remove_prefix(utils.build_underscore_uppercase_name(self.fullname), 'QMI_')
# Emit types header
template = '\n'
if not self.static and self.service != 'CTL':
template += (
'/**\n'
' * ${camelcase}:\n'
' *\n'
' * The #${camelcase} structure contains private data and should only be accessed\n'
' * using the provided API.\n'
' *\n'
' * Since: ${since}\n'
' */\n')
template += (
'typedef struct _${camelcase} ${camelcase};\n'
'${static}GType ${underscore}_get_type (void) G_GNUC_CONST;\n'
'#define ${type_macro} (${underscore}_get_type ())\n')
if self.compat:
template += (
'G_GNUC_INTERNAL\n'
'gpointer ${underscore}_get_compat_context (${camelcase} *self);\n'
'G_GNUC_INTERNAL\n'
'void ${underscore}_set_compat_context (\n'
' ${camelcase} *self,\n'
' gpointer compat_context,\n'
' GDestroyNotify compat_context_free);\n')
hfile.write(string.Template(template).substitute(translations))
# Emit types source
template = (
'\n'
'struct _${camelcase} {\n'
' volatile gint ref_count;\n')
if self.compat:
template += (
'\n'
' gpointer compat_context;\n'
' GDestroyNotify compat_context_free;\n')
cfile.write(string.Template(template).substitute(translations))
if self.fields is not None:
for field in self.fields:
if field.variable is not None:
translations['field_variable_name'] = field.variable_name
translations['field_name'] = field.name
template = (
'\n'
' /* ${field_name} */\n'
' gboolean ${field_variable_name}_set;\n')
cfile.write(string.Template(template).substitute(translations))
field.emit_variable_declaration(cfile)
cfile.write(
'};\n')
"""
Emit container handling core implementation
"""
def __emit_core(self, hfile, cfile, translations):
# Emit container core header
template = '\n'
if not self.static and self.service != 'CTL':
template += (
'\n'
'/**\n'
' * ${underscore}_ref:\n'
' * @self: a #${camelcase}.\n'
' *\n'
' * Atomically increments the reference count of @self by one.\n'
' *\n'
' * Returns: the new reference to @self.\n'
' *\n'
' * Since: ${since}\n'
' */\n')
template += (
'${static}${camelcase} *${underscore}_ref (${camelcase} *self);\n'
'\n')
if not self.static and self.service != 'CTL':
template += (
'/**\n'
' * ${underscore}_unref:\n'
' * @self: a #${camelcase}.\n'
' *\n'
' * Atomically decrements the reference count of @self by one.\n'
' * If the reference count drops to 0, @self is completely disposed.\n'
' *\n'
' * Since: ${since}\n'
' */\n')
template += (
'${static}void ${underscore}_unref (${camelcase} *self);\n'
'G_DEFINE_AUTOPTR_CLEANUP_FUNC (${camelcase}, ${underscore}_unref)\n')
if not self.readonly:
if not self.static:
template += (
'\n'
'/**\n'
' * ${underscore}_new:\n'
' *\n'
' * Allocates a new #${camelcase}.\n'
' *\n'
' * Returns: the newly created #${camelcase}. The returned value should be freed with ${underscore}_unref().\n'
' *\n'
' * Since: ${since}\n'
' */\n')
template += (
'${static}${camelcase} *${underscore}_new (void);\n')
if self.static:
cfile.write(string.Template(template).substitute(translations))
else:
hfile.write(string.Template(template).substitute(translations))
# Emit container core source
template = (
'\n'
'${static}GType\n'
'${underscore}_get_type (void)\n'
'{\n'
' static gsize g_define_type_id_initialized = 0;\n'
'\n'
' if (g_once_init_enter (&g_define_type_id_initialized)) {\n'
' GType g_define_type_id =\n'
' g_boxed_type_register_static (g_intern_static_string ("${camelcase}"),\n'
' (GBoxedCopyFunc) ${underscore}_ref,\n'
' (GBoxedFreeFunc) ${underscore}_unref);\n'
'\n'
' g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);\n'
' }\n'
'\n'
' return g_define_type_id_initialized;\n'
'}\n'
'\n'
'${static}${camelcase} *\n'
'${underscore}_ref (${camelcase} *self)\n'
'{\n'
' g_return_val_if_fail (self != NULL, NULL);\n'
'\n'
' g_atomic_int_inc (&self->ref_count);\n'
' return self;\n'
'}\n'
'\n'
'${static}void\n'
'${underscore}_unref (${camelcase} *self)\n'
'{\n'
' g_return_if_fail (self != NULL);\n'
'\n'
' if (g_atomic_int_dec_and_test (&self->ref_count)) {\n')
if self.compat:
template += (
' if (self->compat_context && self->compat_context_free)\n'
' self->compat_context_free (self->compat_context);\n')
if self.fields is not None:
for field in self.fields:
if field.variable is not None and field.variable.needs_dispose:
template += field.variable.build_dispose(' ', 'self->' + field.variable_name)
if field.variable.needs_compat_gir and self.service != 'CTL':
template += field.variable.build_dispose_gir(' ', 'self->' + field.variable_name)
template += (
' g_slice_free (${camelcase}, self);\n'
' }\n'
'}\n')
if self.compat:
template += (
'gpointer\n'
'${underscore}_get_compat_context (${camelcase} *self)\n'
'{\n'
' g_return_val_if_fail (self != NULL, NULL);\n'
'\n'
' return self->compat_context;\n'
'}\n'
'\n'
'void\n'
'${underscore}_set_compat_context (\n'
' ${camelcase} *self,\n'
' gpointer compat_context,\n'
' GDestroyNotify compat_context_free)\n'
'{\n'
' g_return_if_fail (self != NULL);\n'
'\n'
' if (self->compat_context && self->compat_context_free)\n'
' self->compat_context_free (self->compat_context);\n'
'\n'
' self->compat_context = compat_context;\n'
' self->compat_context_free = compat_context_free;\n'
'}\n')
cfile.write(string.Template(template).substitute(translations))
# _new() is only generated if the container is not readonly
if self.readonly:
return
template = (
'\n'
'${static}${camelcase} *\n'
'${underscore}_new (void)\n'
'{\n'
' ${camelcase} *self;\n'
'\n'
' self = g_slice_new0 (${camelcase});\n'
' self->ref_count = 1;\n'
' return self;\n'
'}\n')
cfile.write(string.Template(template).substitute(translations))
"""
Emit container implementation
"""
def emit(self, hfile, cfile):
translations = { 'name' : self.name,
'camelcase' : utils.build_camelcase_name (self.fullname),
'underscore' : utils.build_underscore_name (self.fullname),
'since' : self.since,
'static' : 'static ' if self.static else '' }
auxfile = cfile if self.static else hfile
if self.fields is None:
template = ('\n'
'/* Note: no fields in the ${name} container */\n')
auxfile.write(string.Template(template).substitute(translations))
cfile.write(string.Template(template).substitute(translations))
return
# Emit the container and field types
# Emit field getter/setter
if self.fields is not None:
for field in self.fields:
field.emit_types(auxfile, cfile)
self.__emit_types(auxfile, cfile, translations)
# Emit TLV enums
self.__emit_tlv_ids_enum(cfile)
# Emit fields
if self.fields is not None:
for field in self.fields:
field.emit_getter(auxfile, cfile)
if not self.readonly:
field.emit_setter(auxfile, cfile)
# Emit the container core
self.__emit_core(auxfile, cfile, translations)
"""
Add sections
"""
def add_sections(self, sections):
if self.fields is None:
return
translations = { 'name' : self.name,
'camelcase' : utils.build_camelcase_name (self.fullname),
'underscore' : utils.build_underscore_name (self.fullname),
'type_macro' : 'QMI_TYPE_' + utils.remove_prefix(utils.build_underscore_uppercase_name(self.fullname), 'QMI_') }
# Standard
template = (
'${underscore}_get_type\n'
'${type_macro}\n')
sections['standard'] += string.Template(template).substitute(translations)
# Private
if self.compat:
template = (
'${underscore}_get_compat_context\n'
'${underscore}_set_compat_context\n')
sections['private'] += string.Template(template).substitute(translations)
# Public types
template = (
'${camelcase}\n')
sections['public-types'] += string.Template(template).substitute(translations)
# Public methods
template = '<SUBSECTION ${camelcase}Methods>\n'
if not self.readonly:
template += (
'${underscore}_new\n')
template += (
'${underscore}_ref\n'
'${underscore}_unref\n')
sections['public-methods'] += string.Template(template).substitute(translations)
for field in self.fields:
field.add_sections(sections)