From 5cf9e549f30f8f195388013ddce3ed04104b1eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 16 Nov 2017 17:21:04 +0100 Subject: [PATCH] Partial merge from 'master' about wrapper and documentation generation scripts --- CMakeLists.txt | 2 +- coreapi/help/doc/CMakeLists.txt | 33 +- coreapi/help/doc/doxygen/CMakeLists.txt | 36 +- coreapi/help/doc/doxygen/doxygen.dox | 5 + coreapi/help/doc/sphinx/CMakeLists.txt | 61 ++ coreapi/help/doc/sphinx/class_page.mustache | 151 +++++ coreapi/help/doc/sphinx/conf.py.in | 158 +++++ coreapi/help/doc/sphinx/enums_page.mustache | 30 + coreapi/help/doc/sphinx/gendoc.py | 377 +++++++++++ coreapi/help/doc/sphinx/index_page.mustache | 19 + coreapi/help/doc/sphinx/source/index.rst | 24 + include/linphone/types.h | 4 + tools/abstractapi.py | 675 ++++++++++++++------ tools/genapixml.py | 17 +- tools/metadoc.py | 589 +++++++++++++++-- tools/metaname.py | 340 ++++++++++ wrappers/cpp/CMakeLists.txt | 1 + wrappers/cpp/class_header.mustache | 44 +- wrappers/cpp/class_impl.mustache | 1 + wrappers/cpp/enums_header.mustache | 4 +- wrappers/cpp/genwrapper.py | 339 ++-------- wrappers/csharp/genwrapper.py | 204 ++---- wrappers/csharp/wrapper_impl.mustache | 4 +- wrappers/java/genwrapper.py | 30 +- 24 files changed, 2379 insertions(+), 769 deletions(-) create mode 100644 coreapi/help/doc/sphinx/CMakeLists.txt create mode 100644 coreapi/help/doc/sphinx/class_page.mustache create mode 100644 coreapi/help/doc/sphinx/conf.py.in create mode 100644 coreapi/help/doc/sphinx/enums_page.mustache create mode 100755 coreapi/help/doc/sphinx/gendoc.py create mode 100644 coreapi/help/doc/sphinx/index_page.mustache create mode 100644 coreapi/help/doc/sphinx/source/index.rst create mode 100644 tools/metaname.py diff --git a/CMakeLists.txt b/CMakeLists.txt index c7d648143..8c964a8f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/coreapi/help/doc/CMakeLists.txt b/coreapi/help/doc/CMakeLists.txt index c4d5481de..d90be026e 100644 --- a/coreapi/help/doc/CMakeLists.txt +++ b/coreapi/help/doc/CMakeLists.txt @@ -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() diff --git a/coreapi/help/doc/doxygen/CMakeLists.txt b/coreapi/help/doc/doxygen/CMakeLists.txt index 8d066161b..ef96a784f 100644 --- a/coreapi/help/doc/doxygen/CMakeLists.txt +++ b/coreapi/help/doc/doxygen/CMakeLists.txt @@ -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() diff --git a/coreapi/help/doc/doxygen/doxygen.dox b/coreapi/help/doc/doxygen/doxygen.dox index 38002841c..4158c7a70 100644 --- a/coreapi/help/doc/doxygen/doxygen.dox +++ b/coreapi/help/doc/doxygen/doxygen.dox @@ -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. diff --git a/coreapi/help/doc/sphinx/CMakeLists.txt b/coreapi/help/doc/sphinx/CMakeLists.txt new file mode 100644 index 000000000..15abb7480 --- /dev/null +++ b/coreapi/help/doc/sphinx/CMakeLists.txt @@ -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() diff --git a/coreapi/help/doc/sphinx/class_page.mustache b/coreapi/help/doc/sphinx/class_page.mustache new file mode 100644 index 000000000..f835c5c3e --- /dev/null +++ b/coreapi/help/doc/sphinx/class_page.mustache @@ -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}} diff --git a/coreapi/help/doc/sphinx/conf.py.in b/coreapi/help/doc/sphinx/conf.py.in new file mode 100644 index 000000000..378b84244 --- /dev/null +++ b/coreapi/help/doc/sphinx/conf.py.in @@ -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'), +] + + + diff --git a/coreapi/help/doc/sphinx/enums_page.mustache b/coreapi/help/doc/sphinx/enums_page.mustache new file mode 100644 index 000000000..fd013094a --- /dev/null +++ b/coreapi/help/doc/sphinx/enums_page.mustache @@ -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}} diff --git a/coreapi/help/doc/sphinx/gendoc.py b/coreapi/help/doc/sphinx/gendoc.py new file mode 100755 index 000000000..800d57bc6 --- /dev/null +++ b/coreapi/help/doc/sphinx/gendoc.py @@ -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 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) + diff --git a/coreapi/help/doc/sphinx/index_page.mustache b/coreapi/help/doc/sphinx/index_page.mustache new file mode 100644 index 000000000..2fa38ec83 --- /dev/null +++ b/coreapi/help/doc/sphinx/index_page.mustache @@ -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 diff --git a/coreapi/help/doc/sphinx/source/index.rst b/coreapi/help/doc/sphinx/source/index.rst new file mode 100644 index 000000000..530adfcc3 --- /dev/null +++ b/coreapi/help/doc/sphinx/source/index.rst @@ -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` + diff --git a/include/linphone/types.h b/include/linphone/types.h index c1c46e835..4df0f956c 100644 --- a/include/linphone/types.h +++ b/include/linphone/types.h @@ -1055,6 +1055,10 @@ typedef enum _LinphoneTransportType { */ typedef struct _LinphoneTunnel LinphoneTunnel; +/** + * @brief Tunnel settings. + * @ingroup tunnel + */ typedef struct _LinphoneTunnelConfig LinphoneTunnelConfig; /** diff --git a/tools/abstractapi.py b/tools/abstractapi.py index 6ba861a96..9f7a4d451 100644 --- a/tools/abstractapi.py +++ b/tools/abstractapi.py @@ -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 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' + 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' + 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) diff --git a/tools/genapixml.py b/tools/genapixml.py index 8666c0574..29a0dedf8 100755 --- a/tools/genapixml.py +++ b/tools/genapixml.py @@ -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') diff --git a/tools/metadoc.py b/tools/metadoc.py index fd985a11f..e2dcdc091 100644 --- a/tools/metadoc.py +++ b/tools/metadoc.py @@ -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, '') lines.append('') - - -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 ''.format(refStr) diff --git a/tools/metaname.py b/tools/metaname.py new file mode 100644 index 000000000..6a76e6f71 --- /dev/null +++ b/tools/metaname.py @@ -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 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() diff --git a/wrappers/cpp/CMakeLists.txt b/wrappers/cpp/CMakeLists.txt index 32dc090e6..5fedcd2b0 100644 --- a/wrappers/cpp/CMakeLists.txt +++ b/wrappers/cpp/CMakeLists.txt @@ -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 diff --git a/wrappers/cpp/class_header.mustache b/wrappers/cpp/class_header.mustache index ae8f3394c..71bd810fb 100644 --- a/wrappers/cpp/class_header.mustache +++ b/wrappers/cpp/class_header.mustache @@ -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}} diff --git a/wrappers/cpp/class_impl.mustache b/wrappers/cpp/class_impl.mustache index ec0c55c07..8c3a83e70 100644 --- a/wrappers/cpp/class_impl.mustache +++ b/wrappers/cpp/class_impl.mustache @@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include #include +#include #include "linphone++/linphone.hh" #include "tools.hh" diff --git a/wrappers/cpp/enums_header.mustache b/wrappers/cpp/enums_header.mustache index 1eff69b46..648a19e77 100644 --- a/wrappers/cpp/enums_header.mustache +++ b/wrappers/cpp/enums_header.mustache @@ -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}} diff --git a/wrappers/cpp/genwrapper.py b/wrappers/cpp/genwrapper.py index d5f0cb0ba..1ec10effc 100755 --- a/wrappers/cpp/genwrapper.py +++ b/wrappers/cpp/genwrapper.py @@ -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' - 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) diff --git a/wrappers/csharp/genwrapper.py b/wrappers/csharp/genwrapper.py index f83c68361..a8b9e268b 100644 --- a/wrappers/csharp/genwrapper.py +++ b/wrappers/csharp/genwrapper.py @@ -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' - 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' - 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) diff --git a/wrappers/csharp/wrapper_impl.mustache b/wrappers/csharp/wrapper_impl.mustache index f66c504fe..8837b4475 100644 --- a/wrappers/csharp/wrapper_impl.mustache +++ b/wrappers/csharp/wrapper_impl.mustache @@ -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}}); diff --git a/wrappers/java/genwrapper.py b/wrappers/java/genwrapper.py index a86e3995b..1299fa367 100644 --- a/wrappers/java/genwrapper.py +++ b/wrappers/java/genwrapper.py @@ -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)