Partial merge from 'master' about wrapper and documentation generation scripts

This commit is contained in:
François Grisez 2017-11-16 17:21:04 +01:00
parent a97c048420
commit 5cf9e549f3
24 changed files with 2379 additions and 769 deletions

View file

@ -171,7 +171,7 @@ if(ENABLE_LIME)
endif()
set(HAVE_LIME 1)
endif()
if(ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRAPPER)
if(ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRAPPER OR ENABLE_SPHINX_DOC)
find_package(PythonInterp REQUIRED)
endif()

View file

@ -21,22 +21,23 @@
############################################################################
add_subdirectory(doxygen)
add_subdirectory(sphinx)
if(ENABLE_JAVADOC)
find_package(Java REQUIRED)
set(JAVADOC_PACKAGES "org.linphone.core org.linphone.mediastream")
set(JAVADOC_CLASSPATHS
"${PROJECT_SOURCE_DIR}/java/common"
"${PROJECT_SOURCE_DIR}/java/j2se"
"${PROJECT_SOURCE_DIR}/mediastreamer2/java/src"
)
string(REPLACE ";" ":" JAVADOC_CLASSPATHS "${JAVADOC_CLASSPATHS}")
set(JAVADOC_TITLE "Linphone SDK ${PROJECT_VERSION} reference documentation")
set(JAVADOC_JAVA_REFERENCE "http://docs.oracle.com/javase/8/docs/api/")
set(JAVADOC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc/java")
set(JAVADOC_LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/javadoc.log")
configure_file("generate_javadoc.sh.in" "generate_javadoc.sh" @ONLY)
add_custom_target(javadoc ALL
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/generate_javadoc.sh"
)
find_package(Java REQUIRED)
set(JAVADOC_PACKAGES "org.linphone.core org.linphone.mediastream")
set(JAVADOC_CLASSPATHS
"${PROJECT_SOURCE_DIR}/java/common"
"${PROJECT_SOURCE_DIR}/java/j2se"
"${PROJECT_SOURCE_DIR}/mediastreamer2/java/src"
)
string(REPLACE ";" ":" JAVADOC_CLASSPATHS "${JAVADOC_CLASSPATHS}")
set(JAVADOC_TITLE "Linphone SDK ${PROJECT_VERSION} reference documentation")
set(JAVADOC_JAVA_REFERENCE "http://docs.oracle.com/javase/8/docs/api/")
set(JAVADOC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc/java")
set(JAVADOC_LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/javadoc.log")
configure_file("generate_javadoc.sh.in" "generate_javadoc.sh" @ONLY)
add_custom_target(javadoc ALL
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/generate_javadoc.sh"
)
endif()

View file

@ -22,12 +22,9 @@
if (ENABLE_DOC OR ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRAPPER)
find_package(Doxygen)
if (DOXYGEN_FOUND)
if (DOXYGEN_DOT_FOUND)
set(DOC_INPUT_FILES ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
${CMAKE_CURRENT_SOURCE_DIR}/doxygen.dox
${LINPHONE_HEADER_FILES}
)
if(DOXYGEN_FOUND)
if(DOXYGEN_DOT_FOUND)
set(top_srcdir "${PROJECT_SOURCE_DIR}")
set(DOXYGEN_INPUT "")
foreach (HEADER_FILE ${LINPHONE_HEADER_FILES})
string(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} " \"${HEADER_FILE}\"")
@ -35,21 +32,28 @@ if (ENABLE_DOC OR ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRA
string(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} " \"${CMAKE_CURRENT_SOURCE_DIR}\"")
string(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} " \"${PROJECT_SOURCE_DIR}/coreapi/help/examples/C\"")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" "${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml"
set(DOC_INPUT_FILES ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
${CMAKE_CURRENT_SOURCE_DIR}/doxygen.dox
${LINPHONE_HEADER_FILES}
)
set(XML_DIR "${CMAKE_CURRENT_BINARY_DIR}/xml")
set(LINPHONE_DOXYGEN_XML_DIR ${XML_DIR} PARENT_SCOPE)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" "${XML_DIR}/index.xml"
COMMAND ${CMAKE_COMMAND} -E remove -f html/* xml/*
COMMAND ${CMAKE_COMMAND} -E remove -f html/* xml/*
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
DEPENDS ${DOC_INPUT_FILES}
)
add_custom_target(linphone-doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" "${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml")
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html" "${CMAKE_CURRENT_BINARY_DIR}/xml"
DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/linphone-${LINPHONE_VERSION}")
else ()
add_custom_target(linphone-doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" "${XML_DIR}/index.xml")
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html" "${XML_DIR}"
DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/linphone-${LINPHONE_VERSION}")
else()
if (ENABLE_CXX_WRAPPER)
message(FATAL_ERROR "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
else ()
else()
message(WARNING "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
endif ()
endif ()
endif ()
endif ()
endif()
endif()
endif()
endif()

View file

@ -34,6 +34,11 @@
* @brief Initializing liblinphone.
**/
/**
* @defgroup logging Logging
* @brief Logging service of Linphone.
*/
/**
* @defgroup call_control Call control
* @brief Placing and receiving calls.

View file

@ -0,0 +1,61 @@
############################################################################
# CMakeLists.txt
# Copyright (C) 2017 Belledonne Communications, Grenoble France
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU 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.
#
############################################################################
if (ENABLE_SPHINX_DOC)
set(GENERATED_LANGUAGES c cpp csharp)
set(DOCUMENTATION_DIRS )
set(GENERATED_SPHINX_SOURCES )
foreach(LANGUAGE_ ${GENERATED_LANGUAGES})
set(DOCUMENTATION_DIR ${CMAKE_CURRENT_BINARY_DIR}/source/${LANGUAGE_})
list(APPEND DOCUMENTATION_DIRS ${DOCUMENTATION_DIR})
list(APPEND GENERATED_SPHINX_SOURCES ${DOCUMENTATION_DIR}/index.rst)
endforeach(LANGUAGE_)
set(PYTHON_SCRIPTS gendoc.py
${linphone_SOURCE_DIR}/tools/abstractapi.py
${linphone_SOURCE_DIR}/tools/genapixml.py
${linphone_SOURCE_DIR}/tools/metadoc.py
${linphone_SOURCE_DIR}/tools/metaname.py
)
set(MUSTACHE_TEMPLATES class_page.mustache
enums_page.mustache
index_page.mustache
)
configure_file(conf.py.in source/conf.py)
configure_file(source/index.rst source/index.rst COPYONLY)
add_custom_command(OUTPUT ${GENERATED_SPHINX_SOURCES}
COMMAND ${CMAKE_COMMAND} -E remove -f ${DOCUMENTATION_DIRS}
COMMAND ${PYTHON_EXECUTABLE} '${CMAKE_CURRENT_SOURCE_DIR}/gendoc.py' '${LINPHONE_DOXYGEN_XML_DIR}' -o 'source'
DEPENDS ${PYTHON_SCRIPTS}
${MUSTACHE_TEMPLATES}
${LINPHONE_DOXYGEN_XML_DIR}/index.xml
linphone-doc
)
add_custom_command(OUTPUT build/html/index.html
COMMAND ${CMAKE_COMMAND} -E remove_directory build
COMMAND ${PYTHON_EXECUTABLE} -msphinx -M html 'source' 'build'
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/source/conf.py
${CMAKE_CURRENT_BINARY_DIR}/source/index.rst
${GENERATED_SPHINX_SOURCES}
)
add_custom_target(sphinx-doc ALL DEPENDS build/html/index.html)
endif()

View file

@ -0,0 +1,151 @@
{{#make_chapter}}{{{className}}} class{{/make_chapter}}
.. {{#write_declarator}}class{{/write_declarator}}:: {{{fullClassName}}}
{{#briefDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDoc}}
{{#detailedDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/detailedDoc}}
{{{selector}}}
Summary
=======
{{#hasProperties}}
Properties
----------
{{{propertiesSummary}}}
{{/hasProperties}}
{{#hasMethods}}
Methods
-------
{{{instanceMethodsSummary}}}
{{/hasMethods}}
{{#hasClassMethods}}
Class methods
-------------
{{{classMethodsSummary}}}
{{/hasClassMethods}}
Detailed descriptions
=====================
{{#hasProperties}}
Properties
----------
{{#properties}}
{{{title}}}
{{#hasNamespaceDeclarator}}
.. {{#write_declarator}}namespace{{/write_declarator}}:: {{{fullClassName}}}
{{/hasNamespaceDeclarator}}
{{#getter}}
.. {{#write_declarator}}method{{/write_declarator}}:: {{{prototype}}}
{{#briefDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDoc}}
{{#detailedDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/detailedDoc}}
{{{selector}}}
{{/getter}}
{{#setter}}
.. {{#write_declarator}}method{{/write_declarator}}:: {{{prototype}}}
{{#briefDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDoc}}
{{#detailedDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/detailedDoc}}
{{{selector}}}
{{/setter}}
{{/properties}}
{{/hasProperties}}
{{#hasMethods}}
Public methods
--------------
{{#hasNamespaceDeclarator}}
.. {{#write_declarator}}namespace{{/write_declarator}}:: {{{fullClassName}}}
{{/hasNamespaceDeclarator}}
{{#methods}}
.. {{#write_declarator}}method{{/write_declarator}}:: {{{prototype}}}
{{#briefDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDoc}}
{{#detailedDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/detailedDoc}}
{{{selector}}}
{{/methods}}
{{/hasMethods}}
{{#hasClassMethods}}
Class methods
-------------
{{#hasNamespaceDeclarator}}
.. {{#write_declarator}}namespace{{/write_declarator}}:: {{{fullClassName}}}
{{/hasNamespaceDeclarator}}
{{#classMethods}}
.. {{#write_declarator}}method{{/write_declarator}}:: static {{{prototype}}}
{{#briefDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDoc}}
{{#detailedDoc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/detailedDoc}}
{{{selector}}}
{{/classMethods}}
{{/hasClassMethods}}

View file

@ -0,0 +1,158 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Linphone API documentation build configuration file, created by
# sphinx-quickstart on Mon Jun 19 11:58:21 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx_csharp.csharp']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Linphone Core API'
copyright = '2017, Belledonne Communications SARL'
author = 'Belledonne Communications SARL'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}'
# The full version, including alpha/beta/rc tags.
release = '${LINPHONE_VERSION}'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
#html_theme = 'alabaster'
html_theme = 'classic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'LinphoneAPIdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'LinphoneAPI.tex', 'Linphone API Documentation',
'Belledonne Communications SARL', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'linphoneapi', 'Linphone Core API Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'LinphoneCoreAPI', 'Linphone Core API Documentation',
author, 'LinphoneCoreAPI', 'One line description of project.',
'Miscellaneous'),
]

View file

@ -0,0 +1,30 @@
{{#enums}}
{{{sectionName}}}
.. {{#write_declarator}}enum{{/write_declarator}}:: {{{fullName}}}
{{#briefDesc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDesc}}
{{{selector}}}
{{#hasNamespaceDeclarator}}
.. {{#write_declarator}}namespace{{/write_declarator}}:: {{{namespace}}}
{{/hasNamespaceDeclarator}}
{{#enumerators}}
.. {{#write_declarator}}enumerator{{/write_declarator}}:: {{{name}}}{{#value}} = {{{value}}}{{/value}}
{{#briefDesc}}
{{#lines}}
{{{line}}}
{{/lines}}
{{/briefDesc}}
{{{selector}}}
{{/enumerators}}
{{/enums}}

377
coreapi/help/doc/sphinx/gendoc.py Executable file
View file

@ -0,0 +1,377 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2017 Belledonne Communications SARL
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU 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.
import sys
import os
import argparse
import pystache
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'tools'))
import abstractapi
import genapixml as capi
import metaname
import metadoc
class RstTools:
@staticmethod
def make_chapter(text):
return RstTools.make_section(text, char='*', overline=True)
@staticmethod
def make_section(text, char='=', overline=False):
size = len(text)
underline = (char*size)
lines = [text, underline]
if overline:
lines.insert(0, underline)
return '\n'.join(lines)
@staticmethod
def make_subsection(text):
return RstTools.make_section(text, char='-')
@staticmethod
def make_subsubsection(text):
return RstTools.make_section(text, char='^')
class Table:
def __init__(self):
self._rows = []
self._widths = []
self._heights = []
@property
def rows(self):
return self._rows
def addrow(self, row):
if len(self._widths) == 0:
self._widths.append(0)
self._widths *= len(row)
elif len(row) != len(self._widths):
raise ValueError('row width mismatch table width')
height = 0
row2 = []
i = 0
while i<len(row):
lines = str(row[i]).split(sep='\n')
row2.append(lines)
width = len(max(lines, key=len))
self._widths[i] = max(self._widths[i], width)
height = max(height, len(lines))
i += 1
self._rows.append(row2)
self._heights.append(height)
def _make_hline(self):
res = '+'
for width in self._widths:
res += ('-' * (width+2))
res += '+'
res += '\n'
return res
def _make_row(self, idx):
res = ''
row = self._rows[idx]
j = 0
while j < self._heights[idx]:
res += '|'
i = 0
while i < len(row):
line = row[i][j] if j < len(row[i]) else ''
res += ' {0} '.format(line)
res += (' ' * (self._widths[i]-len(line)))
res += '|'
i += 1
res += '\n'
j += 1
return res
def __str__(self):
if len(self._rows) == 0 or len(self._widths) == 0:
return ''
else:
res = self._make_hline()
i = 0
while i<len(self._rows):
res += self._make_row(i)
res += self._make_hline()
i += 1
return res
class LangInfo:
def __init__(self, langCode):
self.langCode = langCode
self.displayName = LangInfo._lang_code_to_display_name(langCode)
self.nameTranslator = metaname.Translator.get(langCode)
self.langTranslator = abstractapi.Translator.get(langCode)
self.docTranslator = metadoc.SphinxTranslator(langCode)
@staticmethod
def _lang_code_to_display_name(langCode):
if langCode == 'C':
return 'C'
elif langCode == 'Cpp':
return 'C++'
elif langCode == 'CSharp':
return 'C#'
else:
raise ValueError("Invalid language code: '{0}'".format(langCode))
class SphinxPage(object):
def __init__(self, lang, langs, filename):
object.__init__(self)
self.lang = lang
self.langs = langs
self.filename = filename
@property
def hasNamespaceDeclarator(self):
return ('namespaceDeclarator' in dir(self.docTranslator))
@property
def language(self):
return self.lang.displayName
@property
def docTranslator(self):
return self.lang.docTranslator
def make_chapter(self):
return lambda text: RstTools.make_chapter(pystache.render(text, self))
def make_section(self):
return lambda text: RstTools.make_section(pystache.render(text, self))
def make_subsection(self):
return lambda text: RstTools.make_subsection(pystache.render(text, self.properties))
def write_declarator(self):
return lambda text: self.docTranslator.get_declarator(text)
def write(self, directory):
r = pystache.Renderer()
filepath = os.path.join(directory, self.filename)
with open(filepath, mode='w') as f:
f.write(r.render(self))
def _get_translated_namespace(self, obj):
namespace = obj.find_first_ancestor_by_type(abstractapi.Namespace)
return namespace.name.translate(self.lang.nameTranslator, recursive=True)
def _make_selector(self, obj):
links = []
ref = metadoc.Reference(None)
ref.relatedObject = obj
for lang in self.langs:
if lang is self.lang:
link = lang.displayName
else:
link = ref.translate(lang.docTranslator, label=lang.displayName)
links.append(link)
return ' '.join(links)
@staticmethod
def _classname_to_filename(classname):
return classname.to_snake_case(fullName=True) + '.rst'
class IndexPage(SphinxPage):
def __init__(self, lang, langs):
SphinxPage.__init__(self, lang, langs, 'index.rst')
self.tocEntries = []
def add_class_entry(self, _class):
self.tocEntries.append({'entryName': SphinxPage._classname_to_filename(_class.name)})
class EnumsPage(SphinxPage):
def __init__(self, lang, langs, enums):
SphinxPage.__init__(self, lang, langs, 'enums.rst')
self._translate_enums(enums)
def _translate_enums(self, enums):
self.enums = []
for enum in enums:
translatedEnum = {
'name' : enum.name.translate(self.lang.nameTranslator),
'fullName' : enum.name.translate(self.lang.nameTranslator, recursive=True),
'briefDesc' : enum.briefDescription.translate(self.docTranslator),
'enumerators' : self._translate_enum_values(enum),
'selector' : self._make_selector(enum)
}
translatedEnum['namespace'] = self._get_translated_namespace(enum) if self.lang.langCode == 'Cpp' else translatedEnum['fullName']
translatedEnum['sectionName'] = RstTools.make_section(translatedEnum['name'])
self.enums.append(translatedEnum)
def _translate_enum_values(self, enum):
translatedEnumerators = []
for enumerator in enum.enumerators:
translatedValue = {
'name' : enumerator.name.translate(self.lang.nameTranslator),
'briefDesc' : enumerator.briefDescription.translate(self.docTranslator),
'value' : enumerator.translate_value(self.lang.langTranslator),
'selector' : self._make_selector(enumerator)
}
translatedEnumerators.append(translatedValue)
return translatedEnumerators
class ClassPage(SphinxPage):
def __init__(self, _class, lang, langs):
filename = SphinxPage._classname_to_filename(_class.name)
SphinxPage.__init__(self, lang, langs, filename)
self.namespace = self._get_translated_namespace(_class)
self.className = _class.name.translate(self.lang.nameTranslator)
self.fullClassName = _class.name.translate(self.lang.nameTranslator, recursive=True)
self.briefDoc = _class.briefDescription.translate(self.docTranslator)
self.detailedDoc = _class.detailedDescription.translate(self.docTranslator) if _class.detailedDescription is not None else None
self.properties = self._translate_properties(_class.properties)
self.methods = self._translate_methods(_class.instanceMethods)
self.classMethods = self._translate_methods(_class.classMethods)
self.selector = self._make_selector(_class)
@property
def hasMethods(self):
return len(self.methods) > 0
@property
def hasClassMethods(self):
return len(self.classMethods) > 0
@property
def hasProperties(self):
return len(self.properties) > 0
def _translate_properties(self, properties):
translatedProperties = []
for property_ in properties:
propertyAttr = {
'name' : property_.name.translate(self.lang.nameTranslator),
'ref_label' : '{0}_{1}'.format(self.lang.langCode, property_.name.to_snake_case(fullName=True)),
'getter' : self._translate_method(property_.getter) if property_.getter is not None else None,
'setter' : self._translate_method(property_.setter) if property_.setter is not None else None
}
propertyAttr['title'] = RstTools.make_subsubsection(propertyAttr['name'])
translatedProperties.append(propertyAttr)
return translatedProperties
def _translate_methods(self, methods):
translatedMethods = []
for method in methods:
translatedMethods.append(self._translate_method(method))
return translatedMethods
def _translate_method(self, method):
prototypeParams = {}
if self.lang.langCode == 'Cpp':
prototypeParams['showStdNs'] = True
methAttr = {
'prototype' : method.translate_as_prototype(self.lang.langTranslator, **prototypeParams),
'briefDoc' : method.briefDescription.translate(self.docTranslator),
'detailedDoc' : method.detailedDescription.translate(self.docTranslator),
'selector' : self._make_selector(method)
}
reference = metadoc.FunctionReference(None)
reference.relatedObject = method
methAttr['link'] = reference.translate(self.lang.docTranslator)
return methAttr
@property
def propertiesSummary(self):
table = RstTools.Table()
for property_ in self.properties:
reference = ':ref:`{0}`'.format(property_['ref_label'])
briefDoc = property_['getter']['briefDoc'] if property_['getter'] is not None else property_['setter']['briefDoc']
briefDoc = '\n'.join([line['line'] for line in briefDoc['lines']])
table.addrow([reference, briefDoc])
return table
@property
def instanceMethodsSummary(self):
table = RstTools.Table()
for method in self.methods:
briefDoc = '\n'.join([line['line'] for line in method['briefDoc']['lines']])
table.addrow([method['link'], briefDoc])
return table
@property
def classMethodsSummary(self):
table = RstTools.Table()
for method in self.classMethods:
briefDoc = '\n'.join([line['line'] for line in method['briefDoc']['lines']])
table.addrow([method['link'], briefDoc])
return table
class DocGenerator:
def __init__(self, api):
self.api = api
self.languages = [
LangInfo('C'),
LangInfo('Cpp'),
LangInfo('CSharp')
]
def generate(self, outputdir):
for lang in self.languages:
subdirectory = lang.langCode.lower()
directory = os.path.join(args.outputdir, subdirectory)
if not os.path.exists(directory):
os.mkdir(directory)
enumsPage = EnumsPage(lang, self.languages, absApiParser.enums)
enumsPage.write(directory)
indexPage = IndexPage(lang, self.languages)
for _class in absApiParser.classes:
page = ClassPage(_class, lang, self.languages)
page.write(directory)
indexPage.add_class_entry(_class)
indexPage.write(directory)
if __name__ == '__main__':
argparser = argparse.ArgumentParser(description='Generate a sphinx project to generate the documentation of Linphone Core API.')
argparser.add_argument('xmldir', type=str, help='directory holding the XML documentation of the C API generated by Doxygen')
argparser.add_argument('-o --output', type=str, help='directory into where Sphinx source files will be written', dest='outputdir', default='.')
args = argparser.parse_args()
cProject = capi.Project()
cProject.initFromDir(args.xmldir)
cProject.check()
absApiParser = abstractapi.CParser(cProject)
absApiParser.parse_all()
docGenerator = DocGenerator(absApiParser)
docGenerator.generate(args.outputdir)

View file

@ -0,0 +1,19 @@
{{#make_section}}{{{language}}} API{{/make_section}}
Index of classes
----------------
.. toctree::
:maxdepth: 1
{{#tocEntries}}
{{{entryName}}}
{{/tocEntries}}
Index of enums
--------------
.. toctree::
:maxdepth 1
enums.rst

View file

@ -0,0 +1,24 @@
.. Linphone API documentation master file, created by
sphinx-quickstart on Mon Jun 19 11:58:21 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Linphone API's documentation!
========================================
.. toctree::
:maxdepth: 1
:caption: Contents:
c/index.rst
cpp/index.rst
csharp/index.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -1055,6 +1055,10 @@ typedef enum _LinphoneTransportType {
*/
typedef struct _LinphoneTunnel LinphoneTunnel;
/**
* @brief Tunnel settings.
* @ingroup tunnel
*/
typedef struct _LinphoneTunnelConfig LinphoneTunnelConfig;
/**

View file

@ -1,3 +1,4 @@
# Copyright (C) 2017 Belledonne Communications SARL
#
# This program is free software; you can redistribute it and/or
@ -17,6 +18,7 @@
import re
import genapixml as CApi
import metaname
class Error(RuntimeError):
@ -27,170 +29,24 @@ class BlacklistedException(Error):
pass
class Name(object):
camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*')
lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)')
def __init__(self):
self.words = []
self.prev = None
def copy(self):
nameType = type(self)
name = nameType()
name.words = list(self.words)
name.prev = None if self.prev is None else self.prev.copy()
return name
def delete_prefix(self, prefix):
it = self
_next = None
while it is not None and it.words != prefix.words:
_next = it
it = it.prev
if it is None or it != prefix:
raise Error('no common prefix')
elif _next is not None:
_next.prev = None
def _set_namespace(self, namespace):
self.prev = namespace
if self.prev is not None:
prefix = namespace.to_word_list()
i = 0
while i<len(self.words) and i<len(prefix) and self.words[i] == prefix[i]:
i += 1
if i == len(self.words):
raise Error('name equal to namespace \'{0}\'', self.words)
else:
self.words = self.words[i:]
def _lower_all_words(self):
i = 0
while i<len(self.words):
self.words[i] = self.words[i].lower()
i += 1
def from_snake_case(self, name, namespace=None):
self.words = name.split('_')
Name._set_namespace(self, namespace)
def from_camel_case(self, name, islowercased=False, namespace=None):
if not islowercased:
self.words = Name.camelCaseParsingRegex.findall(name)
else:
match = Name.lowerCamelCaseSplitingRegex.match(name)
self.words = [match.group(1)]
self.words += Name.camelCaseParsingRegex.findall(match.group(2))
Name._lower_all_words(self)
Name._set_namespace(self, namespace)
def to_snake_case(self, fullName=False, upper=False):
if self.prev is None or not fullName:
res = '_'.join(self.words)
if upper:
res = res.upper()
return res
else:
return Name.to_snake_case(self.prev, fullName=True, upper=upper) + '_' + Name.to_snake_case(self, upper=upper)
def to_camel_case(self, lower=False, fullName=False):
if self.prev is None or not fullName:
res = ''
for elem in self.words:
if elem is self.words[0] and lower:
res += elem
else:
res += elem.title()
return res
else:
return Name.to_camel_case(self.prev, fullName=True, lower=lower) + Name.to_camel_case(self)
def concatenate(self, upper=False, fullName=False):
if self.prev is None or not fullName:
res = ''
for elem in self.words:
if upper:
res += elem.upper()
else:
res += elem
return res
else:
return Name.concatenate(self.prev, upper=upper, fullName=True) + Name.concatenate(self, upper=upper)
def to_word_list(self):
if self.prev is None:
return list(self.words)
else:
return Name.to_word_list(self.prev) + self.words
@staticmethod
def find_common_parent(name1, name2):
if name1.prev is None or name2.prev is None:
return None
elif name1.prev is name2.prev:
return name1.prev
else:
commonParent = Name.find_common_parent(name1.prev, name2)
if commonParent is not None:
return commonParent
else:
return Name.find_common_parent(name1, name2.prev)
class ClassName(Name):
def to_c(self):
return Name.to_camel_case(self, fullName=True)
class InterfaceName(ClassName):
def to_c(self):
return ClassName.to_c(self)[:-8] + 'Cbs'
class EnumName(ClassName):
class Constant:
pass
class EnumValueName(ClassName):
pass
class Nil(Constant):
def translate(self, langTranslator):
return langTranslator.nilToken
class MethodName(Name):
regex = re.compile('^\d+$')
class Boolean(Constant):
def __init__(self, value=False):
self.value = value
def __init__(self):
self.overloadRef = 0
def __bool__(self):
return self.value
def from_snake_case(self, name, namespace=None):
Name.from_snake_case(self, name, namespace=namespace)
if len(self.words) > 0:
suffix = self.words[-1]
if MethodName.regex.match(suffix) is not None:
self.overloadRef = int(suffix)
del self.words[-1]
def to_c(self):
suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else ''
return self.to_snake_case(fullName=True) + suffix
class ArgName(Name):
def to_c(self):
return self.to_snake_case()
class PropertyName(ArgName):
pass
class NamespaceName(Name):
def __init__(self, *params):
Name.__init__(self)
if len(params) > 0:
self.words = params[0]
def translate(self, langTranslator):
return langTranslator.trueConstantToken if self else langTranslator.falseConstantToken
class Object(object):
@ -199,9 +55,12 @@ class Object(object):
self.parent = None
self.deprecated = False
def find_first_ancestor_by_type(self, _type):
def __lt__(self, other):
return self.name < other.name
def find_first_ancestor_by_type(self, *types):
ancestor = self.parent
while ancestor is not None and type(ancestor) is not _type:
while ancestor is not None and type(ancestor) not in types:
ancestor = ancestor.parent
return ancestor
@ -211,7 +70,7 @@ class Type(Object):
Object.__init__(self, name)
self.isconst = isconst
self.isref = isref
self.cname = None
self.cDecl = None
class BaseType(Type):
@ -219,18 +78,27 @@ class BaseType(Type):
Type.__init__(self, name, isconst=isconst, isref=isref)
self.size = size
self.isUnsigned = isUnsigned
def translate(self, translator, **params):
return translator.translate_base_type(self, **params)
class EnumType(Type):
def __init__(self, name, isconst=False, isref=False, enumDesc=None):
Type.__init__(self, name, isconst=isconst, isref=isref)
self.desc = enumDesc
def translate(self, translator, **params):
return translator.translate_enum_type(self, **params)
class ClassType(Type):
def __init__(self, name, isconst=False, isref=False, classDesc=None):
Type.__init__(self, name, isconst=isconst, isref=isref)
self.desc = classDesc
def translate(self, translator, **params):
return translator.translate_class_type(self, **params)
class ListType(Type):
@ -239,23 +107,43 @@ class ListType(Type):
self.containedTypeName = containedTypeName
self._containedTypeDesc = None
def set_contained_type_desc(self, desc):
def _set_contained_type_desc(self, desc):
self._containedTypeDesc = desc
desc.parent = self
def get_contained_type_desc(self):
def _get_contained_type_desc(self):
return self._containedTypeDesc
containedTypeDesc = property(fset=set_contained_type_desc, fget=get_contained_type_desc)
containedTypeDesc = property(fset=_set_contained_type_desc, fget=_get_contained_type_desc)
def translate(self, translator, **params):
return translator.translate_list_type(self, **params)
class DocumentableObject(Object):
def __init__(self, name):
Object.__init__(self, name)
self.briefDescription = None
self.detailedDescription = None
self._briefDescription = None
self._detailedDescription = None
self.deprecated = None
def _get_brief_description(self):
return self._briefDescription
def _set_brief_description(self, description):
self._briefDescription = description
description.relatedObject = self
def _get_detailed_description(self):
return self._detailedDescription
def _set_detailed_description(self, description):
self._detailedDescription = description
description.relatedObject = self
briefDescription = property(fget=_get_brief_description, fset=_set_brief_description)
detailedDescription = property(fget=_get_detailed_description, fset=_set_detailed_description)
def set_from_c(self, cObject, namespace=None):
self.briefDescription = cObject.briefDescription
self.detailedDescription = cObject.detailedDescription
@ -281,32 +169,38 @@ class Namespace(DocumentableObject):
child.parent = self
GlobalNs = Namespace('')
class Flag:
def __init__(self, position):
self.position = position
class EnumValue(DocumentableObject):
class Enumerator(DocumentableObject):
def __init__(self, name):
DocumentableObject.__init__(self, name)
self.value = None
def value_from_string(self, stringValue):
m = re.match('^1\s*<<\s*([0-9]+)$', stringValue)
m = re.match('^\s*1\s*<<\s*([0-9]+)$', stringValue)
if m is not None:
self.value = Flag(int(m.group(1)))
else:
self.value = int(stringValue, base=0)
def translate_value(self, translator):
return translator.translate_enumerator_value(self.value)
class Enum(DocumentableObject):
def __init__(self, name):
DocumentableObject.__init__(self, name)
self.values = []
self.enumerators = []
def add_value(self, value):
self.values.append(value)
value.parent = self
def add_enumerator(self, enumerator):
self.enumerators.append(enumerator)
enumerator.parent = self
def set_from_c(self, cEnum, namespace=None):
Object.set_from_c(self, cEnum, namespace=namespace)
@ -316,14 +210,14 @@ class Enum(DocumentableObject):
else:
name = cEnum.name
self.name = EnumName()
self.name = metaname.EnumName()
self.name.prev = None if namespace is None else namespace.name
self.name.set_from_c(name)
for cEnumValue in cEnum.values:
aEnumValue = EnumValue()
aEnumValue = Enumerator()
aEnumValue.set_from_c(cEnumValue, namespace=self)
self.add_value(aEnumValue)
self.add_enumerator(aEnumValue)
class Argument(DocumentableObject):
@ -342,6 +236,9 @@ class Argument(DocumentableObject):
return self._type
type = property(fset=_set_type, fget=_get_type)
def translate(self, translator, **params):
return translator.translate_argument(self, **params)
class Method(DocumentableObject):
@ -352,10 +249,14 @@ class Method(DocumentableObject):
def __init__(self, name, type=Type.Instance):
DocumentableObject.__init__(self, name)
self.type = type
self.constMethod = False
self.isconst = False
self.args = []
self._returnType = None
def add_arguments(self, arg):
self.args.append(arg)
arg.parent = self
def _set_return_type(self, returnType):
self._returnType = returnType
returnType.parent = self
@ -363,11 +264,10 @@ class Method(DocumentableObject):
def _get_return_type(self):
return self._returnType
def add_arguments(self, arg):
self.args.append(arg)
arg.parent = self
returnType = property(fset=_set_return_type, fget=_get_return_type)
def translate_as_prototype(self, translator, **params):
return translator.translate_method_as_prototype(self, **params)
class Property(DocumentableObject):
@ -427,6 +327,11 @@ class Class(DocumentableObject):
return self._listenerInterface
listenerInterface = property(fget=get_listener_interface, fset=set_listener_interface)
def sort(self):
self.properties.sort()
self.instanceMethods.sort()
self.classMethods.sort()
class Interface(DocumentableObject):
@ -443,6 +348,9 @@ class Interface(DocumentableObject):
return self._listenedClass
listenedClass = property(fget=get_listened_class)
def sort(self):
self.methods.sort()
class CParser(object):
@ -469,6 +377,7 @@ class CParser(object):
self.cProject = cProject
self.enumsIndex = {}
self.enums = []
for enum in self.cProject.enums:
if enum.associatedTypedef is None:
self.enumsIndex[enum.name] = None
@ -477,6 +386,8 @@ class CParser(object):
self.classesIndex = {}
self.interfacesIndex = {}
self.classes = []
self.interfaces = []
for _class in self.cProject.classes:
if _class.name not in self.classBl:
if _class.name.endswith('Cbs'):
@ -496,15 +407,15 @@ class CParser(object):
if _property.getter is not None:
self.methodsIndex[_property.getter.name] = None
name = NamespaceName()
name = metaname.NamespaceName()
name.from_snake_case('linphone')
self.namespace = Namespace(name)
def _is_blacklisted(self, name):
if type(name) is MethodName:
if type(name) is metaname.MethodName:
return name.to_camel_case(lower=True) in self.methodBl or name.to_c() in self.functionBl
elif type(name) is ClassName:
elif type(name) is metaname.ClassName:
return name.to_c() in self.classBl
else:
return False
@ -526,6 +437,7 @@ class CParser(object):
self._clean_all_indexes()
self._sortall()
self._fix_all_types()
self._fix_all_docs()
@ -542,6 +454,15 @@ class CParser(object):
for key in keysToRemove:
del index[key]
def _sortall(self):
self.enums.sort()
self.classes.sort()
self.interfaces.sort()
for class_ in self.classes:
class_.sort()
for interface in self.interfaces:
interface.sort()
def _class_is_refcountable(self, _class):
if _class.name in self.forcedRefcountableClasses:
return True
@ -609,11 +530,19 @@ class CParser(object):
def _fix_all_docs(self):
for _class in self.classesIndex.values():
if _class.briefDescription is not None:
_class.briefDescription.resolve_all_references(self)
self._fix_doc(_class)
for enum in self.enumsIndex.values():
self._fix_doc(enum)
for enumerator in enum.enumerators:
self._fix_doc(enumerator)
for method in self.methodsIndex.values():
if method.briefDescription is not None:
method.briefDescription.resolve_all_references(self)
self._fix_doc(method)
def _fix_doc(self, obj):
if obj.briefDescription is not None:
obj.briefDescription.resolve_all_references(self)
if obj.detailedDescription is not None:
obj.detailedDescription.resolve_all_references(self)
def parse_enum(self, cenum):
if 'associatedTypedef' in dir(cenum):
@ -621,25 +550,28 @@ class CParser(object):
else:
nameStr = cenum.name
name = EnumName()
name = metaname.EnumName()
name.from_camel_case(nameStr, namespace=self.namespace.name)
enum = Enum(name)
enum.briefDescription = cenum.briefDoc
enum.detailedDescription = cenum.detailedDoc
self.namespace.add_child(enum)
for cEnumValue in cenum.values:
valueName = EnumValueName()
valueName = metaname.EnumeratorName()
valueName.from_camel_case(cEnumValue.name, namespace=name)
aEnumValue = EnumValue(valueName)
aEnumValue = Enumerator(valueName)
aEnumValue.briefDescription = cEnumValue.briefDoc
aEnumValue.detailedDescription = cEnumValue.detailedDoc
if cEnumValue.value is not None:
try:
aEnumValue.value_from_string(cEnumValue.value)
except ValueError:
raise Error('{0} enum value has an invalid definition ({1})'.format(cEnumValue.name, cEnumValue.value))
enum.add_value(aEnumValue)
enum.add_enumerator(aEnumValue)
self.enumsIndex[nameStr] = enum
self.enums.append(enum)
return enum
def parse_class(self, cclass):
@ -649,17 +581,20 @@ class CParser(object):
if cclass.name.endswith('Cbs'):
_class = self._parse_listener(cclass)
self.interfacesIndex[cclass.name] = _class
self.interfaces.append(_class)
else:
_class = self._parse_class(cclass)
self.classesIndex[cclass.name] = _class
self.classes.append(_class)
self.namespace.add_child(_class)
return _class
def _parse_class(self, cclass):
name = ClassName()
name = metaname.ClassName()
name.from_camel_case(cclass.name, namespace=self.namespace.name)
_class = Class(name)
_class.briefDescription = cclass.briefDoc
_class.detailedDescription = cclass.detailedDoc
for cproperty in cclass.properties.values():
try:
@ -700,7 +635,7 @@ class CParser(object):
return _class
def _parse_property(self, cproperty, namespace=None):
name = PropertyName()
name = metaname.PropertyName()
name.from_snake_case(cproperty.name)
if (cproperty.setter is not None and len(cproperty.setter.arguments) == 1) or (cproperty.getter is not None and len(cproperty.getter.arguments) == 0):
methodType = Method.Type.Class
@ -717,7 +652,7 @@ class CParser(object):
def _parse_listener(self, cclass):
name = InterfaceName()
name = metaname.InterfaceName()
name.from_camel_case(cclass.name, namespace=self.namespace.name)
if name.words[len(name.words)-1] == 'cbs':
@ -727,6 +662,7 @@ class CParser(object):
listener = Interface(name)
listener.briefDescription = cclass.briefDoc
listener.detailedDescription = cclass.detailedDoc
for property in cclass.properties.values():
if property.name != 'user_data':
@ -739,7 +675,7 @@ class CParser(object):
return listener
def _parse_listener_property(self, property, listener, events):
methodName = MethodName()
methodName = metaname.MethodName()
methodName.from_snake_case(property.name)
methodName.words.insert(0, 'on')
methodName.prev = listener.name
@ -759,7 +695,7 @@ class CParser(object):
method = Method(methodName)
method.returnType = self.parse_type(event.returnArgument)
for arg in event.arguments:
argName = ArgName()
argName = metaname.ArgName()
argName.from_snake_case(arg.name)
argument = Argument(argName, self.parse_type(arg))
method.add_arguments(argument)
@ -767,7 +703,7 @@ class CParser(object):
return method
def parse_method(self, cfunction, namespace, type=Method.Type.Instance):
name = MethodName()
name = metaname.MethodName()
name.from_snake_case(cfunction.name, namespace=namespace)
if self._is_blacklisted(name):
@ -775,15 +711,16 @@ class CParser(object):
method = Method(name, type=type)
method.briefDescription = cfunction.briefDoc
method.detailedDescription = cfunction.detailedDoc
method.deprecated = cfunction.deprecated
method.returnType = self.parse_type(cfunction.returnArgument)
for arg in cfunction.arguments:
if type == Method.Type.Instance and arg is cfunction.arguments[0]:
method.constMethod = ('const' in arg.completeType.split(' '))
method.isconst = ('const' in arg.completeType.split(' '))
else:
aType = self.parse_type(arg)
argName = ArgName()
argName = metaname.ArgName()
argName.from_snake_case(arg.name)
absArg = Argument(argName, aType)
method.add_arguments(absArg)
@ -807,7 +744,7 @@ class CParser(object):
else:
raise Error('Unknown C type \'{0}\''.format(cType.ctype))
absType.cname = cType.completeType
absType.cDecl = cType.completeType
return absType
def parse_c_base_type(self, cDecl):
@ -871,3 +808,341 @@ class CParser(object):
return BaseType(name, **param)
else:
raise Error('could not find type in \'{0}\''.format(cDecl))
class Translator:
instances = {}
@staticmethod
def get(langCode):
try:
if langCode not in Translator.instances:
className = langCode + 'LangTranslator'
_class = globals()[className]
Translator.instances[langCode] = _class()
return Translator.instances[langCode]
except KeyError:
raise ValueError("Invalid language code: '{0}'".format(langCode))
class CLikeLangTranslator(Translator):
def translate_enumerator_value(self, value):
if value is None:
return None
elif isinstance(value, int):
return str(value)
elif isinstance(value, Flag):
return '1<<{0}'.format(value.position)
else:
raise TypeError('invalid enumerator value type: {0}'.format(value))
def translate_argument(self, argument, **kargs):
return '{0} {1}'.format(argument.type.translate(self, **kargs), argument.name.translate(self.nameTranslator))
class CLangTranslator(CLikeLangTranslator):
def __init__(self):
self.nameTranslator = metaname.Translator.get('C')
self.nilToken = 'NULL'
self.falseConstantToken = 'FALSE'
self.trueConstantToken = 'TRUE'
def translate_base_type(self, _type):
return _type.cDecl
def translate_enum_type(self, _type):
return _type.cDecl
def translate_class_type(self, _type):
return _type.cDecl
def translate_list_type(self, _type):
return _type.cDecl
def translate_enumerator_value(self, value):
if value is None:
return None
elif isinstance(value, int):
return str(value)
elif isinstance(value, Flag):
return '1<<{0}'.format(value.position)
else:
raise TypeError('invalid enumerator value type: {0}'.format(value))
def translate_method_as_prototype(self, method, **params):
_class = method.find_first_ancestor_by_type(Class)
params = '{const}{className} *obj'.format(
className=_class.name.to_c(),
const='const ' if method.isconst else ''
)
for arg in method.args:
params += (', ' + arg.translate(self))
return '{returnType} {name}({params})'.format(
returnType=method.returnType.translate(self),
name=method.name.translate(self.nameTranslator),
params=params
)
class CppLangTranslator(CLikeLangTranslator):
def __init__(self):
self.nameTranslator = metaname.Translator.get('Cpp')
self.nilToken = 'nullptr'
self.falseConstantToken = 'false'
self.trueConstantToken = 'true'
self.ambigousTypes = []
def translate_base_type(self, _type, showStdNs=True, namespace=None):
if _type.name == 'void':
if _type.isref:
return 'void *'
else:
return 'void'
elif _type.name == 'boolean':
res = 'bool'
elif _type.name == 'character':
res = 'char'
elif _type.name == 'size':
res = 'size_t'
elif _type.name == 'time':
res = 'time_t'
elif _type.name == 'integer':
if _type.size is None:
res = 'int'
elif isinstance(_type.size, str):
res = _type.size
else:
res = 'int{0}_t'.format(_type.size)
elif _type.name == 'floatant':
if _type.size is not None and _type.size == 'double':
res = 'double'
else:
res = 'float'
elif _type.name == 'status':
res = 'linphone::Status'
elif _type.name == 'string':
res = CppLangTranslator.prepend_std('string', showStdNs)
if type(_type.parent) is Argument:
res += ' &'
elif _type.name == 'string_array':
res = '{0}<{1}>'.format(
CppLangTranslator.prepend_std('list', showStdNs),
CppLangTranslator.prepend_std('string', showStdNs)
)
if type(_type.parent) is Argument:
res += ' &'
else:
raise Error('\'{0}\' is not a base abstract type'.format(_type.name))
if _type.isUnsigned:
if _type.name == 'integer' and isinstance(_type.size, int):
res = 'u' + res
else:
res = 'unsigned ' + res
if _type.isconst:
if _type.name not in ['string', 'string_array'] or type(_type.parent) is Argument:
res = 'const ' + res
if _type.isref:
res += ' *'
return res
def translate_enum_type(self, _type, showStdNs=True, namespace=None):
if _type.desc is None:
raise Error('{0} has not been fixed'.format(_type.name))
if namespace is not None:
nsName = namespace.name if namespace is not GlobalNs else None
else:
method = _type.find_first_ancestor_by_type(Method)
nsName = metaname.Name.find_common_parent(_type.desc.name, method.name)
return _type.desc.name.translate(self.nameTranslator, recursive=True, topAncestor=nsName)
def translate_class_type(self, _type, showStdNs=True, namespace=None):
if _type.desc is None:
raise Error('{0} has not been fixed'.format(_type.name))
if namespace is not None:
nsName = namespace.name if namespace is not GlobalNs else None
else:
method = _type.find_first_ancestor_by_type(Method)
nsName = metaname.Name.find_common_parent(_type.desc.name, method.name)
if _type.desc.name.to_c() in self.ambigousTypes:
nsName = None
res = _type.desc.name.translate(self.nameTranslator, recursive=True, topAncestor=nsName)
if _type.desc.refcountable:
if _type.isconst:
res = 'const ' + res
if type(_type.parent) is Argument:
return 'const {0}<{1}> &'.format(
CppLangTranslator.prepend_std('shared_ptr', showStdNs),
res
)
else:
return '{0}<{1}>'.format(
CppLangTranslator.prepend_std('shared_ptr', showStdNs),
res
)
else:
if type(_type.parent) is Argument:
return 'const {0} &'.format(res)
else:
return '{0}'.format(res)
def translate_list_type(self, _type, showStdNs=True, namespace=None):
if _type.containedTypeDesc is None:
raise Error('{0} has not been fixed'.format(_type.containedTypeName))
elif isinstance(_type.containedTypeDesc, BaseType):
res = _type.containedTypeDesc.translate(self)
else:
res = _type.containedTypeDesc.translate(self, showStdNs=showStdNs, namespace=namespace)
if type(_type.parent) is Argument:
return 'const {0}<{1} > &'.format(
CppLangTranslator.prepend_std('list', showStdNs),
res
)
else:
return '{0}<{1} >'.format(
CppLangTranslator.prepend_std('list', showStdNs),
res
)
def translate_method_as_prototype(self, method, showStdNs=True, namespace=None):
_class = method.find_first_ancestor_by_type(Class, Interface)
if namespace is not None:
if _class.name.to_c() in self.ambigousTypes:
nsName = None
else:
nsName = namespace.name if namespace is not GlobalNs else None
else:
nsName = _class.name
argsString = ''
argStrings = []
for arg in method.args:
argStrings.append(arg.translate(self, showStdNs=showStdNs, namespace=namespace))
argsString = ', '.join(argStrings)
return '{_return} {name}({args}){const}'.format(
_return=method.returnType.translate(self, ),
name=method.name.translate(self.nameTranslator, recursive=True, topAncestor=nsName),
args=argsString,
const=' const' if method.isconst else ''
)
@staticmethod
def prepend_std(string, prepend):
return 'std::' + string if prepend else string
class CSharpLangTranslator(CLikeLangTranslator):
def __init__(self):
self.nameTranslator = metaname.Translator.get('CSharp')
self.nilToken = 'null'
self.falseConstantToken = 'false'
self.trueConstantToken = 'true'
def translate_base_type(self, _type, dllImport=True):
if _type.name == 'void':
if _type.isref:
return 'IntPtr'
return 'void'
elif _type.name == 'status':
if dllImport:
return 'int'
else:
return 'void'
elif _type.name == 'boolean':
if dllImport:
res = 'char'
else:
res = 'bool'
elif _type.name == 'integer':
if _type.isUnsigned:
res = 'uint'
else:
res = 'int'
elif _type.name == 'string':
if dllImport:
if type(_type.parent) is Argument:
return 'string'
else:
res = 'IntPtr' # Return as IntPtr and get string with Marshal.PtrToStringAnsi()
else:
return 'string'
elif _type.name == 'character':
if _type.isUnsigned:
res = 'byte'
else:
res = 'sbyte'
elif _type.name == 'time':
res = 'long' #TODO check
elif _type.name == 'size':
res = 'long' #TODO check
elif _type.name == 'floatant':
return 'float'
elif _type.name == 'string_array':
if dllImport or type(_type.parent) is Argument:
return 'IntPtr'
else:
return 'IEnumerable<string>'
else:
raise Error('\'{0}\' is not a base abstract type'.format(_type.name))
return res
def translate_enum_type(self, _type, dllImport=True):
if dllImport and type(_type.parent) is Argument:
return 'int'
else:
return _type.desc.name.translate(self.nameTranslator)
def translate_class_type(self, _type, dllImport=True):
return "IntPtr" if dllImport else _type.desc.name.translate(self.nameTranslator)
def translate_list_type(self, _type, dllImport=True):
if dllImport:
return 'IntPtr'
else:
if type(_type.containedTypeDesc) is BaseType:
if _type.containedTypeDesc.name == 'string':
return 'IEnumerable<string>'
else:
raise Error('translation of bctbx_list_t of basic C types is not supported')
elif type(_type.containedTypeDesc) is ClassType:
ptrType = _type.containedTypeDesc.desc.name.translate(self.nameTranslator)
return 'IEnumerable<' + ptrType + '>'
else:
if _type.containedTypeDesc:
raise Error('translation of bctbx_list_t of enums')
else:
raise Error('translation of bctbx_list_t of unknow type !')
def translate_argument(self, arg, dllImport=True):
return '{0} {1}'.format(
arg.type.translate(self, dllImport=dllImport),
arg.name.translate(self.nameTranslator)
)
def translate_method_as_prototype(self, method):
kargs = {
'static' : 'static ' if method.type == Method.Type.Class else '',
'override' : 'override ' if method.name.translate(self.nameTranslator) == 'ToString' else '',
'returnType' : method.returnType.translate(self, dllImport=False),
'name' : method.name.translate(self.nameTranslator),
'args' : ''
}
for arg in method.args:
if kargs['args'] != '':
kargs['args'] += ', '
kargs['args'] += arg.translate(self, dllImport=False)
return '{static}{override}{returnType} {name}({args})'.format(**kargs)

View file

@ -33,6 +33,7 @@ class CObject:
self.detailedDescription = None
self.deprecated = False
self.briefDoc = None
self.detailedDoc = None
class CEnumValue(CObject):
@ -339,6 +340,7 @@ class Project:
if st.associatedTypedef == td:
cclass = CClass(st)
cclass.briefDoc = td.briefDoc
cclass.detailedDoc = td.detailedDoc
self.add(cclass)
break
elif ('Linphone' + td.definition) == td.name:
@ -346,6 +348,7 @@ class Project:
st.associatedTypedef = td
cclass = CClass(st)
cclass.briefDoc = td.briefDoc
cclass.detailedDoc = td.detailedDoc
self.add(st)
self.add(cclass)
# Sort classes by length of name (longest first), so that methods are put in the right class
@ -389,6 +392,7 @@ class Project:
ev.deprecated = True
ev.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
ev.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
ev.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
ev.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return ev
@ -401,6 +405,7 @@ class Project:
e.deprecated = True
e.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
e.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
e.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
e.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
enumvalues = node.findall("enumvalue[@prot='public']")
for enumvalue in enumvalues:
@ -424,6 +429,7 @@ class Project:
sm.deprecated = True
sm.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
sm.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
sm.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
sm.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return sm
@ -434,6 +440,7 @@ class Project:
s.deprecated = True
s.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
s.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
s.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
s.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
structmembers = node.findall("sectiondef/memberdef[@kind='variable'][@prot='public']")
for structmember in structmembers:
@ -503,6 +510,7 @@ class Project:
f.deprecated = True
f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
f.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
f.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return f
else:
@ -515,6 +523,7 @@ class Project:
td.deprecated = True
td.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
td.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
td.detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
td.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return td
return None
@ -531,6 +540,11 @@ class Project:
internal = node.find("./detaileddescription/internal")
if internal is not None:
return None
# The doc must be parsed here since the XML tree is to be modified in below code
briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
detailedDoc = self.docparser.parse_description(node.find('./detaileddescription'))
missingDocWarning = ''
name = node.find('./name').text
t = ''.join(node.find('./type').itertext())
@ -574,10 +588,11 @@ class Project:
if deprecatedNode is not None:
f.deprecated = True
f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
f.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
if f.briefDescription == '' and ''.join(f.detailedDescription.itertext()).strip() == '':
return None
f.briefDoc = briefDoc
f.detailedDoc = detailedDoc
locationNode = node.find('./location')
if locationNode is not None:
f.location = locationNode.get('file')

View file

@ -1,5 +1,3 @@
#!/usr/bin/python
# Copyright (C) 2017 Belledonne Communications SARL
#
# This program is free software; you can redistribute it and/or
@ -16,17 +14,131 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import metaname
import abstractapi
import re
class Nil:
class ParsingError(RuntimeError):
pass
class Reference:
class UnreleasedNodeError(ValueError):
pass
class ChildrenList(list):
def __init__(self, node):
list.__init__(self)
self.node = node
def __setitem__(self, key, child):
if child.parent is not None:
raise UnreleasedNodeError()
self[key].parent = None
list.__setitem__(self, key, child)
child.parent = self.node
def __delitem__(self, key):
self[key].parent = None
list.__delitem__(self, key)
def __iadd__(self, other):
list.__iadd__(self, other)
for child in other:
child.parent = self.node
return self
def append(self, child):
list.append(self, child)
child.parent = self.node
def removeall(self):
children = []
while len(self) > 0:
children.append(self[0])
del self[0]
return children
class TreeNode(object):
def __init__(self):
self.parent = None
def find_ancestor(self, ancestorType):
ancestor = self.parent
while ancestor is not None and type(ancestor) is not ancestorType:
ancestor = ancestor.parent
return ancestor
def find_root(self):
node = self
while node.parent is not None:
node = node.parent
return node
class SingleChildTreeNode(TreeNode):
def __init__(self):
TreeNode.__init__(self)
self._child = None
def _setchild(self, child):
if child is not None and child.parent is not None:
raise UnreleasedNodeError()
if self._child is not None:
self._child.parent = None
self._child = child
if child is not None:
child.parent = self
def _getchild(self):
return self._child
def _delchild(self):
if self._child is not None:
self._child.parent = None
del self._child
child = property(fset=_setchild, fget=_getchild, fdel=_delchild)
class MultiChildTreeNode(TreeNode):
def __init__(self):
TreeNode.__init__(self)
self.children = ChildrenList(self)
class ParagraphPart(TreeNode):
pass
class TextPart(ParagraphPart):
def __init__(self, text):
ParagraphPart.__init__(self)
self.text = text
def translate(self, docTranslator, **kargs):
return docTranslator.translate_text(self)
class LanguageKeyword(ParagraphPart):
def __init__(self, keyword):
ParagraphPart.__init__(self)
self.keyword = keyword
def translate(self, docTranslator, **kargs):
return docTranslator.translate_keyword(self)
class Reference(ParagraphPart):
def __init__(self, cname):
ParagraphPart.__init__(self)
self.cname = cname
self.relatedObject = None
def translate(self, docTranslator, **kargs):
return docTranslator.translate_reference(self, **kargs)
class ClassReference(Reference):
@ -45,54 +157,210 @@ class FunctionReference(Reference):
print('doc reference pointing on an unknown object ({0})'.format(self.cname))
class Paragraph:
def __init__(self):
self.parts = []
class Paragraph(MultiChildTreeNode):
@property
def parts(self):
return self.children
@parts.setter
def parts(self, parts):
self.children = parts
def resolve_all_references(self, api):
for part in self.parts:
if isinstance(part, Reference):
part.resolve(api)
elif isinstance(part, (Section, ParameterList)):
part.resolve_all_references(api)
def translate(self, docTranslator, **kargs):
return docTranslator._translate_paragraph(self, **kargs)
class Description:
class Section(SingleChildTreeNode):
def __init__(self, kind):
SingleChildTreeNode.__init__(self)
self.kind = kind
@property
def paragraph(self):
return self.child
@paragraph.setter
def paragraph(self, paragraph):
self.child = paragraph
def resolve_all_references(self, api):
if self.paragraph is not None:
self.paragraph.resolve_all_references(api)
def translate(self, docTranslator, **kargs):
return docTranslator._translate_section(self, **kargs)
class ParameterDescription(SingleChildTreeNode):
def __init__(self, name, desc):
SingleChildTreeNode.__init__(self)
self.name = name
self.child = desc
@property
def desc(self):
return self.child
@desc.setter
def desc(self, desc):
self.child = desc
def is_self_parameter(self):
method = self.find_ancestor(Description).relatedObject
return method.type == abstractapi.Method.Type.Instance and self.name not in [arg.name for arg in method.args]
class ParameterList(MultiChildTreeNode):
@property
def parameters(self):
return self.children
@parameters.setter
def parameters(self, parameters):
self.children = parameters
def resolve_all_references(self, api):
for parameter in self.parameters:
if parameter.desc is not None:
parameter.desc.resolve_all_references(api)
def translate(self, docTranslator, **kargs):
return docTranslator._translate_parameter_list(self, **kargs)
class Description(MultiChildTreeNode):
def __init__(self):
self.paragraphs = []
MultiChildTreeNode.__init__(self)
self.relatedObject = None
@property
def paragraphs(self):
return self.children
@paragraphs.setter
def paragraphs(self, paragraphs):
self.children = paragraphs
def resolve_all_references(self, api):
for paragraph in self.paragraphs:
paragraph.resolve_all_references(api)
def translate(self, translator, **kargs):
return translator.translate_description(self, **kargs)
class Parser:
def __init__(self):
self.constants_regex = re.compile('(?:^|\W)(TRUE|FALSE|NULL)(?:\W|$)')
def parse_description(self, node):
if node is None:
return None
desc = Description()
for paraNode in node.findall('./para'):
desc.paragraphs.append(self._parse_paragraph(paraNode))
desc.paragraphs += self._parse_paragraph(paraNode)
return desc
def _parse_paragraph(self, node):
paragraphs = []
paragraph = Paragraph()
for partNode in node.iter():
if partNode is node:
text = node.text
if text is not None:
paragraph.parts += self._parse_text(text)
for partNode in node.findall('*'):
if partNode.tag == 'ref':
ref = self._parse_reference(partNode)
if ref is not None:
paragraph.parts.append(ref)
elif partNode.tag == 'simplesect':
paragraphs.append(paragraph)
paragraph.parts.append(self._parse_simple_section(partNode))
paragraph = Paragraph()
elif partNode.tag == 'xrefsect':
paragraphs.append(paragraph)
paragraph.parts.append(self._parse_xref_section(partNode))
paragraph = Paragraph()
elif partNode.tag == 'parameterlist' and partNode.get('kind') == 'param':
paragraphs.append(paragraph)
paragraphs.append(self._parse_parameter_list(partNode))
paragraph = Paragraph()
else:
text = partNode.text
if text is not None:
paragraph.parts.append(text)
else:
if partNode.tag == 'ref':
ref = self._parse_reference(partNode)
if ref is not None:
paragraph.parts.append(ref)
else:
text = partNode.text
if text is not None:
paragraph.parts.append(text)
tail = partNode.tail
if tail is not None:
paragraph.parts.append(tail)
paragraph.parts += self._parse_text(text)
text = partNode.tail
if text is not None:
text = text.strip('\n')
if len(text) > 0:
paragraph.parts += self._parse_text(text)
return paragraph
paragraphs.append(paragraph)
return [x for x in paragraphs if type(x) is not Paragraph or len(x.parts) > 0]
def _parse_text(self, text):
parts = []
lastIndex = 0
match = self.constants_regex.search(text)
while match is not None:
if match.start(1)-lastIndex > 0:
parts.append(TextPart(text[lastIndex:match.start(1)]))
parts.append(self._parse_constant(text[match.start(1):match.end(1)]))
lastIndex = match.end(1)
match = self.constants_regex.search(text, lastIndex)
if lastIndex < len(text):
parts.append(TextPart(text[lastIndex:]))
return parts
def _parse_constant(self, token):
if token == 'TRUE':
return LanguageKeyword(abstractapi.Boolean(True))
elif token == 'FALSE':
return LanguageKeyword(abstractapi.Boolean(False))
elif token == 'NULL':
return LanguageKeyword(abstractapi.Nil())
else:
raise ValueError("invalid C constant token '{0}'".format(token))
def _parse_simple_section(self, sectionNode):
section = Section(sectionNode.get('kind'))
para = sectionNode.find('./para')
paragraphs = self._parse_paragraph(para)
section.paragraph = paragraphs[0] if len(paragraphs) > 0 else None
return section
def _parse_parameter_list(self, paramListNode):
paramList = ParameterList()
for paramItemNode in paramListNode.findall('./parameteritem'):
name = metaname.ArgName()
name.from_snake_case(paramItemNode.find('./parameternamelist/parametername').text)
desc = self.parse_description(paramItemNode.find('parameterdescription'))
paramList.parameters.append(ParameterDescription(name, desc))
return paramList
def _parse_xref_section(self, sectionNode):
sectionId = sectionNode.get('id')
if sectionId.startswith('deprecated_'):
section = Section('deprecated')
description = self.parse_description(sectionNode.find('./xrefdescription'))
paras = description.paragraphs.removeall()
section.paragraph = paras[0] if len(paras) > 0 else None
return section
else:
raise ParsingError('unknown xrefsect type ({0})'.format(sectionId))
def _parse_reference(self, node):
if node.text.endswith('()'):
@ -101,50 +369,92 @@ class Parser:
return ClassReference(node.text)
class Translator:
def __init__(self):
self.textWidth = 80
class TranslationError(Exception):
pass
class ReferenceTranslationError(TranslationError):
def __init__(self, refName):
Exception.__init__(self, refName)
def translate(self, description):
def msg(self):
return '{0} reference could not been translated'.format(self.args[0])
class Translator:
def __init__(self, langCode):
self.textWidth = 80
self.nameTranslator = metaname.Translator.get(langCode)
self.langTranslator = abstractapi.Translator.get(langCode)
self.displaySelfParam = True if langCode == 'C' else False
def translate_description(self, description, tagAsBrief=False):
if description is None:
return None
lines = []
for para in description.paragraphs:
if para is not description.paragraphs[0]:
lines.append('')
lines.append(self._translate_paragraph(para))
paras = self._translate_description(description)
lines = self._paragraphs_to_lines(paras)
if tagAsBrief:
self._tag_as_brief(lines)
self._tag_as_brief(lines)
lines = self._crop_text(lines, self.textWidth)
translatedDoc = {'lines': []}
for line in lines:
translatedDoc['lines'].append({'line': line})
return translatedDoc
def translate_reference(self, ref, absName=False, namespace=None):
if ref.relatedObject is None:
raise ReferenceTranslationError(ref.cname)
if absName:
commonName = None
else:
if namespace is None:
description = ref.find_root()
namespaceObj = description.relatedObject.find_first_ancestor_by_type(abstractapi.Namespace, abstractapi.Class)
namespace = namespaceObj.name
if namespace.is_prefix_of(ref.relatedObject.name):
commonName = namespace
else:
commonName = metaname.Name.find_common_parent(ref.relatedObject.name, namespace)
return ref.relatedObject.name.translate(self.nameTranslator, recursive=True, topAncestor=commonName)
def translate_keyword(self, keyword):
return keyword.keyword.translate(self.langTranslator)
def translate_text(self, textpart):
return textpart.text
def _translate_description(self, desc):
paras = []
for para in desc.paragraphs:
paras.append(para.translate(self))
return [para for para in paras if para != '']
def _translate_paragraph(self, para):
strPara = ''
for part in para.parts:
if isinstance(part, str):
strPara += part
elif isinstance(part, Reference):
try:
strPara += self._translate_reference(part)
except ReferenceTranslationError as e:
print('could not translate one reference in docstrings ({0})'.format(e.args[0]))
strPara += Translator._translate_reference(self, part)
else:
raise TypeError('untranslatable paragraph element ({0})'.format(part))
try:
if isinstance(part, str):
strPara += part
else:
strPara += part.translate(self)
except TranslationError as e:
print('error: {0}'.format(e.msg()))
return strPara
def _translate_reference(self, ref):
if isinstance(ref, FunctionReference):
return ref.cname + '()'
else:
return ref.cname
def _paragraphs_to_lines(self, paragraphs):
lines = []
for para in paragraphs:
if para is not paragraphs[0]:
lines.append('')
lines += para.split('\n')
return lines
def _crop_text(self, inputLines, width):
outputLines = []
@ -152,7 +462,12 @@ class Translator:
outputLines += self._split_line(line, width)
return outputLines
def _split_line(self, line, width):
def _split_line(self, line, width, indent=False):
firstNonTab = next((c for c in line if c != '\t'), None)
tabCount = line.index(firstNonTab) if firstNonTab is not None else 0
linePrefix = ('\t' * tabCount)
line = line[tabCount:]
lines = []
while len(line) > width:
cutIndex = line.rfind(' ', 0, width)
@ -163,36 +478,170 @@ class Translator:
cutIndex = width
lines.append(line[0:cutIndex])
line = line[cutIndex:]
lines.append(line)
return lines
if indent:
lines = [line if line is lines[0] else '\t' + line for line in lines]
return [linePrefix + line for line in lines]
def _tag_as_brief(self, lines):
pass
class ReferenceTranslationError(RuntimeError):
pass
class DoxygenCppTranslator(Translator):
class DoxygenTranslator(Translator):
def _tag_as_brief(self, lines):
if len(lines) > 0:
lines[0] = '@brief ' + lines[0]
def _translate_reference(self, ref):
def translate_reference(self, ref):
refStr = Translator.translate_reference(self, ref)
if isinstance(ref.relatedObject, (abstractapi.Class, abstractapi.Enum)):
return '#' + ref.relatedObject.name.to_c()
return '#' + refStr
elif isinstance(ref.relatedObject, abstractapi.Method):
return ref.relatedObject.name.to_c() + '()'
return refStr + '()'
else:
raise ReferenceTranslationError(ref.cname)
def _translate_section(self, section):
return '@{0} {1}'.format(
section.kind,
self._translate_paragraph(section.paragraph)
)
def _translate_parameter_list(self, parameterList):
text = ''
for paramDesc in parameterList.parameters:
if self.displaySelfParam or not paramDesc.is_self_parameter():
desc = self._translate_description(paramDesc.desc)
desc = desc[0] if len(desc) > 0 else ''
text = ('@param {0} {1}'.format(paramDesc.name.translate(self.nameTranslator), desc))
return text
class SandcastleCSharpTranslator(Translator):
class JavaDocTranslator(DoxygenTranslator):
def __init__(self):
DoxygenTranslator.__init__(self, 'C')
def _tag_as_brief(self, lines):
pass
class SphinxTranslator(Translator):
def __init__(self, langCode):
Translator.__init__(self, langCode)
if langCode == 'C':
self.domain = 'c'
self.classDeclarator = 'type'
self.methodDeclarator = 'function'
self.enumDeclarator = 'type'
self.enumeratorDeclarator = 'var'
self.enumeratorReferencer = 'data'
self.methodReferencer = 'func'
elif langCode == 'Cpp':
self.domain = 'cpp'
self.classDeclarator = 'class'
self.methodDeclarator = 'function'
self.enumDeclarator = 'enum'
self.enumeratorDeclarator = 'enumerator'
self.namespaceDeclarator = 'namespace'
self.methodReferencer = 'func'
elif langCode == 'CSharp':
self.domain = 'csharp'
self.classDeclarator = 'class'
self.methodDeclarator = 'method'
self.enumDeclarator = 'enum'
self.enumeratorDeclarator = 'value'
self.namespaceDeclarator = 'namespace'
self.classReferencer = 'type'
self.enumReferencer = 'type'
self.enumeratorReferencer = 'enum'
self.methodReferencer = 'meth'
else:
raise ValueError('invalid language code: {0}'.format(langCode))
def get_declarator(self, typeName):
try:
attrName = typeName + 'Declarator'
declarator = getattr(self, attrName)
return '{0}:{1}'.format(self.domain, declarator)
except AttributeError:
raise ValueError("'{0}' declarator type not supported".format(typeName))
def get_referencer(self, typeName):
try:
attrName = typeName + 'Referencer'
if attrName in dir(self):
referencer = getattr(self, attrName)
return '{0}:{1}'.format(self.domain, referencer)
else:
return self.get_declarator(typeName)
except AttributeError:
raise ValueError("'{0}' referencer type not supported".format(typeName))
def translate_reference(self, ref, label=None, namespace=None):
strRef = Translator.translate_reference(self, ref, absName=True)
kargs = {
'tag' : self._sphinx_ref_tag(ref),
'ref' : strRef,
}
kargs['label'] = label if label is not None else Translator.translate_reference(self, ref, namespace=namespace)
if isinstance(ref, FunctionReference):
kargs['label'] += '()'
return ':{tag}:`{label} <{ref}>`'.format(**kargs)
def translate_keyword(self, keyword):
translatedKeyword = Translator.translate_keyword(self, keyword)
return '``{0}``'.format(translatedKeyword)
def _translate_section(self, section):
strPara = self._translate_paragraph(section.paragraph)
if section.kind == 'deprecated':
return '**Deprecated:** {0}\n'.format(strPara)
else:
if section.kind == 'see':
kind = 'seealso'
else:
kind = section.kind
if section.kind == 'return':
return ':return: {0}'.format(strPara)
else:
return '.. {0}::\n\t\n\t{1}\n\n'.format(kind, strPara)
def _translate_parameter_list(self, parameterList):
text = ''
for paramDesc in parameterList.parameters:
if self.displaySelfParam or not paramDesc.is_self_parameter():
desc = self._translate_description(paramDesc.desc)
desc = desc[0] if len(desc) > 0 else ''
text += (':param {0}: {1}\n'.format(paramDesc.name.translate(self.nameTranslator), desc))
text += '\n'
return text
def _sphinx_ref_tag(self, ref):
typeName = type(ref.relatedObject).__name__.lower()
return self.get_referencer(typeName)
isParamDescRegex = re.compile('\t*:(?:param\s+\w+|return):')
def _split_line(self, line, width):
if SphinxTranslator.isParamDescRegex.match(line) is not None:
lines = Translator._split_line(self, line, width, indent=True)
return lines
else:
return Translator._split_line(self, line, width)
class SandCastleTranslator(Translator):
def _tag_as_brief(self, lines):
if len(lines) > 0:
lines.insert(0, '<summary>')
lines.append('</summary>')
class SandcastleJavaTranslator(Translator):
def _tag_as_brief(self, lines):
pass
def translate_reference(self, ref):
refStr = Translator.translate_reference(self, ref, absName=True)
if isinstance(ref, FunctionReference):
refStr += '()'
return '<see cref="{0}" />'.format(refStr)

340
tools/metaname.py Normal file
View file

@ -0,0 +1,340 @@
# Copyright (C) 2017 Belledonne Communications SARL
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU 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.
import re
class Name(object):
camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*')
lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)')
def __init__(self):
self.words = []
self.prev = None
def __eq__(self, other):
return (other is not None and self.words == other.words) and (self.prev == other.prev)
def __lt__(self, other):
return self.to_camel_case() < other.to_camel_case()
def copy(self):
nameType = type(self)
name = nameType()
name.words = list(self.words)
name.prev = None if self.prev is None else self.prev.copy()
return name
def delete_prefix(self, prefix):
it = self
_next = None
while it is not None and it.words != prefix.words:
_next = it
it = it.prev
if it is None or it != prefix:
raise Error('no common prefix')
elif _next is not None:
_next.prev = None
def _set_namespace(self, namespace):
self.prev = namespace
if self.prev is not None:
prefix = namespace.to_word_list()
i = 0
while i<len(self.words) and i<len(prefix) and self.words[i] == prefix[i]:
i += 1
if i == len(self.words):
raise Error('name equal to namespace \'{0}\'', self.words)
else:
self.words = self.words[i:]
def _lower_all_words(self):
i = 0
while i<len(self.words):
self.words[i] = self.words[i].lower()
i += 1
def from_snake_case(self, name, namespace=None):
self.words = name.split('_')
Name._set_namespace(self, namespace)
def from_camel_case(self, name, islowercased=False, namespace=None):
if not islowercased:
self.words = Name.camelCaseParsingRegex.findall(name)
else:
match = Name.lowerCamelCaseSplitingRegex.match(name)
self.words = [match.group(1)]
self.words += Name.camelCaseParsingRegex.findall(match.group(2))
Name._lower_all_words(self)
Name._set_namespace(self, namespace)
def to_snake_case(self, fullName=False, upper=False):
if self.prev is None or not fullName:
res = '_'.join(self.words)
if upper:
res = res.upper()
return res
else:
return Name.to_snake_case(self.prev, fullName=True, upper=upper) + '_' + Name.to_snake_case(self, upper=upper)
def to_camel_case(self, lower=False, fullName=False):
if self.prev is None or not fullName:
res = ''
for elem in self.words:
if elem is self.words[0] and lower:
res += elem
else:
res += elem.title()
return res
else:
return Name.to_camel_case(self.prev, fullName=True, lower=lower) + Name.to_camel_case(self)
def concatenate(self, upper=False, fullName=False):
if self.prev is None or not fullName:
res = ''
for elem in self.words:
if upper:
res += elem.upper()
else:
res += elem
return res
else:
return Name.concatenate(self.prev, upper=upper, fullName=True) + Name.concatenate(self, upper=upper)
def to_word_list(self):
if self.prev is None:
return list(self.words)
else:
return Name.to_word_list(self.prev) + self.words
def is_prefix_of(self, other):
node = other
while node is not None and node != self:
node = node.prev
return (node is not None)
@staticmethod
def find_common_parent(name1, name2):
if name1.prev is None or name2.prev is None:
return None
elif name1.prev is name2.prev:
return name1.prev
else:
commonParent = Name.find_common_parent(name1.prev, name2)
if commonParent is not None:
return commonParent
else:
return Name.find_common_parent(name1, name2.prev)
class ClassName(Name):
def to_c(self):
return self.to_camel_case(fullName=True)
def translate(self, translator, **params):
return translator.translate_class_name(self, **params)
class InterfaceName(ClassName):
def to_c(self):
return ClassName.to_c(self)[:-8] + 'Cbs'
def translate(self, translator, **params):
return translator.translate_interface_name(self, **params)
class EnumName(ClassName):
def translate(self, translator, **params):
return translator.translate_enum_name(self, **params)
class EnumeratorName(ClassName):
def translate(self, translator, **params):
return translator.translate_enumerator_name(self, **params)
class MethodName(Name):
regex = re.compile('^\d+$')
def __init__(self):
self.overloadRef = 0
def from_snake_case(self, name, namespace=None):
Name.from_snake_case(self, name, namespace=namespace)
if len(self.words) > 0:
suffix = self.words[-1]
if MethodName.regex.match(suffix) is not None:
self.overloadRef = int(suffix)
del self.words[-1]
def to_c(self):
suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else ''
return self.to_snake_case(fullName=True) + suffix
def translate(self, translator, **params):
return translator.translate_method_name(self, **params)
class ArgName(Name):
def to_c(self):
return self.to_snake_case()
def translate(self, translator, **params):
return translator.translate_argument_name(self, **params)
class PropertyName(ArgName):
def translate(self, translator, **params):
return translator.translate_property_name(self, **params)
class NamespaceName(Name):
def __init__(self, *params):
Name.__init__(self)
if len(params) > 0:
self.words = params[0]
def translate(self, translator, **params):
return translator.translate_namespace_name(self, **params)
class Translator:
instances = {}
@staticmethod
def get(langCode):
try:
if langCode == '':
raise ValueError('Empty language code')
if langCode not in Translator.instances:
className = langCode + 'Translator'
_class = globals()[className]
Translator.instances[langCode] = _class()
return Translator.instances[langCode]
except KeyError:
raise ValueError("Invalid language code: '{0}'".format(langCode))
class CTranslator(Translator):
def translate_class_name(self, name, **params):
return name.to_c()
def translate_interface_name(self, name, **params):
return name.to_c()
def translate_enum_name(self, name, **params):
return name.to_c()
def translate_enumerator_name(self, name, **params):
return name.to_c()
def translate_method_name(self, name, **params):
return name.to_c()
def translate_namespace_name(self, name, **params):
return None
def translate_argument_name(self, name, **params):
return name.to_c()
def translate_property_name(self, name, **params):
return name.to_c()
class JavaTranslator(Translator):
def __init__(self):
self.nsSep = '.'
self.keyWordEscapes = {}
self.lowerMethodNames = True
self.lowerNamespaceNames = True
def translate_class_name(self, name, recursive=False, topAncestor=None):
if name.prev is None or not recursive or name.prev is topAncestor:
return name.to_camel_case()
else:
params = {'recursive': recursive, 'topAncestor': topAncestor}
return name.prev.translate(self, **params) + self.nsSep + name.to_camel_case()
def translate_interface_name(self, name, **params):
return self.translate_class_name(name, **params)
def translate_enum_name(self, name, **params):
return self.translate_class_name(name, **params)
def translate_enumerator_name(self, name, **params):
return self.translate_class_name(name, **params)
def translate_method_name(self, name, recursive=False, topAncestor=None):
translatedName = name.to_camel_case(lower=self.lowerMethodNames)
translatedName = self._escape_keyword(translatedName)
if name.prev is None or not recursive or name.prev is topAncestor:
return translatedName
else:
params = {'recursive': recursive, 'topAncestor': topAncestor}
return name.prev.translate(self, **params) + self.nsSep + translatedName
def translate_namespace_name(self, name, recursive=False, topAncestor=None):
translatedName = name.concatenate() if self.lowerNamespaceNames else name.to_camel_case()
if name.prev is None or not recursive or name.prev is topAncestor:
return translatedName
else:
params = {'recursive': recursive, 'topAncestor': topAncestor}
return name.prev.translate(self, **params) + self.nsSep + translatedName
def translate_argument_name(self, name):
argname = name.to_camel_case(lower=True)
return self._escape_keyword(argname)
def translate_property_name(self, name):
return self.translate_argument_name(name)
def _escape_keyword(self, keyword):
try:
return self.keyWordEscapes[keyword]
except KeyError:
return keyword
class CppTranslator(JavaTranslator):
def __init__(self):
JavaTranslator.__init__(self)
self.nsSep = '::'
self.keyWordEscapes = {'new' : '_new'}
def translate_enumerator_name(self, name, **params):
return self.translate_enum_name(name.prev, **params) + name.to_camel_case()
class CSharpTranslator(JavaTranslator):
def __init__(self):
JavaTranslator.__init__(self)
self.keyWordEscapes = {
'params' : 'parameters',
'event' : 'ev',
'ref' : 'reference',
'value' : 'val',
'new' : '_new'
}
self.lowerMethodNames = False
self.lowerNamespaceNames = False
def translate_property_name(self, name):
return name.to_camel_case()

View file

@ -24,6 +24,7 @@ add_custom_command(OUTPUT include/linphone++/linphone.hh src/linphone++.cc
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/genwrapper.py" "${PROJECT_BINARY_DIR}/coreapi/help/doc/doxygen/xml"
DEPENDS ${PROJECT_SOURCE_DIR}/tools/genapixml.py
${PROJECT_SOURCE_DIR}/tools/metadoc.py
${PROJECT_SOURCE_DIR}/tools/metaname.py
${PROJECT_SOURCE_DIR}/tools/abstractapi.py
genwrapper.py
class_header.mustache

View file

@ -54,13 +54,19 @@ namespace linphone {
{{/priorDeclarations}}
{{#_class}}
{{#doc}}
/**
{{#briefDoc}}
{{#lines}}
* {{{line}}}
{{/lines}}
{{/briefDoc}}
*
{{#detailedDoc}}
{{#lines}}
* {{{line}}}
{{/lines}}
{{/detailedDoc}}
*/
{{/doc}}
class {{className}}{{#parentClassName}}: public {{{parentClassName}}}{{/parentClassName}} {
{{#friendClasses}}
friend class {{name}};
@ -102,25 +108,37 @@ namespace linphone {
{{#methods}}
{{#doc}}
/**
{{#lines}}
{{#briefDoc}}
{{#lines}}
* {{{line}}}
{{/lines}}
{{/lines}}
{{/briefDoc}}
*
{{#detailedDoc}}
{{#lines}}
* {{{line}}}
{{/lines}}
{{/detailedDoc}}
*/
{{/doc}}
{{{prototype}}}
LINPHONECXX_PUBLIC {{#deprecated}}LINPHONECXX_DEPRECATED {{/deprecated}}{{#isListener}}virtual {{/isListener}}{{{declPrototype}}}{{{suffix}}};
{{/methods}}
{{#staticMethods}}
{{#doc}}
{{#staticMethods}};
/**
{{#lines}}
{{#briefDoc}}
{{#lines}}
* {{{line}}}
{{/lines}}
{{/lines}}
{{/briefDoc}}
*
{{#detailedDoc}}
{{#lines}}
* {{{line}}}
{{/lines}}
{{/detailedDoc}}
*/
{{/doc}}
{{{prototype}}}
LINPHONECXX_PUBLIC {{#deprecated}}LINPHONECXX_DEPRECATED {{/deprecated}}static {{{declPrototype}}};
{{/staticMethods}}

View file

@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <linphone/linphone_tunnel.h>
#include <linphone/linphonecore_utils.h>
#include <linphone/wrapper_utils.h>
#include <linphone/logging.h>
#include "linphone++/linphone.hh"
#include "tools.hh"

View file

@ -31,7 +31,7 @@ namespace linphone {
*/
{{/doc}}
enum {{name}} {
{{#values}}
{{#enumerators}}
{{#doc}}
/**
{{#lines}}
@ -40,7 +40,7 @@ namespace linphone {
*/
{{/doc}}
{{name}}{{#value}} = {{{value}}}{{/value}}{{#notLast}},{{/notLast}}
{{/values}}
{{/enumerators}}
};
{{/enums}}

View file

@ -23,19 +23,23 @@ import argparse
import os
import sys
import errno
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'tools'))
import genapixml as CApi
import abstractapi as AbsApi
import metadoc
import metaname
class CppTranslator(object):
sharedPtrTypeExtractor = re.compile('^(const )?std::shared_ptr<(.+)>( &)?$')
def __init__(self):
self.ignore = []
self.ambigousTypes = ['LinphonePayloadType']
self.docTranslator = metadoc.DoxygenCppTranslator()
self.nameTranslator = metaname.Translator.get('Cpp')
self.langTranslator = AbsApi.Translator.get('Cpp')
self.langTranslator.ambigousTypes.append('LinphonePayloadType')
self.docTranslator = metadoc.DoxygenTranslator('Cpp')
def is_ambigous_type(self, _type):
return _type.name in self.ambigousTypes or (_type.name == 'list' and self.is_ambigous_type(_type.containedTypeDesc))
@ -43,32 +47,23 @@ class CppTranslator(object):
def translate_enum(self, enum):
enumDict = {}
enumDict['name'] = enum.name.to_camel_case()
enumDict['doc'] = self.docTranslator.translate(enum.briefDescription)
enumDict['values'] = []
i = 0
for enumValue in enum.values:
enumValDict = self.translate_enum_value(enumValue)
enumValDict['notLast'] = (i != len(enum.values)-1)
enumDict['values'].append(enumValDict)
i += 1
enumDict['doc'] = enum.briefDescription.translate(self.docTranslator)
enumDict['enumerators'] = []
for enumerator in enum.enumerators:
enumeratorDict = self.translate_enumerator(enumerator)
enumeratorDict['notLast'] = (enumerator is not enum.enumerators[-1])
enumDict['enumerators'].append(enumeratorDict)
return enumDict
def translate_enum_value(self, enumValue):
enumValueDict = {}
enumValueDict['name'] = CppTranslator.translate_enum_value_name(enumValue.name)
enumValueDict['doc'] = self.docTranslator.translate(enumValue.briefDescription)
if type(enumValue.value) is int:
enumValueDict['value'] = str(enumValue.value)
elif type(enumValue.value) is AbsApi.Flag:
enumValueDict['value'] = '1<<' + str(enumValue.value.position)
else:
enumValueDict['value'] = None
return enumValueDict
def translate_enumerator(self, enumerator):
enumeratorDict = {
'name' : enumerator.name.translate(self.nameTranslator),
'doc' : enumerator.briefDescription.translate(self.docTranslator),
'value' : enumerator.translate_value(self.langTranslator)
}
return enumeratorDict
def translate_class(self, _class):
if _class.name.to_camel_case(fullName=True) in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(_class.name.to_camel_case(fullName=True)))
islistenable = _class.listenerInterface is not None
ismonolistenable = (islistenable and not _class.multilistener)
ismultilistenable = (islistenable and _class.multilistener)
@ -81,9 +76,10 @@ class CppTranslator(object):
'isrefcountable' : _class.refcountable,
'isnotrefcountable' : not _class.refcountable,
'isNotListener' : True,
'isListener' : False,
'isfactory' : (_class.name.to_c() == 'LinphoneFactory'),
'isVcard' : (_class.name.to_c() == 'LinphoneVcard'),
'className' : CppTranslator.translate_class_name(_class.name),
'className' : _class.name.translate(self.nameTranslator),
'cClassName' : '::' + _class.name.to_c(),
'privCClassName' : '_' + _class.name.to_c(),
'parentClassName' : 'Object' if _class.refcountable else None,
@ -96,12 +92,13 @@ class CppTranslator(object):
if _class.name.to_c() == 'LinphoneCore':
classDict['friendClasses'].append({'name': 'Factory'});
classDict['doc'] = self.docTranslator.translate(_class.briefDescription)
classDict['briefDoc'] = _class.briefDescription.translate(self.docTranslator, tagAsBrief=True)
classDict['detailedDoc'] = _class.detailedDescription.translate(self.docTranslator)
if islistenable:
classDict['listenerClassName'] = CppTranslator.translate_class_name(_class.listenerInterface.name)
classDict['listenerClassName'] = _class.listenerInterface.name.translate(self.nameTranslator)
classDict['cListenerName'] = _class.listenerInterface.name.to_c()
classDict['cppListenerName'] = CppTranslator.translate_class_name(_class.listenerInterface.name)
classDict['cppListenerName'] = _class.listenerInterface.name.translate(self.nameTranslator)
for method in _class.listenerInterface.methods:
classDict['wrapperCbs'].append(self._generate_wrapper_callback(_class, method))
@ -148,10 +145,10 @@ class CppTranslator(object):
args = []
wrappedArgs = []
for arg in method.args:
args.append(arg.type.cname + ' ' + arg.name.to_c())
args.append(arg.type.cDecl + ' ' + arg.name.to_c())
wrappedArgs.append(self._wrap_c_expression_to_cpp(arg.name.to_c(), arg.type, usedNamespace=namespace))
params['params'] = ', '.join(args)
params['returnType'] = method.returnType.cname
params['returnType'] = method.returnType.cDecl
wrapperCbDict = {}
wrapperCbDict['cbName'] = params['name']
@ -168,16 +165,15 @@ class CppTranslator(object):
return wrapperCbDict
def translate_interface(self, interface):
if interface.name.to_camel_case(fullName=True) in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(interface.name.to_camel_case(fullName=True)))
intDict = {}
intDict['inheritFrom'] = {'name': 'Listener'}
intDict['className'] = CppTranslator.translate_class_name(interface.name)
intDict['constructor'] = None
intDict['parentClassName'] = 'Listener'
intDict['isNotListener'] = False
intDict['methods'] = []
intDict = {
'inheritFrom' : {'name': 'Listener'},
'className' : interface.name.translate(self.nameTranslator),
'constructor' : None,
'parentClassName' : 'Listener',
'isNotListener' : False,
'isListener' : True,
'methods' : []
}
for method in interface.methods:
try:
methodDict = self.translate_method(method, genImpl=False)
@ -196,57 +192,26 @@ class CppTranslator(object):
return res
def translate_method(self, method, genImpl=True):
if method.name.to_snake_case(fullName=True) in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(method.name.to_snake_case(fullName=True)))
namespace = method.find_first_ancestor_by_type(AbsApi.Namespace)
methodElems = {}
methodElems['return'] = self.translate_type(method.returnType)
methodElems['name'] = CppTranslator.translate_method_name(method.name)
methodDict = {
'declPrototype': method.translate_as_prototype(self.langTranslator),
'implPrototype': method.translate_as_prototype(self.langTranslator, namespace=namespace),
'deprecated': method.deprecated,
'suffix': '',
'briefDoc': method.briefDescription.translate(self.docTranslator, tagAsBrief=True) if method.briefDescription is not None else None,
'detailedDoc': method.detailedDescription.translate(self.docTranslator) if method.detailedDescription is not None else None
}
methodElems['params'] = ''
for arg in method.args:
if arg is not method.args[0]:
methodElems['params'] += ', '
methodElems['params'] += self.translate_argument(arg)
methodElems['const'] = ' const' if method.constMethod else ''
methodElems['semicolon'] = ';'
if type(method.parent) is AbsApi.Class and method.type == AbsApi.Method.Type.Class:
methodElems['methodType'] = 'static '
elif type(method.parent) is AbsApi.Interface:
methodElems['methodType'] = 'virtual '
if type(method.parent) is AbsApi.Interface:
if isinstance(method.returnType, AbsApi.BaseType) and method.returnType.name == 'void':
methodElems['semicolon'] = ' {}'
methodDict['suffix'] = ' {}'
else:
methodElems['semicolon'] = ' = 0;'
else:
methodElems['methodType'] = ''
methodElems['deprecated'] = 'LINPHONECXX_DEPRECATED ' if method.deprecated else ''
methodDict = {}
methodDict['prototype'] = 'LINPHONECXX_PUBLIC {deprecated}{methodType}{return} {name}({params}){const}{semicolon}'.format(**methodElems)
methodDict['suffix'] = ' = 0'
if genImpl:
if not self.is_ambigous_type(method.returnType):
methodElems['implReturn'] = self.translate_type(method.returnType, namespace=namespace)
else:
methodElems['implReturn'] = self.translate_type(method.returnType, namespace=None)
methodElems['longname'] = CppTranslator.translate_method_name(method.name, recursive=True)
methodElems['implParams'] = ''
for arg in method.args:
if arg is not method.args[0]:
methodElems['implParams'] += ', '
methodElems['implParams'] += self.translate_argument(arg, namespace=namespace)
methodDict['implPrototype'] = '{implReturn} {longname}({implParams}){const}'.format(**methodElems)
methodDict['sourceCode' ] = self._generate_source_code(method, usedNamespace=namespace)
methodDict['doc'] = self.docTranslator.translate(method.briefDescription) if method.briefDescription is not None else None
return methodDict
def _generate_source_code(self, method, usedNamespace=None):
@ -291,7 +256,7 @@ class CppTranslator(object):
elif type(exprtype) is AbsApi.ClassType:
cPtrType = exprtype.desc.name.to_c()
if exprtype.desc.refcountable:
ptrType = self.translate_class_type(exprtype, namespace=usedNamespace)
ptrType = exprtype.translate(self.langTranslator, namespace=usedNamespace)
ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2)
param = {
'ptrType' : ptrType,
@ -309,7 +274,7 @@ class CppTranslator(object):
if type(exprtype.containedTypeDesc) is AbsApi.BaseType and exprtype.containedTypeDesc.name == 'string':
cExpr = 'StringBctbxListWrapper({0}).c_list()'.format(cppExpr)
elif type(exprtype.containedTypeDesc) is AbsApi.ClassType:
ptrType = self.translate_class_type(exprtype.containedTypeDesc, namespace=usedNamespace)
ptrType = exprtype.containedTypeDesc.translate(self.langTranslator, namespace=usedNamespace)
if exprtype.containedTypeDesc.desc.refcountable:
ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2)
cExpr = 'ObjectBctbxListWrapper<{0}>({1}).c_list()'.format(ptrType, cppExpr)
@ -335,10 +300,10 @@ class CppTranslator(object):
else:
return cExpr
elif type(exprtype) is AbsApi.EnumType:
cppEnumName = self.translate_enum_type(exprtype, namespace=usedNamespace)
cppEnumName = exprtype.translate(self.langTranslator, namespace=usedNamespace)
return '({0}){1}'.format(cppEnumName, cExpr)
elif type(exprtype) is AbsApi.ClassType:
cppReturnType = self.translate_class_type(exprtype, namespace=usedNamespace)
cppReturnType = exprtype.translate(self.langTranslator, namespace=usedNamespace)
if exprtype.desc.refcountable:
cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2)
@ -358,7 +323,7 @@ class CppTranslator(object):
if type(exprtype.containedTypeDesc) is AbsApi.BaseType and exprtype.containedTypeDesc.name == 'string':
return 'StringBctbxListWrapper::bctbxListToCppList({0})'.format(cExpr)
elif type(exprtype.containedTypeDesc) is AbsApi.ClassType:
cppReturnType = self.translate_class_type(exprtype.containedTypeDesc, namespace=usedNamespace)
cppReturnType = exprtype.containedTypeDesc.translate(self.langTranslator, namespace=usedNamespace)
if exprtype.containedTypeDesc.desc.refcountable:
cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2)
return 'ObjectBctbxListWrapper<{0}>::bctbxListToCppList({1})'.format(cppReturnType, cExpr)
@ -370,198 +335,6 @@ class CppTranslator(object):
else:
return cExpr
def translate_argument(self, arg, **params):
return '{0} {1}'.format(self.translate_type(arg.type, **params), CppTranslator.translate_argument_name(arg.name))
def translate_type(self, aType, **params):
if type(aType) is AbsApi.BaseType:
return self.translate_base_type(aType)
elif type(aType) is AbsApi.EnumType:
return self.translate_enum_type(aType, **params)
elif type(aType) is AbsApi.ClassType:
return self.translate_class_type(aType, **params)
elif type(aType) is AbsApi.ListType:
return self.translate_list_type(aType, **params)
else:
CppTranslator.fail(aType)
def translate_base_type(self, _type):
if _type.name == 'void':
if _type.isref:
return 'void *'
else:
return 'void'
elif _type.name == 'boolean':
res = 'bool'
elif _type.name == 'character':
res = 'char'
elif _type.name == 'size':
res = 'size_t'
elif _type.name == 'time':
res = 'time_t'
elif _type.name == 'integer':
if _type.size is None:
res = 'int'
elif isinstance(_type.size, str):
res = _type.size
else:
res = 'int{0}_t'.format(_type.size)
elif _type.name == 'floatant':
if _type.size is not None and _type.size == 'double':
res = 'double'
else:
res = 'float'
elif _type.name == 'status':
res = 'linphone::Status'
elif _type.name == 'string':
res = 'std::string'
if type(_type.parent) is AbsApi.Argument:
res += ' &'
elif _type.name == 'string_array':
res = 'std::list<std::string>'
if type(_type.parent) is AbsApi.Argument:
res += ' &'
else:
raise AbsApi.Error('\'{0}\' is not a base abstract type'.format(_type.name))
if _type.isUnsigned:
if _type.name == 'integer' and isinstance(_type.size, int):
res = 'u' + res
else:
res = 'unsigned ' + res
if _type.isconst:
if _type.name not in ['string', 'string_array'] or type(_type.parent) is AbsApi.Argument:
res = 'const ' + res
if _type.isref:
res += ' *'
return res
def translate_enum_type(self, _type, **params):
if _type.name in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(_type.name))
if _type.desc is None:
raise AbsApi.Error('{0} has not been fixed'.format(_type.name))
if 'namespace' in params:
nsName = params['namespace'].name if params['namespace'] is not None else None
else:
method = _type.find_first_ancestor_by_type(AbsApi.Method)
nsName = AbsApi.Name.find_common_parent(_type.desc.name, method.name)
return CppTranslator.translate_enum_name(_type.desc.name, recursive=True, topAncestor=nsName)
def translate_class_type(self, _type, **params):
if _type.name in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(_type.name))
if _type.desc is None:
raise AbsApi.Error('{0} has not been fixed'.format(_type.name))
if 'namespace' in params:
nsName = params['namespace'].name if params['namespace'] is not None else None
else:
method = _type.find_first_ancestor_by_type(AbsApi.Method)
nsName = AbsApi.Name.find_common_parent(_type.desc.name, method.name)
res = CppTranslator.translate_class_name(_type.desc.name, recursive=True, topAncestor=nsName)
if _type.desc.refcountable:
if _type.isconst:
res = 'const ' + res
if type(_type.parent) is AbsApi.Argument:
return 'const std::shared_ptr<{0}> &'.format(res)
else:
return 'std::shared_ptr<{0}>'.format(res)
else:
if type(_type.parent) is AbsApi.Argument:
return 'const {0} &'.format(res)
else:
return '{0}'.format(res)
def translate_list_type(self, _type, **params):
if _type.containedTypeDesc is None:
raise AbsApi.Error('{0} has not been fixed'.format(_type.containedTypeName))
elif isinstance(_type.containedTypeDesc, AbsApi.BaseType):
res = self.translate_type(_type.containedTypeDesc)
else:
res = self.translate_type(_type.containedTypeDesc, **params)
if type(_type.parent) is AbsApi.Argument:
return 'const std::list<{0} > &'.format(res)
else:
return 'std::list<{0} >'.format(res)
@staticmethod
def translate_name(aName, **params):
if type(aName) is AbsApi.ClassName:
return CppTranslator.translate_class_name(aName, **params)
elif type(aName) is AbsApi.InterfaceName:
return CppTranslator.translate_class_name(aName, **params)
elif type(aName) is AbsApi.EnumName:
return CppTranslator.translate_enum_name(aName, **params)
elif type(aName) is AbsApi.EnumValueName:
return CppTranslator.translate_enum_value_name(aName, **params)
elif type(aName) is AbsApi.MethodName:
return CppTranslator.translate_method_name(aName, **params)
elif type(aName) is AbsApi.ArgName:
return CppTranslator.translate_argument_name(aName, **params)
elif type(aName) is AbsApi.NamespaceName:
return CppTranslator.translate_namespace_name(aName, **params)
elif type(aName) is AbsApi.PropertyName:
return CppTranslator.translate_property_name(aName, **params)
else:
CppTranslator.fail(aName)
@staticmethod
def translate_class_name(name, recursive=False, topAncestor=None):
if name.prev is None or not recursive or name.prev is topAncestor:
return name.to_camel_case()
else:
params = {'recursive': recursive, 'topAncestor': topAncestor}
return CppTranslator.translate_name(name.prev, **params) + '::' + name.to_camel_case()
@staticmethod
def translate_enum_name(name, recursive=False, topAncestor=None):
params = {'recursive': recursive, 'topAncestor': topAncestor}
return CppTranslator.translate_class_name(name, **params)
@staticmethod
def translate_enum_value_name(name, recursive=False, topAncestor=None):
params = {'recursive': recursive, 'topAncestor': topAncestor}
return CppTranslator.translate_enum_name(name.prev, **params) + name.to_camel_case()
@staticmethod
def translate_method_name(name, recursive=False, topAncestor=None):
translatedName = name.to_camel_case(lower=True)
if translatedName == 'new':
translatedName = '_new'
if name.prev is None or not recursive or name.prev is topAncestor:
return translatedName
else:
params = {'recursive': recursive, 'topAncestor': topAncestor}
return CppTranslator.translate_name(name.prev, **params) + '::' + translatedName
@staticmethod
def translate_namespace_name(name, recursive=False, topAncestor=None):
if name.prev is None or not recursive or name.prev is topAncestor:
return name.concatenate()
else:
params = {'recursive': recursive, 'topAncestor': topAncestor}
return CppTranslator.translate_namespace_name(name.prev, **params) + '::' + name.concatenate()
@staticmethod
def translate_argument_name(name):
return name.to_camel_case(lower=True)
@staticmethod
def translate_property_name(name):
CppTranslator.translate_argument_name(name)
@staticmethod
def fail(obj):
raise AbsApi.Error('Cannot translate {0} type'.format(type(obj)))
@ -594,7 +367,7 @@ class ClassHeader(object):
if include == 'enums':
self.includes['internal'].append({'name': include})
else:
className = AbsApi.ClassName()
className = metaname.ClassName()
className.from_snake_case(include)
self.priorDeclarations.append({'name': className.to_camel_case()})
@ -682,7 +455,7 @@ class GenWrapper(object):
self.parser = AbsApi.CParser(project)
self.parser.parse_all()
self.translator = CppTranslator()
self.renderer = pystache.Renderer()
self.renderer = pystache.Renderer()
self.mainHeader = MainHeader()
self.impl = ClassImpl()
@ -743,14 +516,14 @@ def main():
os.makedirs(includedir)
except OSError as e:
if e.errno != errno.EEXIST:
print("Cannot create '{0}' dircetory: {1}".format(includedir, e.strerror))
print("Cannot create '{0}' directory: {1}".format(includedir, e.strerror))
sys.exit(1)
try:
os.makedirs(srcdir)
except OSError as e:
if e.errno != errno.EEXIST:
print("Cannot create '{0}' dircetory: {1}".format(srcdir, e.strerror))
print("Cannot create '{0}' directory: {1}".format(srcdir, e.strerror))
sys.exit(1)
genwrapper = GenWrapper(includedir, srcdir, args.xmldir)

View file

@ -22,15 +22,17 @@ import sys
import pystache
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'tools'))
print(sys.path)
import genapixml as CApi
import abstractapi as AbsApi
import metadoc
import metaname
class CsharpTranslator(object):
def __init__(self):
self.ignore = []
self.docTranslator = metadoc.SandcastleCSharpTranslator()
self.docTranslator = metadoc.SandCastleTranslator('CSharp')
self.nameTranslator = metaname.Translator.get('CSharp')
self.langTranslator = AbsApi.Translator.get('CSharp')
def init_method_dict(self):
methodDict = {}
@ -52,114 +54,16 @@ class CsharpTranslator(object):
if length > 11 and name[:11] == 'IEnumerable':
return name[12:length-1]
return None
def is_generic(self, methodDict):
return not methodDict['is_string'] and not methodDict['is_bool'] and not methodDict['is_class'] and not methodDict['is_enum'] and methodDict['list_type'] == None
def translate_method_name(self, name, recursive=False, topAncestor=None):
translatedName = name.to_camel_case(lower=True)
if name.prev is None or not recursive or name.prev is topAncestor:
return translatedName
def translate_argument_name(self, name):
argname = name.to_camel_case(lower=True)
arg = argname
if argname == "params":
arg = "parameters"
elif argname == "event":
arg = "ev"
elif argname == "ref":
arg = "reference"
elif argname == "value":
arg = "val"
return arg
def translate_base_type(self, _type, isArg, dllImport=True):
if _type.name == 'void':
if _type.isref:
return 'IntPtr'
return 'void'
elif _type.name == 'status':
if dllImport:
return 'int'
else:
return 'void'
elif _type.name == 'boolean':
if dllImport:
res = 'int' # In C the bool_t is an integer
else:
res = 'bool'
elif _type.name == 'integer':
if _type.isUnsigned:
res = 'uint'
else:
res = 'int'
elif _type.name == 'string':
if dllImport:
if isArg:
return 'string'
else:
res = 'IntPtr' # Return as IntPtr and get string with Marshal.PtrToStringAnsi()
else:
return 'string'
elif _type.name == 'character':
if _type.isUnsigned:
res = 'byte'
else:
res = 'sbyte'
elif _type.name == 'time':
res = 'long' #TODO check
elif _type.name == 'size':
res = 'long' #TODO check
elif _type.name == 'floatant':
return 'float'
elif _type.name == 'string_array':
if dllImport or isArg:
return 'IntPtr'
else:
return 'IEnumerable<string>'
else:
raise AbsApi.Error('\'{0}\' is not a base abstract type'.format(_type.name))
return res
def is_linphone_type(self, _type, isArg, dllImport=True):
if type(_type) is AbsApi.ClassType:
return False if dllImport else True
elif type(_type) is AbsApi.EnumType:
return False if dllImport else True
def translate_type(self, _type, isArg, dllImport=True):
if type(_type) is AbsApi.EnumType:
if dllImport and isArg:
return 'int'
return _type.desc.name.to_camel_case()
elif type(_type) is AbsApi.ClassType:
return "IntPtr" if dllImport else _type.desc.name.to_camel_case()
elif type(_type) is AbsApi.BaseType:
return self.translate_base_type(_type, isArg, dllImport)
elif type(_type) is AbsApi.ListType:
if dllImport:
return 'IntPtr'
else:
if type(_type.containedTypeDesc) is AbsApi.BaseType:
if _type.containedTypeDesc.name == 'string':
return 'IEnumerable<string>'
else:
raise AbsApi.Error('translation of bctbx_list_t of basic C types is not supported')
elif type(_type.containedTypeDesc) is AbsApi.ClassType:
ptrType = _type.containedTypeDesc.desc.name.to_camel_case()
return 'IEnumerable<' + ptrType + '>'
else:
if _type.containedTypeDesc:
raise AbsApi.Error('translation of bctbx_list_t of enums')
else:
raise AbsApi.Error('translation of bctbx_list_t of unknow type !')
def translate_argument(self, arg, dllImport=True):
return '{0} {1}'.format(self.translate_type(arg.type, True, dllImport), self.translate_argument_name(arg.name))
def throws_exception(self, return_type):
if type(return_type) is AbsApi.BaseType:
if return_type.name == 'status':
@ -167,31 +71,31 @@ class CsharpTranslator(object):
return False
def translate_method(self, method, static=False, genImpl=True):
if method.name.to_snake_case(fullName=True) in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(method.name.to_snake_case(fullName=True)))
if method.name.to_c() in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(method.name.to_c()))
methodElems = {}
methodElems['return'] = self.translate_type(method.returnType, False)
methodElems['return'] = method.returnType.translate(self.langTranslator)
methodElems['name'] = method.name.to_c()
methodElems['params'] = '' if static else 'IntPtr thiz'
for arg in method.args:
if arg is not method.args[0] or not static:
methodElems['params'] += ', '
methodElems['params'] += self.translate_argument(arg)
methodElems['params'] += arg.translate(self.langTranslator)
methodDict = {}
methodDict['prototype'] = "static extern {return} {name}({params});".format(**methodElems)
methodDict['doc'] = self.docTranslator.translate(method.briefDescription)
methodDict['doc'] = method.briefDescription.translate(self.docTranslator, tagAsBrief=True)
methodDict['has_impl'] = genImpl
if genImpl:
methodDict['impl'] = {}
methodDict['impl']['static'] = 'static ' if static else ''
methodDict['impl']['exception'] = self.throws_exception(method.returnType)
methodDict['impl']['type'] = self.translate_type(method.returnType, False, False)
methodDict['impl']['name'] = method.name.to_camel_case()
methodDict['impl']['override'] = 'override ' if method.name.to_camel_case() == 'ToString' else ''
methodDict['impl']['type'] = method.returnType.translate(self.langTranslator, dllImport=False)
methodDict['impl']['name'] = method.name.translate(self.nameTranslator)
methodDict['impl']['override'] = 'override ' if method.name.translate(self.nameTranslator) == 'ToString' else ''
methodDict['impl']['return'] = '' if methodDict['impl']['type'] == "void" else 'return '
methodDict['impl']['c_name'] = method.name.to_c()
methodDict['impl']['nativePtr'] = '' if static else ('nativePtr, ' if len(method.args) > 0 else 'nativePtr')
@ -218,21 +122,21 @@ class CsharpTranslator(object):
methodDict['impl']['c_args'] += ', '
if self.is_linphone_type(arg.type, False, False):
if type(arg.type) is AbsApi.ClassType:
argname = self.translate_argument_name(arg.name)
argname = arg.name.translate(self.nameTranslator)
methodDict['impl']['c_args'] += argname + " != null ? " + argname + ".nativePtr : IntPtr.Zero"
else:
methodDict['impl']['c_args'] += '(int)' + self.translate_argument_name(arg.name)
elif self.translate_type(arg.type, False, False) == "bool":
methodDict['impl']['c_args'] += self.translate_argument_name(arg.name) + " ? 1 : 0"
elif self.get_class_array_type(self.translate_type(arg.type, False, False)) is not None:
listtype = self.get_class_array_type(self.translate_type(arg.type, False, False))
methodDict['impl']['c_args'] += '(int)' + arg.name.translate(self.nameTranslator)
elif arg.type.translate(self.langTranslator, dllImport=False) == "bool":
methodDict['impl']['c_args'] += arg.name.translate(self.nameTranslator) + " ? (char)1 : (char)0"
elif self.get_class_array_type(arg.type.translate(self.langTranslator, dllImport=False)) is not None:
listtype = self.get_class_array_type(arg.type.translate(self.langTranslator, dllImport=False))
if listtype == 'string':
methodDict['impl']['c_args'] += "StringArrayToBctbxList(" + self.translate_argument_name(arg.name) + ")"
methodDict['impl']['c_args'] += "StringArrayToBctbxList(" + arg.name.translate(self.nameTranslator) + ")"
else:
methodDict['impl']['c_args'] += "ObjectArrayToBctbxList<" + listtype + ">(" + self.translate_argument_name(arg.name) + ")"
methodDict['impl']['c_args'] += "ObjectArrayToBctbxList<" + listtype + ">(" + arg.name.translate(self.nameTranslator) + ")"
else:
methodDict['impl']['c_args'] += self.translate_argument_name(arg.name)
methodDict['impl']['args'] += self.translate_argument(arg, False)
methodDict['impl']['c_args'] += arg.name.translate(self.nameTranslator)
methodDict['impl']['args'] += arg.translate(self.langTranslator, dllImport=False)
return methodDict
@ -242,7 +146,7 @@ class CsharpTranslator(object):
methodDict = self.translate_method(prop, static, False)
methodDict['property_static'] = 'static ' if static else ''
methodDict['property_return'] = self.translate_type(prop.returnType, False, False)
methodDict['property_return'] = prop.returnType.translate(self.langTranslator, dllImport=False)
methodDict['property_name'] = (name[3:] if len(name) > 3 else 'Instance') if name[:3] == "Get" else name
methodDict['has_property'] = True
@ -273,7 +177,7 @@ class CsharpTranslator(object):
methodDict = self.translate_method(prop, static, False)
methodDict['property_static'] = 'static ' if static else ''
methodDict['property_return'] = self.translate_type(prop.args[0].type, True, False)
methodDict['property_return'] = prop.args[0].type.translate(self.langTranslator, dllImport=False)
methodDict['property_name'] = (name[3:] if len(name) > 3 else 'Instance') if name[:3] == "Set" else name
methodDict['has_property'] = True
@ -297,7 +201,7 @@ class CsharpTranslator(object):
return methodDict
def translate_property_getter_setter(self, getter, setter, name, static=False):
methodDict = self.translate_property_getter(getter, name, static)
methodDict = self.translate_property_getter(getter, name, static=static)
methodDictSet = self.translate_property_setter(setter, name, static)
protoElems = {}
@ -314,7 +218,7 @@ class CsharpTranslator(object):
def translate_property(self, prop):
res = []
name = prop.name.to_camel_case()
name = prop.name.translate(self.nameTranslator)
if prop.getter is not None:
if prop.setter is not None:
res.append(self.translate_property_getter_setter(prop.getter, prop.setter, name))
@ -331,7 +235,7 @@ class CsharpTranslator(object):
listenerDict = {}
c_name_setter = listenedClass.name.to_snake_case(fullName=True) + '_cbs_set_' + method.name.to_snake_case()[3:]
delegate_name_public = method.name.to_camel_case() + "Delegate"
delegate_name_public = method.name.translate(self.nameTranslator) + "Delegate"
delegate_name_private = delegate_name_public + "Private"
listenerDict['cb_setter'] = {}
listenerDict['cb_setter']['name'] = c_name_setter
@ -345,9 +249,9 @@ class CsharpTranslator(object):
listenerDict['delegate']['var_public'] = var_name_public
listenerDict['delegate']['var_private'] = var_name_private
listenerDict['delegate']['cb_name'] = method.name.to_snake_case()
listenerDict['delegate']['name'] = method.name.to_camel_case()
listenerDict['delegate']['name'] = method.name.translate(self.nameTranslator)
listenerDict['delegate']['interfaceClassName'] = listenedClass.name.to_camel_case()
listenerDict['delegate']['interfaceClassName'] = listenedClass.name.translate(self.nameTranslator)
listenerDict['delegate']['isSimpleListener'] = not listenedClass.multilistener
listenerDict['delegate']['isMultiListener'] = listenedClass.multilistener
@ -355,10 +259,10 @@ class CsharpTranslator(object):
listenerDict['delegate']['params_private'] = ""
listenerDict['delegate']['params'] = ""
for arg in method.args:
dllImportType = self.translate_type(arg.type, True, True)
normalType = self.translate_type(arg.type, True, False)
dllImportType = arg.type.translate(self.langTranslator, dllImport=True)
normalType = arg.type.translate(self.langTranslator, dllImport=False)
argName = self.translate_argument_name(arg.name)
argName = arg.name.translate(self.nameTranslator)
if arg != method.args[0]:
listenerDict['delegate']['params_public'] += ', '
listenerDict['delegate']['params_private'] += ', '
@ -369,9 +273,9 @@ class CsharpTranslator(object):
else:
if normalType == "bool":
listenerDict['delegate']['params'] += argName + " == 0"
elif self.is_linphone_type(arg.type, True, False) and type(arg.type) is AbsApi.ClassType:
elif self.is_linphone_type(arg.type, True, dllImport=False) and type(arg.type) is AbsApi.ClassType:
listenerDict['delegate']['params'] += "fromNativePtr<" + normalType + ">(" + argName + ")"
elif self.is_linphone_type(arg.type, True, False) and type(arg.type) is AbsApi.EnumType:
elif self.is_linphone_type(arg.type, True, dllImport=False) and type(arg.type) is AbsApi.EnumType:
listenerDict['delegate']['params'] += "(" + normalType + ")" + argName + ""
else:
raise("Error")
@ -439,15 +343,15 @@ class CsharpTranslator(object):
def translate_enum(self, enum):
enumDict = {}
enumDict['enumName'] = enum.name.to_camel_case()
enumDict['doc'] = self.docTranslator.translate(enum.briefDescription)
enumDict['enumName'] = enum.name.translate(self.nameTranslator)
enumDict['doc'] = enum.briefDescription.translate(self.docTranslator, tagAsBrief=True)
enumDict['values'] = []
i = 0
lastValue = None
for enumValue in enum.values:
for enumValue in enum.enumerators:
enumValDict = {}
enumValDict['name'] = enumValue.name.to_camel_case()
enumValDict['doc'] = self.docTranslator.translate(enumValue.briefDescription)
enumValDict['name'] = enumValue.name.translate(self.nameTranslator)
enumValDict['doc'] = enumValue.briefDescription.translate(self.docTranslator, tagAsBrief=True)
if type(enumValue.value) is int:
lastValue = enumValue.value
enumValDict['value'] = str(enumValue.value)
@ -464,21 +368,21 @@ class CsharpTranslator(object):
return enumDict
def translate_class(self, _class):
if _class.name.to_camel_case(fullName=True) in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(_class.name.to_camel_case(fullName=True)))
if _class.name.to_c() in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(_class.name.to_c()))
classDict = {}
classDict['className'] = _class.name.to_camel_case()
classDict['isLinphoneFactory'] = _class.name.to_camel_case() == "Factory"
classDict['className'] = _class.name.translate(self.nameTranslator)
classDict['isLinphoneFactory'] = classDict['className'] == "Factory"
classDict['isLinphoneCall'] = _class.name.to_camel_case() == "Call"
classDict['isLinphoneCore'] = _class.name.to_camel_case() == "Core"
classDict['doc'] = self.docTranslator.translate(_class.briefDescription)
classDict['doc'] = _class.briefDescription.translate(self.docTranslator, tagAsBrief=True)
classDict['dllImports'] = []
islistenable = _class.listenerInterface is not None
ismonolistenable = (islistenable and not _class.multilistener)
if islistenable:
listenerName = _class.listenerInterface.name.to_camel_case()
listenerName = _class.listenerInterface.name.translate(self.nameTranslator)
if ismonolistenable:
classDict['dllImports'].append(self.generate_getter_for_listener_callbacks(_class, listenerName))
else:
@ -488,7 +392,7 @@ class CsharpTranslator(object):
for method in _class.classMethods:
try:
if 'get' in method.name.to_word_list():
methodDict = self.translate_property_getter(method, method.name.to_camel_case(), True)
methodDict = self.translate_property_getter(method, method.name.translate(self.nameTranslator), True)
#The following doesn't work because there a at least one method that has both getter and setter,
#and because it doesn't do both of them at once, property is declared twice
#elif 'set' in method.name.to_word_list():
@ -497,29 +401,29 @@ class CsharpTranslator(object):
methodDict = self.translate_method(method, static=True, genImpl=True)
classDict['dllImports'].append(methodDict)
except AbsApi.Error as e:
print('Could not translate {0}: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0]))
print('Could not translate {0}: {1}'.format(method.name.to_c(), e.args[0]))
for prop in _class.properties:
try:
classDict['dllImports'] += self.translate_property(prop)
except AbsApi.Error as e:
print('error while translating {0} property: {1}'.format(prop.name.to_snake_case(), e.args[0]))
print('error while translating {0} property: {1}'.format(prop.name.to_c(), e.args[0]))
for method in _class.instanceMethods:
try:
methodDict = self.translate_method(method, static=False, genImpl=True)
classDict['dllImports'].append(methodDict)
except AbsApi.Error as e:
print('Could not translate {0}: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0]))
print('Could not translate {0}: {1}'.format(method.name.to_c(), e.args[0]))
return classDict
def translate_interface(self, interface):
if interface.name.to_camel_case(fullName=True) in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(interface.name.to_camel_case(fullName=True)))
if interface.name.to_c() in self.ignore:
raise AbsApi.Error('{0} has been escaped'.format(interface.name.to_c()))
interfaceDict = {}
interfaceDict['interfaceName'] = interface.name.to_camel_case()
interfaceDict['interfaceName'] = interface.name.translate(self.nameTranslator)
interfaceDict['methods'] = []
for method in interface.methods:
interfaceDict['methods'].append(self.translate_listener(interface, method))
@ -615,7 +519,7 @@ def main():
impl = InterfaceImpl(_class, translator)
interfaces.append(impl)
except AbsApi.Error as e:
print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0]))
print('Could not translate {0}: {1}'.format(_class.name.to_c(), e.args[0]))
wrapper = WrapperImpl(enums, interfaces, classes)
render(renderer, wrapper, args.outputdir + "/" + args.outputfile)

View file

@ -484,7 +484,7 @@ namespace Linphone
{{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}}
{{/is_string}}
{{#is_bool}}
{{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value ? 1 : 0);
{{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value ? (char)1 : (char)0);
{{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}}
{{/is_bool}}
{{#is_class}}
@ -525,7 +525,7 @@ namespace Linphone
return Marshal.PtrToStringAnsi(stringPtr);
{{/is_string}}
{{#is_bool}}
{{return}}{{c_name}}({{nativePtr}}{{c_args}}) == 0 ? false : true;
{{return}}{{c_name}}({{nativePtr}}{{c_args}}) == (char)0 ? false : true;
{{/is_bool}}
{{#is_class}}
IntPtr ptr = {{c_name}}({{nativePtr}}{{c_args}});

View file

@ -90,7 +90,7 @@ class JavaTranslator(object):
self.jni_package += directory + '_'
self.jni_path += directory + '/'
self.docTranslator = metadoc.SandcastleJavaTranslator()
self.docTranslator = metadoc.JavaDocTranslator()
def throws_exception(self, _type):
if not self.exceptions:
@ -353,7 +353,7 @@ class JavaTranslator(object):
methodDict['native_params_impl'] += self.translate_argument_name(arg.name)
methodDict['deprecated'] = _method.deprecated
methodDict['doc'] = self.docTranslator.translate(_method.briefDescription) if _method.briefDescription is not None else None
methodDict['doc'] = _method.briefDescription.translate(self.docTranslator) if _method.briefDescription is not None else None
return methodDict
@ -449,7 +449,7 @@ class JavaTranslator(object):
classDict['isLinphoneFactory'] = _class.name.to_camel_case() == "Factory"
classDict['isLinphoneCore'] = _class.name.to_camel_case() == "Core"
classDict['doc'] = self.docTranslator.translate(_class.briefDescription) if _class.briefDescription is not None else None
classDict['doc'] = _class.briefDescription.translate(self.docTranslator) if _class.briefDescription is not None else None
for _property in _class.properties:
try:
@ -572,7 +572,7 @@ class JavaTranslator(object):
'jniMethods': [],
}
interfaceDict['doc'] = self.docTranslator.translate(_class.briefDescription)
interfaceDict['doc'] = _class.briefDescription.translate(self.docTranslator)
for method in _class.methods:
interfaceDict['methods'].append(self.translate_method(method))
@ -580,23 +580,23 @@ class JavaTranslator(object):
return interfaceDict
def translate_enum(self, _class):
def translate_enum(self, enum):
enumDict = {
'jniMethods': [],
}
enumDict['name'] = _class.name.to_camel_case()
enumDict['doc'] = self.docTranslator.translate(_class.briefDescription)
enumDict['name'] = enum.name.to_camel_case()
enumDict['doc'] = enum.briefDescription.translate(self.docTranslator)
enumDict['values'] = []
i = 0
lastValue = None
enumDict['jniPath'] = self.jni_path
for enumValue in _class.values:
for enumValue in enum.enumerators:
enumValDict = {}
enumValDict['name'] = enumValue.name.to_camel_case()
enumValDict['doc'] = self.docTranslator.translate(enumValue.briefDescription)
enumValDict['doc'] = enumValue.briefDescription.translate(self.docTranslator)
if type(enumValue.value) is int:
lastValue = enumValue.value
enumValDict['value'] = str(enumValue.value)
@ -609,7 +609,7 @@ class JavaTranslator(object):
else:
enumValDict['value'] = i
i += 1
enumValDict['commarorsemicolon'] = ';' if i == len(_class.values) else ','
enumValDict['commarorsemicolon'] = ';' if i == len(enum.enumerators) else ','
enumDict['values'].append(enumValDict)
return enumDict
@ -805,22 +805,22 @@ class GenWrapper(object):
if _enum[1] is not None:
self.render_java_enum(_enum[1])
for name, value in self.enums.iteritems():
for name, value in self.enums.items():
self.jni.add_enum(value)
if name in ENUMS_LIST:
className = ENUMS_LIST[name]
print 'Enum ' + name + ' belongs to class ' + className
print('Enum ' + name + ' belongs to class ' + className)
self.classes[className].add_enum(value)
self.enums_to_remove.append(name)
for enum in self.enums_to_remove:
self.enums.pop(enum, None)
for name, value in self.enums.iteritems():
for name, value in self.enums.items():
self.render(value, self.javadir + '/' + value.filename)
for name, value in self.interfaces.iteritems():
for name, value in self.interfaces.items():
self.render(value, self.javadir + '/' + value.filename)
for name, value in self.classes.iteritems():
for name, value in self.classes.items():
self.render(value, self.javadir + '/' + value.filename)
self.jni.add_object(value)