summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/python/PythonTools.groovy
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/interfaces/python/PythonTools.groovy')
-rw-r--r--xbmc/interfaces/python/PythonTools.groovy140
1 files changed, 140 insertions, 0 deletions
diff --git a/xbmc/interfaces/python/PythonTools.groovy b/xbmc/interfaces/python/PythonTools.groovy
new file mode 100644
index 0000000..e41db6c
--- /dev/null
+++ b/xbmc/interfaces/python/PythonTools.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+import Helper
+import SwigTypeParser
+
+public class PythonTools
+{
+ /**
+ * This array defines a mapping of the api spec type to the python parse format character.
+ * By default, if a lookup here results in 'null' then the format char is 'O'
+ */
+ private static Map ltypeToFormatChar = [
+ 'p.char':"s", bool:"b",
+ int:"i", 'unsigned int' : 'I',
+ long:"l", 'unsigned long' : 'k',
+ 'double':"d", 'float':"f",
+ 'long long' : "L"
+ ]
+
+ /**
+ * if the parameter can be directly read from python then its type should be in the ltypeToFormatChar
+ * otherwise we need an intermediate pyobject
+ */
+ public static boolean parameterCanBeUsedDirectly(Node param) { return ltypeToFormatChar[SwigTypeParser.convertTypeToLTypeForParam(param.@type)] != null }
+
+ /**
+ * This method will take the parameter list from the method node passed
+ * and will convert it to a Python argument string for PyArg_ParseTupleAndKeywords
+ */
+ public static String makeFormatStringFromParameters(Node method)
+ {
+ if (!method)
+ return ''
+ List params = method.parm
+ String format = ""
+ boolean previousDefaulted = false
+ params.eachWithIndex { param, i ->
+ String defaultValue = param.@value
+ String paramtype = SwigTypeParser.convertTypeToLTypeForParam(param.@type)
+ String curFormat = ltypeToFormatChar[paramtype];
+ if (curFormat == null) // then we will assume it's an object
+ curFormat = "O";
+
+ if (defaultValue != null && !previousDefaulted)
+ {
+ format +="|"
+ previousDefaulted = true
+ }
+ format += curFormat
+ }
+ return format;
+ }
+
+ /**
+ * This method gets the FULL class name as a variable including the
+ * namespace. If converts all of the '::' references to '_' so
+ * that the result can be used in part, or in whole, as a variable name
+ */
+ public static String getClassNameAsVariable(Node clazz) { return Helper.findFullClassName(clazz).replaceAll('::','_') }
+
+ public static String getPyMethodName(Node method, MethodType methodType)
+ {
+ String clazz = Helper.findFullClassName(method)?.replaceAll('::','_')
+
+ // if we're not in a class then this must be a method node
+ assert (clazz != null || methodType == MethodType.method), 'Cannot use a non-class function as a constructor or destructor ' + method
+
+ // it's ok to pass a 'class' node if the methodType is either constructor or destructor
+ assert (method.name() != 'class' || (methodType == MethodType.constructor || methodType == MethodType.destructor))
+
+ // if this is a constructor node then the methodtype best reflect that
+ assert (method.name() != 'constructor' || methodType == MethodType.constructor), 'Cannot use a constructor node and not identify the type as a constructor' + method
+
+ // if this is a destructor node then the methodtype best reflect that
+ assert (method.name() != 'destructor' || methodType == MethodType.destructor), 'Cannot use a destructor node and not identify the type as a destructor' + method
+
+ if (clazz == null)
+ return method.@sym_name
+
+ if (methodType == MethodType.constructor)
+ return clazz + "_New"
+
+ if (methodType == MethodType.destructor)
+ return clazz + "_Dealloc"
+
+ if (method.@name.startsWith("operator "))
+ {
+ if ("[]" == method.@name.substring(9))
+ return clazz + "_operatorIndex_"
+
+ if ("()" == method.@name.substring(9))
+ return clazz + "_callable_"
+ }
+
+ return clazz + "_" + method.@sym_name;
+ }
+
+ public static String makeDocString(Node docnode)
+ {
+ if (docnode?.name() != 'doc')
+ throw new RuntimeException("Invalid doc Node passed to PythonTools.makeDocString (" + docnode + ")")
+
+ String[] lines = (docnode.@value).split(Helper.newline)
+ def ret = ''
+ lines.eachWithIndex { val, index ->
+ val = ((val =~ /\\n/).replaceAll('')) // remove extraneous \n's
+ val = val.replaceAll("\\\\","\\\\\\\\") // escape backslash
+ val = ((val =~ /\"/).replaceAll("\\\\\"")) // escape quotes
+ ret += ('"' + val + '\\n"' + (index != lines.length - 1 ? Helper.newline : ''))
+ }
+
+ return ret
+ }
+
+ public static Node findValidBaseClass(Node clazz, Node module, boolean warn = false)
+ {
+ // I need to find the base type if there is a known class with it
+ assert clazz.baselist.size() < 2, "${clazz} has multiple baselists - need to write code to separate out the public one."
+ String baseclass = 'NULL'
+ List knownbases = []
+ if (clazz.baselist)
+ {
+ if (clazz.baselist[0].base) clazz.baselist[0].base.each {
+ Node baseclassnode = Helper.findClassNodeByName(module,it.@name,clazz)
+ if (baseclassnode) knownbases.add(baseclassnode)
+ else if (warn && !Helper.isKnownBaseType(it.@name,clazz))
+ System.out.println("WARNING: the base class ${it.@name} for ${Helper.findFullClassName(clazz)} is unrecognized within ${module.@name}.")
+ }
+ }
+ assert knownbases.size() < 2,
+ "The class ${Helper.findFullClassName(clazz)} has too many known base classes. Multiple inheritance isn't supported in the code generator. Please \"#ifdef SWIG\" out all but one."
+ return knownbases.size() > 0 ? knownbases[0] : null
+ }
+}