diff options
Diffstat (limited to 'xbmc/interfaces/python/PythonTools.groovy')
-rw-r--r-- | xbmc/interfaces/python/PythonTools.groovy | 140 |
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 + } +} |