aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJim Carroll <thecarrolls@jiminger.com>2012-09-09 17:15:11 -0400
committerJim Carroll <thecarrolls@jiminger.com>2012-09-09 17:15:11 -0400
commit7850f2e6881107ee4efb705b1c675599495df70c (patch)
treee96598d6a75cb1336fecd1bcbad7cbf54f73e260 /tools
parentcb2d3015c739ae05d3d86025edad2b1b995640c4 (diff)
Add Groovy based codegenerator to tools.
Diffstat (limited to 'tools')
-rw-r--r--tools/codegenerator/GenerateSWIGBindings.bat33
-rw-r--r--tools/codegenerator/Generator.groovy75
-rw-r--r--tools/codegenerator/Helper.groovy756
-rw-r--r--tools/codegenerator/SwigTypeParser.groovy552
-rw-r--r--tools/codegenerator/example/AddonModuleXbmc.i24
-rw-r--r--tools/codegenerator/example/Example-lval-rval.template18
-rw-r--r--tools/codegenerator/example/Example.call.template48
-rw-r--r--tools/codegenerator/example/Example.intypemap.template31
-rw-r--r--tools/codegenerator/example/Example.returns.template42
-rw-r--r--tools/codegenerator/example/native/ModuleXbmc.h1
10 files changed, 1580 insertions, 0 deletions
diff --git a/tools/codegenerator/GenerateSWIGBindings.bat b/tools/codegenerator/GenerateSWIGBindings.bat
new file mode 100644
index 0000000000..35f1ffb40e
--- /dev/null
+++ b/tools/codegenerator/GenerateSWIGBindings.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+
+SET cur_dir=%CD%
+
+SET base_dir=%cur_dir%\..\..
+SET groovy_dir=%base_dir%\lib\groovy
+SET generator_dir=%base_dir%\tools\codegenerator
+SET bin_dir=%cur_dir%\..\BuildDependencies\bin
+
+rem go into xbmc/interfaces/python
+cd %1\..\python
+
+SET python_dir=%CD%
+SET python_generated_dir=%python_dir%\generated
+SET doxygen_dir=%python_generated_dir%\doxygenxml
+SET swig_dir=%python_dir%\..\swig
+
+rem make sure all necessary directories exist and delete any old generated files
+IF NOT EXIST %python_generated_dir% md %python_generated_dir%
+IF EXIST %python_generated_dir%\%2.xml del %python_generated_dir%\%2.xml
+IF EXIST %python_generated_dir%\%2.cpp del %python_generated_dir%\%2.cpp
+IF NOT EXIST %doxygen_dir% md %doxygen_dir%
+
+rem run doxygen
+%bin_dir%\doxygen\doxygen.exe > NUL 2>&1
+
+rem run swig to generate the XML used by groovy to generate the python bindings
+%bin_dir%\swig\swig.exe -w401 -c++ -outdir %python_generated_dir% -o %python_generated_dir%\%2.xml -xml -I"%base_Dir%\xbmc" -xmllang python %swig_dir%\%2.i
+rem run groovy to generate the python bindings
+java.exe -cp "%groovy_dir%\groovy-all-1.8.4.jar;%groovy_dir%\commons-lang-2.6.jar;%generator_dir%;%python_dir%" groovy.ui.GroovyMain %generator_dir%\Generator.groovy %python_generated_dir%\%2.xml %python_dir%\PythonSwig.cpp.template %python_generated_dir%\%2.cpp %doxygen_dir%
+
+rem go back to the initial directory
+cd %cur_dir% \ No newline at end of file
diff --git a/tools/codegenerator/Generator.groovy b/tools/codegenerator/Generator.groovy
new file mode 100644
index 0000000000..c7dc88a642
--- /dev/null
+++ b/tools/codegenerator/Generator.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2005-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+import groovy.util.Node
+import groovy.text.SimpleTemplateEngine
+import groovy.xml.XmlUtil
+
+import Helper
+
+def usage()
+{
+ println "java/groovy -cp [...] " + getClass().getName() + " [-verbose] moduleSpecFile templateFile outputFile [doxygenoutputdir]";
+ System.exit 1
+}
+
+def verbose = false;
+
+newargs = []
+
+println args
+
+args.each {
+ if (it == '-verbose' || it == '--verbose' || it == '-v')
+ verbose = true
+ else
+ newargs.add(it)
+}
+
+if (newargs.size() != 3 && newargs.size() != 4)
+ usage()
+
+// set the doxygen xml directory on the Helper assuming the output file will be placed
+// in the same place the doxygen subdirectory is placed
+if (newargs.size() > 3)
+ Helper.setDoxygenXmlDir(new File(newargs[3]))
+
+File moduleSpec = new File(newargs[0])
+assert moduleSpec.exists() && moduleSpec.isFile(), 'Cannot locate the spec file "' + moduleSpec.getCanonicalPath() + '."'
+
+spec = [ 'module' : Helper.transformSwigXml(new XmlParser().parse(moduleSpec)) ]
+
+if (verbose)
+ println XmlUtil.serialize(spec['module'])
+
+File templateFile = new File(newargs[1])
+assert templateFile.exists() && templateFile.isFile(), 'Cannot locate the template file "' + templateFile.getCanonicalPath() + '."'
+
+te = new SimpleTemplateEngine()
+println 'Processing "' + templateFile + '" using the module specification for module "' + moduleSpec + '"'
+if (verbose) te.setVerbose(true)
+template = te.createTemplate(templateFile).make(spec)
+String output = template.toString()
+if (verbose) println output
+
+println 'writing'
+new File(newargs[2]).write output
+
diff --git a/tools/codegenerator/Helper.groovy b/tools/codegenerator/Helper.groovy
new file mode 100644
index 0000000000..7e54697770
--- /dev/null
+++ b/tools/codegenerator/Helper.groovy
@@ -0,0 +1,756 @@
+/*
+ * Copyright (C) 2005-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+import groovy.xml.XmlUtil
+import org.apache.commons.lang.StringEscapeUtils
+
+import groovy.text.SimpleTemplateEngine
+import groovy.text.SimpleTemplateEngine
+import java.util.regex.Pattern
+
+/**
+ * This class contains a series of helper methods for parsing a xbmc addon spec xml file. It is intended to be
+ * used from a bindings template.
+ *
+ * @author jim
+ *
+ */
+public class Helper
+{
+ private static List classes
+ private static Map outTypemap = [:]
+ private static def defaultOutTypeConversion = null
+ private static Map inTypemap = [:]
+ private static def defaultInTypeConversion = null
+ private static def doxygenXmlDir = null
+ public static String newline = System.getProperty("line.separator");
+
+ /**
+ * In order to use any of the typemap helper features, the Helper class needs to be initialized with
+ * this information.
+ * @param pclasses is the list of all class nodes from the module
+ * @param poutTypemap is the typemap table for output return values to the scripting language
+ * @param defaultOutTypemap is the default typemap to use when the type conversion is unknown
+ * @param pinTypemap is the typemap table for input parameters from the scripting language
+ * @param defaultInTypemap is the default typemap for the input parameters from the scripting language
+ */
+ public static void setup(List pclasses, Map poutTypemap, def defaultOutTypemap,
+ Map pinTypemap, def defaultInTypemap)
+ {
+ classes = pclasses ? pclasses : []
+ if (poutTypemap) outTypemap.putAll(poutTypemap)
+ if (defaultOutTypemap) defaultOutTypeConversion = defaultOutTypemap
+ if (pinTypemap) inTypemap.putAll(pinTypemap)
+ if (defaultInTypemap) defaultInTypeConversion = defaultInTypemap
+ }
+
+ public static class Sequence
+ {
+ private long cur = 0;
+
+ public long increment() { return ++cur }
+ }
+
+ private static ThreadLocal<Sequence> curSequence = new ThreadLocal<Sequence>();
+
+ public static void setDoxygenXmlDir(File dir) { doxygenXmlDir = dir }
+
+ private static String retrieveDocStringFromDoxygen(Node methodOrClass)
+ {
+ if (doxygenXmlDir == null)
+ return null
+
+ Node doc = null
+ def ret = ''
+
+ // make the class name or namespave
+ String doxygenId = findFullClassName(methodOrClass,'_1_1')
+ boolean isInClass = doxygenId != null
+ if (!doxygenId)
+ doxygenId = findNamespace(methodOrClass,'_1_1',false)
+ doxygenId = (isInClass ? 'class' : 'namespace') + doxygenId
+
+ String doxygenFilename = doxygenId + '.xml'
+ File doxygenXmlFile = new File(doxygenXmlDir,doxygenFilename)
+ if (! doxygenXmlFile.exists())
+ {
+ System.out.println("WARNING: Cannot find doxygen file for ${methodOrClass.toString()} which should be \"${doxygenXmlFile}\"")
+ return null
+ }
+
+ Node docspec = (new XmlParser().parse(doxygenXmlFile))
+ if (methodOrClass.name() == 'class')
+ doc = docspec.compounddef[0].detaileddescription[0]
+ else // it's a method of some sort ... or it better be
+ {
+ Node memberdef = docspec.depthFirst().find {
+ return ((it.name() == 'memberdef' && it.@kind == 'function' && it.@id.startsWith(doxygenId)) &&
+ (it.name != null && it.name.text().trim() == methodOrClass.@sym_name))
+ }
+
+ doc = memberdef != null ? memberdef.detaileddescription[0] : null
+ }
+
+ if (doc != null)
+ {
+ def indent = ' '
+ def curIndent = ''
+ def prevIndent = ''
+
+ def handleDoc
+ handleDoc = {
+ if (it instanceof String)
+ ret += it
+ else // it's a Node
+ {
+ if (it.name() == 'detaileddescription')
+ it.children().each handleDoc
+ else if (it.name() == 'para')
+ {
+ it.children().each handleDoc
+ ret += (it.parent()?.name() == 'listitem') ? newline : (newline + newline)
+ }
+ else if (it.name() == 'ref' || it.name() == "ulink")
+ ret += (it.text() + ' ')
+ else if (it.name() == 'verbatim')
+ ret += it.text().denormalize()
+ else if (it.name() == 'itemizedlist')
+ {
+ ret += newline
+ prevIndent = curIndent
+ curIndent += indent
+ it.children().each handleDoc
+ curIndent = prevIndent
+ }
+ else if (it.name() == 'listitem')
+ {
+ ret += (curIndent + '- ')
+ it.children().each handleDoc
+ }
+ else if (it.name() == 'linebreak')
+ ret += newline
+ else if (it.name() == 'ndash')
+ ret += "--"
+ else
+ System.out.println("WARNING: Cannot parse the following as part of the doxygen processing:" + XmlUtil.serialize(it))
+ }
+ }
+
+ doc.children().each handleDoc
+ }
+
+ return ret.denormalize()
+ }
+
+ /**
+ * <p>This method uses the previously set outTypemap and defaultOutTypemap to produce the chunk of code
+ * that will be used to return the method invocation result to the scripting language. For example, in
+ * python, if the return type from the method is a long, then the OutConversion could look something like:</p>
+ * <code>
+ * result = PyInt_FromLong(thingReturnedFromMethod);
+ * </code>
+ * <p>This could have resulted from a mini-template stored as the way to handle 'long's in the outTypemap:</p>
+ * <code>
+ * ${result} = PyInt_FromLong(${api});
+ * </code>
+ * @param apiType - is the Swig typecode that describes the return type from the native method
+ * @param method - is the node from the module xml that contains the method description
+ * @return the code chunk as a string ready to be placed into the generated code.
+ */
+ public static String getOutConversion(String apiType, String apiName, Node method, Map overrideBindings = null, boolean recurse = true)
+ {
+ def convertTemplate = outTypemap[apiType]
+
+ // String apiLType = SwigTypeParser.convertTypeToLType(apiType)
+ // if (convertTemplate == null) convertTemplate = outTypemap[apiLType]
+
+ // is the returns a pointer to a known class
+ Node classNode = null
+ if (convertTemplate == null && apiType.startsWith('p.'))
+ {
+ classNode = findClassNodeByName(parents(method)[0], SwigTypeParser.getBaseType(apiType),method)
+ if (classNode) convertTemplate = defaultOutTypeConversion
+ }
+
+ if (convertTemplate == null)
+ {
+ // Look for Pattern for keys that might fit
+ convertTemplate = outTypemap.find({ key, value -> (key instanceof Pattern && key.matcher(apiType).matches()) })?.value
+ }
+
+ if (!convertTemplate)
+ {
+ if (recurse)
+ return getOutConversion(SwigTypeParser.SwigType_ltype(apiType),apiName,method,overrideBindings,false)
+ else
+ throw new RuntimeException("WARNING: Cannot convert the return value of swig type ${apiType} for the call ${Helper.findFullClassName(method) + '::' + Helper.callingName(method)}")
+ }
+
+ boolean seqSetHere = false
+ Sequence seq = curSequence.get()
+ if (seq == null)
+ {
+ seqSetHere = true
+ seq = new Sequence()
+ curSequence.set(seq)
+ }
+
+ Map bindings = ['result' : apiName, 'api' : 'apiResult', 'type' : "${apiType}",
+ 'method' : method, 'helper' : Helper.class,
+ 'swigTypeParser' : SwigTypeParser.class,
+ 'sequence' : seq ]
+ if (classNode) bindings['classnode'] = classNode
+
+ if (overrideBindings) bindings.putAll(overrideBindings)
+
+ if (convertTemplate instanceof List) /// then we expect the template string/file to be the first entry
+ {
+ Map additionalBindings = convertTemplate.size() > 1 ? convertTemplate[1] : [:]
+ bindings.putAll(additionalBindings)
+ convertTemplate = convertTemplate[0]
+ }
+
+ if (seqSetHere) curSequence.set(null)
+ return new SimpleTemplateEngine().createTemplate(convertTemplate).make(bindings).toString()
+ }
+
+ /**
+ * <p>This method uses the previously set inTypemap and defaultInTypemap to produce the chunk of code
+ * that will be used to convert input parameters from the scripting language to the native method invocation
+ * parameters. For example, if the native call takes a 'String' then the InConversion could look something like:</p>
+ * <code>
+ * if (pythonStringArgument) PyXBMCGetUnicodeString(cArgument,pythonStringArgument,"cArgumentName");
+ * </code>
+ * <p>This could have resulted from a mini-template stored as the way to handle 'long's in the outTypemap:</p>
+ * <code>
+ * if (${slarg}) PyXBMCGetUnicodeString(${api},${slarg},"${api}");
+ * </code>
+ * @param apiType - is the Swig typecode that describes the parameter type from the native method
+ * @param apiName - is the name of the parameter from the method parameter list in the api
+ * @param slName - is the name of the varialbe that holds the parameter from the scripting language
+ * @param method - is the node from the module xml that contains the method description
+ * @return the code chunk as a string ready to be placed into the generated code.
+ */
+ public static String getInConversion(String apiType, String apiName, String slName, Node method, Map overrideBindings = null)
+ {
+ return getInConversion(apiType, apiName, apiName, slName, method, overrideBindings);
+ }
+
+ /**
+ * <p>This method uses the previously set inTypemap and defaultInTypemap to produce the chunk of code
+ * that will be used to convert input parameters from the scripting language to the native method invocation
+ * parameters. For example, if the native call takes a 'String' then the InConversion could look something like:</p>
+ * <code>
+ * if (pythonStringArgument) PyXBMCGetUnicodeString(cArgument,pythonStringArgument,"cArgumentName");
+ * </code>
+ * <p>This could have resulted from a mini-template stored as the way to handle 'long's in the outTypemap:</p>
+ * <code>
+ * if (${slarg}) PyXBMCGetUnicodeString(${api},${slarg},"${api}");
+ * </code>
+ * @param apiType - is the Swig typecode that describes the parameter type from the native method
+ * @param apiName - is the name of the varialbe that holds the api parameter
+ * @param paramName - is the name of the parameter from the method parameter list in the api
+ * @param slName - is the name of the varialbe that holds the parameter from the scripting language
+ * @param method - is the node from the module xml that contains the method description
+ * @return the code chunk as a string ready to be placed into the generated code.
+ */
+ public static String getInConversion(String apiType, String apiName, String paramName, String slName, Node method, Map overrideBindings = null)
+ {
+ def convertTemplate = inTypemap[apiType]
+
+ String apiLType = SwigTypeParser.convertTypeToLTypeForParam(apiType)
+
+ if (convertTemplate == null) convertTemplate = inTypemap[apiLType]
+
+ // is the returns a pointer to a known class
+ if (convertTemplate == null && apiType.startsWith('p.'))
+ {
+ // strip off rval qualifiers
+ String thisNamespace = Helper.findNamespace(method)
+ Node clazz = classes.find { Helper.findFullClassName(it) == apiLType.substring(2) ||
+ (it.@sym_name == apiLType.substring(2) && thisNamespace == Helper.findNamespace(it)) }
+ if (clazz != null) convertTemplate = defaultInTypeConversion
+ }
+
+ // Look for Pattern for keys that might fit
+ if (convertTemplate == null)
+ convertTemplate = inTypemap.find({ key, value -> (key instanceof Pattern && key.matcher(apiType).matches()) })?.value
+
+ // Try the LType
+ if (convertTemplate == null)
+ convertTemplate = inTypemap.find({ key, value -> (key instanceof Pattern && key.matcher(apiLType).matches()) })?.value
+
+ if (!convertTemplate){
+ // it's ok if this is a known type
+ if (!isKnownApiType(apiType,method) && !isKnownApiType(apiLType,method))
+ System.out.println("WARNING: Unknown parameter type: ${apiType} (or ${apiLType}) for the call ${Helper.findFullClassName(method) + '::' + Helper.callingName(method)}")
+ convertTemplate = defaultInTypeConversion
+ }
+
+ if (convertTemplate)
+ {
+ boolean seqSetHere = false
+ Sequence seq = curSequence.get()
+ if (seq == null)
+ {
+ seqSetHere = true
+ seq = new Sequence()
+ curSequence.set(seq)
+ }
+
+ Map bindings = [
+ 'type': "${apiType}", 'ltype': "${apiLType}",
+ 'slarg' : "${slName}", 'api' : "${apiName}",
+ 'param' : "${paramName}",
+ 'method' : method, 'helper' : Helper.class,
+ 'swigTypeParser' : SwigTypeParser.class,
+ 'sequence' : seq
+ ]
+
+ if (overrideBindings) bindings.putAll(overrideBindings)
+
+ if (convertTemplate instanceof List) /// then we expect the template string/file to be the first entry
+ {
+ Map additionalBindings = convertTemplate.size() > 1 ? convertTemplate[1] : [:]
+ bindings.putAll(additionalBindings)
+ convertTemplate = convertTemplate[0]
+ }
+
+ if (seqSetHere) curSequence.set(null);
+ return new SimpleTemplateEngine().createTemplate(convertTemplate).make(bindings).toString()
+ }
+
+ return ''
+ }
+
+ static def ignoreAttributes = ['classes', 'symtab', 'sym_symtab',
+ 'sym_overname', 'options', 'sym_nextSibling', 'csym_nextSibling',
+ 'sym_previousSibling' ]
+
+ /**
+ * <p>Transform a Swig generated xml file into something more manageable. For the most part this method will:</p>
+ *
+ * <li>1) Make all pertinent 'attributelist' elements actually be attributes of the parent element while
+ * an attribute with the name 'name' will become that parent element name.</li>
+ * <li>2) Filter out unused attributes</li>
+ * <li>3) Filter out the automatically included 'swig'swg'</li>
+ * <li>4) Flatten out the remaining 'include' elements</li>
+ * <li>5) Removes extraneous default argument function/method Node</li>
+ * <li>6) Converts all type tables to a single entry under the main module node removing all 1-1 mappings.</li>
+ * <li>7) Removes all non-public non-constructor methods.</li>
+ * @param swigxml is the raw swig output xml document
+ * @return the transformed document
+ */
+ public static Node transformSwigXml(Node swigxml)
+ {
+ Node node = transform(swigxml,
+ {
+ // do not include the 'include' entry that references the default swig.swg file.
+ !(it.name() == 'include' &&
+ // needs to also contain an attribute list with an attribute 'name' that matches the swig.swg file
+ it.find {
+ it.name() == 'attributelist' && it.find {
+ it.name() == 'attribute' && it.@name == 'name' && it.@value =~ /swig\.swg$/
+ }
+ } ||
+ // also don't include any typescope entries
+ it.name() == 'typescopesitem' || it.name() == 'typetabsitem'
+ )
+ },{ key, value -> !ignoreAttributes.contains(key) })
+
+ // now make sure the outer most node is an include and there's only one
+ assert node.include?.size() == 1 && node.include[0].module?.size() == 1 && node.include[0].module[0]?.@name != null,
+ "Invalid xml doc result. Expected a single child node of the root node call 'include' with a single 'module' child node but got " + XmlUtil.serialize(node)
+
+ // create an outermost 'module' node with the correct name
+ Node ret = new Node(null, 'module', [ 'name':node.include[0].module[0].@name] )
+ node.include[0].children().each { if (it.name() != 'module') ret.append(it) }
+
+ // flatten out all other 'include' elements, parmlists, and typescopes
+ flatten(ret,['include', 'parmlist', 'typescope' ])
+
+ // remove any function nodes with default arguments
+ List tmpl = []
+ tmpl.addAll(ret.depthFirst())
+ for (Node cur : tmpl)
+ {
+ if ((cur.name() == 'function' || cur.name() == 'constructor') && cur.@defaultargs != null)
+ cur.parent().remove(cur)
+ }
+
+ // now validate that no other methods are overloaded since we can't handle those right now.
+ functionNodesByOverloads(ret).each { key, value -> assert value.size() == 1, "Cannot handle overloaded methods unless simply using defaulting: " + value }
+
+ // now gather up all of the typetabs and add a nice single
+ // typetab entry with a better format in the main module
+ List allTypetabs = ret.depthFirst().findAll { it.name() == 'typetab' }
+ Node typenode = new Node(ret,'typetab')
+ allTypetabs.each {
+ it.attributes().each { key, value ->
+ if (key != 'id' && key != value)
+ {
+ Node typeentry = new Node(null,'entry')
+ String namespace = findNamespace(it)
+ typeentry.@namespace = namespace != null ? namespace.trim() : ''
+ typeentry.@type = key
+ typeentry.@basetype = value
+
+ if (typenode.find({ it.@basetype == typeentry.@basetype && it.@namespace == typeentry.@namespace }) == null)
+ typenode.append(typeentry)
+ }
+ }
+ it.parent().remove(it)
+ }
+
+ // now remove all non-public methods, but leave constructors
+ List allMethods = ret.depthFirst().findAll({ it.name() == 'function' || it.name() == 'destructor' || it.name() == 'constructor'})
+ allMethods.each {
+ if (it.@access != null && it.@access != 'public' && it.name() != 'constructor')
+ it.parent().remove(it)
+ else
+ {
+ def doc = retrieveDocStringFromDoxygen(it)
+ if (doc != null && doc != '' && doc.trim() != ' ')
+ new Node(it,'doc',['value' : doc])
+ }
+ }
+
+ // add the doc string to the classes
+ List allClasses = ret.depthFirst().findAll({ it.name() == 'class'})
+ allClasses.each {
+ def doc = retrieveDocStringFromDoxygen(it)
+ if (doc != null && doc != '' && doc.trim() != ' ')
+ new Node(it,'doc',['value' : doc])
+ }
+
+ return ret
+ }
+
+ /**
+ * @return true if the class node has a defined constructor. false otherwise.
+ */
+ public static boolean hasDefinedConstructor(Node clazz)
+ {
+ return (clazz.constructor != null && clazz.constructor.size() > 0)
+ }
+
+ /**
+ * @return true id this Node has a docstring associated with it.
+ */
+ public static boolean hasDoc(Node methodOrClass)
+ {
+ return methodOrClass.doc != null && methodOrClass.doc[0] != null && methodOrClass.doc[0].@value != null
+ }
+
+ /**
+ * @return true of the class node has a constructor but it's hidden (not 'public'). false otherwise.
+ */
+ public static boolean hasHiddenConstructor(Node clazz)
+ {
+ return (hasDefinedConstructor(clazz) && clazz.constructor[0].@access != null && clazz.constructor[0].@access != 'public')
+ }
+
+ /**
+ * <p>This will look through the entire module and look up a class node by name. It will return null if
+ * that class node isn't found. It's meant to be used to look up base classes from a base class list
+ * so it's fairly robust. It goes through the following rules:</p>
+ *
+ * <li>Does the FULL classname (considering the namespace) match the name provided.</li>
+ * <li>Does the FULL classname match the reference nodes namespace + '::' + the provided classname.</li>
+ * <li>Does the class node's name (which may contain the full classname) match the classname provided.</li>
+ *
+ * <p>Note, this method is not likely to find the classnode if you just pass a simple name and
+ * no referenceNode in the case where namespaces are used extensively.</p>
+ */
+ public static Node findClassNodeByName(Node module, String classname, Node referenceNode = null)
+ {
+ return module.depthFirst().findAll({ it.name() == 'class' }).find {
+ // first check to see if this FULL class name matches
+ if (findFullClassName(it).trim() == classname.trim()) return true
+
+ // now check to see if it matches the straight name considering the reference node
+ if (referenceNode != null && (findNamespace(referenceNode) + classname) == findFullClassName(it)) return true
+
+ // now just see if it matches the straight name
+ if (it.@name == classname) return true
+
+ return false
+ }
+ }
+
+ /**
+ * Find me the class node that this node either is, or is within.
+ * If this node is not within a class node then it will return null.
+ */
+ public static Node findClassNode(Node node)
+ {
+ if (node.name() == 'class') return node
+ return node.parent() == null ? null : findClassNode(node.parent())
+ }
+
+ /**
+ * If this node is a class node, or a child of a class name (for example, a method) then
+ * the full classname, with the namespace will be returned. Otherwise, null.
+ */
+ public static String findFullClassName(Node node, String separator = '::')
+ {
+ String ret = null
+ List rents = parents(node, { it.name() == 'class' })
+ if (node.name() == 'class') rents.add(node)
+ rents.each {
+ if (ret == null)
+ ret = it.@sym_name
+ else
+ ret += separator + it.@sym_name
+ }
+
+ return ret ? findNamespace(node,separator) + ret : null
+ }
+
+ /**
+ * Given the Node this method looks to see if it occurs within namespace and returns
+ * the namespace as a String. It includes the trailing '::'
+ */
+ public static String findNamespace(Node node, String separator = '::', boolean endingSeparator = true)
+ {
+ String ret = null
+ parents(node, { it.name() == 'namespace' }).each {
+ if (ret == null)
+ ret = it.@name
+ else
+ ret += separator + it.@name
+ }
+
+ return ret == null ? '' : (ret + (endingSeparator ? separator : ''))
+ }
+
+ /**
+ * Gather up all of the parent nodes in a list ordered from the top node, down to the
+ * node that's passed as a parameter.
+ */
+ public static List parents(Node node, Closure filter = null, List ret = null)
+ {
+ Node parent = node.parent()
+ if (parent != null)
+ {
+ ret = parents(parent,filter,ret)
+ if (filter == null || filter.call(parent))
+ ret += parent
+ }
+ else if (ret == null)
+ ret = []
+
+ return ret
+ }
+
+ /**
+ * Group together overloaded methods into a map keyed by the first method's id. Each
+ * entry in this map contains a list of nodes that represent overloaded versions of
+ * the same method.
+ */
+ public static Map functionNodesByOverloads(Node module)
+ {
+ // find function nodes
+ Map ret = [:]
+ module.depthFirst().each {
+ if (it.name() == 'function' || it.name() == 'constructor' || it.name() == 'destructor')
+ {
+ String id = it.@sym_overloaded != null ? it.@sym_overloaded : it.@id
+ if (ret[id] == null) ret[id] = [it]
+ else ret[id] += it
+ }
+ }
+ return ret
+ }
+
+ /**
+ * Because the return type is a combination of the function 'decl' and the
+ * function 'type,' this method will construct a valid Swig typestring from
+ * the two.
+ */
+ public static String getReturnSwigType(Node method)
+ {
+ // we're going to take a shortcut here because it appears only the pointer indicator
+ // ends up attached to the decl.
+ String prefix = (method.@decl != null && method.@decl.endsWith('.p.')) ? 'p.' : ''
+ return method.@type != null ? prefix + method.@type : 'void'
+ }
+
+ /**
+ * Given the method node this will produce the actual name of the method as if
+ * it's being called. In the case of a constructor it will do a 'new.' In the
+ * case of a destructor it will produce a 'delete.'
+ */
+ public static String callingName(Node method)
+ {
+ // if we're not in a class we need the fully qualified name
+ String clazz = findFullClassName(method)
+ // if we're in a class then we are going to assume we have a 'self' pointer
+ // that we are going to invoke this on.
+ if (clazz == null)
+ return method.@name
+
+ if (method.name() == 'constructor')
+ return "new ${findNamespace(method)}${method.@sym_name}"
+
+ if (method.name() == 'destructor')
+ return 'delete'
+
+ // otherwise it's just a call on a class being invoked on an instance
+ return method.@name
+ }
+
+ /**
+ * Swig has 'insert' nodes in it's parse tree that contain code chunks that are
+ * meant to be inserted into various positions in the generated code. This method
+ * will extract the nodes that refer to the specific section asked for. See the
+ * Swig documentation for more information.
+ */
+ public static List getInsertNodes(Node module, String section)
+ {
+ return module.insert.findAll { section == it.@section || (section == 'header' && it.@section == null) }
+ }
+
+ public static String unescape(Node insertSection) { return unescape(insertSection.@code) }
+
+ public static String unescape(String insertSection) { return StringEscapeUtils.unescapeHtml(insertSection) }
+
+ public static boolean isDirector(Node method)
+ {
+ Node clazz = findClassNode(method)
+ if (!clazz || !clazz.@feature_director)
+ return false
+ if (method.name() == 'destructor')
+ return false
+ if (method.name() == 'constructor')
+ return false
+ return method.@storage && method.@storage == 'virtual'
+ }
+
+ /**
+ * This method will search from the 'searchFrom' Node up to the root
+ * looking for a %feature("knownbasetypes") declaration that the given 'type' is
+ * known for 'searchFrom' Node.
+ */
+ public static boolean isKnownBaseType(String type, Node searchFrom)
+ {
+ return hasFeatureSetting(type,searchFrom,'feature_knownbasetypes',{ it.split(',').find({ it == type }) != null })
+ }
+
+ /**
+ * This method will search from the 'searchFrom' Node up to the root
+ * looking for a %feature("knownapitypes") declaration that the given 'type' is
+ * known for 'searchFrom' Node.
+ */
+ public static boolean isKnownApiType(String type, Node searchFrom)
+ {
+ String rootType = SwigTypeParser.getBaseType(type)
+ return hasFeatureSetting(type,searchFrom,'feature_knownapitypes',{ it.split(',').find({ it == rootType }) != null })
+ }
+
+ private static boolean hasFeatureSetting(String type, Node searchFrom, String feature, Closure test)
+ {
+ if (!searchFrom)
+ return false
+
+ if (searchFrom.attribute(feature) && test.call(searchFrom.attribute(feature)))
+ return true
+
+ return hasFeatureSetting(type,searchFrom.parent(),feature,test)
+ }
+
+ private static void flatten(Node node, List elementsToRemove)
+ {
+ for (boolean done = false; !done;)
+ {
+ done = true
+ for (Node child : node.breadthFirst())
+ {
+ if (elementsToRemove.contains(child.name()))
+ {
+ Node parent = child.parent()
+ parent.remove(child)
+ child.each { parent.append(it) }
+ done = false
+ break
+ }
+ }
+ }
+ }
+
+ private static Node transform(Node node, Closure nodeFilter, Closure attributeFilter)
+ {
+ // need to create a map and a list of nodes (which will be children) from the
+ // attribute list.
+ Map attributes = [:]
+ List nodes = []
+ node.each {
+ if (nodeFilter == null || nodeFilter.call(it) == true)
+ {
+ if (it.name() == 'attributelist')
+ {
+ Tuple results = transformSwigAttributeList(it)
+ attributes.putAll(results[0].findAll(attributeFilter))
+ List childNodes = results[1]
+ childNodes.each {
+ if (nodeFilter != null && nodeFilter.call(it) == true)
+ nodes.add(transform(it,nodeFilter,attributeFilter))
+ }
+ }
+ else
+ nodes.add(transform(it,nodeFilter,attributeFilter))
+ }
+ }
+
+ // transfer the addr attribute from the original node over to the 'id' attribute of the
+ // new node by adding it to the attributes map
+ if (node.@addr)
+ {
+ // copy over the other attributes
+ node.attributes().findAll { key,value -> if (key != 'addr' && key != 'id') attributes[key] = value }
+ attributes['id'] = node.@addr
+ }
+
+ // In the case when the Node is a cdecl, we really want to replace the node name
+ // with the 'kind' attribute value.
+ Node ret
+ if (node.name() == 'cdecl' && attributes.containsKey('kind'))
+ ret = new Node(null, attributes['kind'], attributes.findAll({key, value -> key != 'kind' } ))
+ else
+ ret = new Node(null, node.name(), attributes)
+ nodes.each { ret.append(it) }
+ return ret
+ }
+
+ private static Tuple transformSwigAttributeList(Node attributeList)
+ {
+ Map attributes = [:]
+ List nodes = []
+ attributeList.each {
+ if (it.name() == 'attribute')
+ attributes[it.@name] = it.@value
+ else
+ nodes.add(it)
+ }
+ return [attributes, nodes]
+ }
+}
+
diff --git a/tools/codegenerator/SwigTypeParser.groovy b/tools/codegenerator/SwigTypeParser.groovy
new file mode 100644
index 0000000000..7b086541ee
--- /dev/null
+++ b/tools/codegenerator/SwigTypeParser.groovy
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2005-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/**
+ * These methods are somewhat ugly because they have been copied out of
+ * the Swig source code and simply made compilable with groovy. They could
+ * all be much cleaner and smaller if they were completely groovyfied but
+ * I have no intention of doing that since they are complicated and they work
+ * and I don't want to try to trace down problems that would be inevitable
+ * with such a refactor.
+ */
+public class SwigTypeParser
+{
+ /**
+ * This holds a mapping for typedefs from a type to it's base type.
+ */
+ private static Map typeTable = [:]
+
+ public static void appendTypeTable(Node typetab) { typetab.each { typeTable[it.@namespace + it.@type] = it.@basetype } }
+
+ /**
+ * Convert the type to an ltype considering the overloaded conversions.
+ */
+ public static String convertTypeToLTypeForParam(String ty)
+ {
+ // in the case where we're converting from a type to an ltype for a parameter,
+ // and the type is a r.q(const).*, we are going to assume the ltype is
+ // a "pass-by-value" on the stack.
+ return (ty.trim().startsWith('r.q(const).') ? SwigTypeParser.SwigType_ltype(ty.trim().substring(11)) : SwigTypeParser.SwigType_ltype(ty.trim()))
+ }
+
+ /**
+ * This method will return the base type for the provided type string. For example,
+ * if the type string is p.MyType you will get MyType. If the string is
+ * p.q(const).int you will get 'int'
+ */
+ public static String getBaseType(String ty)
+ {
+ int li = ty.lastIndexOf('.')
+ return li >= 0 ? ty.substring(li + 1) : ty
+ }
+
+ /**
+ * SwigType_str()
+ *
+ * Create a C string representation of a datatype.
+ */
+ public static String SwigType_str(String ty, String id = null)
+ {
+ String result = id ? id : ''
+ String nextelement
+ String forwardelement
+ List elements = SwigType_split(ty)
+ if (elements == null) elements = []
+ int nelements = elements.size()
+ String element = nelements > 0 ? elements[0] : null
+
+ /* Now, walk the type list and start emitting */
+ for (int i = 0; i < nelements; i++) {
+ if (i < (nelements - 1)) {
+ nextelement = elements[i + 1]
+ forwardelement = nextelement
+ if (nextelement.startsWith('q(')) {
+ if (i < (nelements - 2)) forwardelement = elements[i + 2]
+ }
+ } else {
+ nextelement = null
+ forwardelement = null
+ }
+ if (element.startsWith('q(')) {
+ String q = SwigType_parm(element)
+ result = q + ' ' + result
+ } else if (SwigType_ispointer(element)) {
+ result = "*" + result
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ result = "(" + result + ")"
+ }
+ } else if (SwigType_ismemberpointer(element)) {
+ String q = SwigType_parm(element);
+ result = q + "::*" + result
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ result = '(' + result + ')'
+ }
+ } else if (SwigType_isreference(element)) {
+ result = '&' + result
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ result = '(' + result + ')'
+ }
+ } else if (SwigType_isarray(element)) {
+ result += '[' + SwigType_parm(element) + ']'
+ } else if (SwigType_isfunction(element)) {
+ result += '('
+ List parms = SwigType_parmlist(element)
+ boolean didOne = false
+ for (String cur : parms) {
+ String p = SwigType_str(cur)
+ result += (didOne ? ',' : '') + p
+ didOne = true
+ }
+ result += ')'
+ } else {
+ if (element.startsWith("v(...)")) result = result + "..."
+ else {
+ String bs = SwigType_namestr(element);
+ result = bs + ' ' + result
+ }
+ }
+ element = nextelement;
+ }
+ // convert template parameters
+ return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>')
+ }
+
+ public static String SwigType_typedef_resolve(String t)
+ {
+ String td = typeTable[t]
+ String ret = td == null ? t : td
+// System.out.println "trying to resolve ${t} and it appears to be typedefed to ${ret}"
+ return ret
+ }
+
+ public static String SwigType_typedef_resolve_all(String t)
+ {
+ String prev = t
+ t = SwigType_typedef_resolve(prev)
+ while(prev != t)
+ {
+ String tmp = t
+ t = SwigType_typedef_resolve(prev)
+ prev = tmp
+ }
+ return t
+ }
+
+ /**
+ * SwigType_ltype(const SwigType *ty)
+ *
+ * Create a locally assignable type
+ */
+ public static String SwigType_ltype(String s) {
+ String result = ''
+ String tc = s
+
+ /* Nuke all leading qualifiers */
+ while (SwigType_isqualifier(tc)) {
+ tc = SwigType_pop(tc)[1]
+ }
+
+ if (SwigType_issimple(tc)) {
+ /* Resolve any typedef definitions */
+ String tt = tc
+ String td
+ while ((td = SwigType_typedef_resolve(tt)) != tt) {
+ if ((td != tt) && (SwigType_isconst(td) || SwigType_isarray(td) || SwigType_isreference(td))) {
+ /* We need to use the typedef type */
+ tt = td
+ break
+ }
+ else if (td != tt) tt = td
+ }
+ tc = td
+ }
+ List elements = SwigType_split(tc)
+ int nelements = elements.size()
+
+ /* Now, walk the type list and start emitting */
+ boolean notypeconv = false
+ boolean firstarray = true
+ for (int i = 0; i < nelements; i++) {
+ String element = elements[i]
+ /* when we see a function, we need to preserve the following types */
+ if (SwigType_isfunction(element)) {
+ notypeconv = true
+ }
+ if (SwigType_isqualifier(element)) {
+ /* Do nothing. Ignore */
+ } else if (SwigType_ispointer(element)) {
+ result += element
+ // this is a bit of a short circuit to avoid having to import the entire SwigType_typedef_resolve method which
+ // handles pointers to typedefed types, etc.
+ // collapse the rest of the list
+ String tmps = ''
+ for (int j = i + 1; j < nelements; j++) tmps += elements[j]
+ return result + SwigType_ltype(tmps)
+ //firstarray = false
+ } else if (SwigType_ismemberpointer(element)) {
+ result += element
+ firstarray = false
+ } else if (SwigType_isreference(element)) {
+ if (notypeconv) {
+ result == element
+ } else {
+ result += "p."
+ }
+ firstarray = false
+ } else if (SwigType_isarray(element) && firstarray) {
+ if (notypeconv) {
+ result += element
+ } else {
+ result += "p."
+ }
+ firstarray = 0;
+ } else if (SwigType_isenum(element)) {
+ boolean anonymous_enum = (element == "enum ")
+ if (notypeconv || !anonymous_enum) {
+ result += element
+ } else {
+ result += "int"
+ }
+ } else {
+ result += element
+ }
+ }
+
+ return result
+ // convert template parameters
+ //return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>')
+ }
+
+ /**
+ * This creates the C++ declaration for a valid ltype for the type string
+ * given. For example, if the type is a "const char*" which is equivalent
+ * to the type string 'p.q(const).char', the return value from this method
+ * will be "char *".
+ */
+ public static String SwigType_lstr(String type)
+ {
+ return SwigType_str(convertTypeToLTypeForParam(type))
+ }
+
+ public static boolean SwigType_ispointer(String t)
+ {
+ if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1,)
+ return t.startsWith('p.')
+ }
+
+ public static boolean SwigType_isarray(String t) { return t.startsWith('a(') }
+
+ public static boolean SwigType_ismemberpointer(String t) { return t?.startsWith('m(') }
+
+ public static boolean SwigType_isqualifier(String t) { return t?.startsWith('q(') }
+
+ public static boolean SwigType_isreference(String t) { return t.startsWith('r.') }
+
+ public static boolean SwigType_isenum(String t) { return t.startsWith('enum') }
+
+ public static String SwigType_istemplate(String t) {
+ int c = t.indexOf("<(")
+ return (c >= 0 && t.indexOf(')>',c+2) >= 0)
+ }
+
+ public static boolean SwigType_isfunction(String t)
+ {
+ if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1,)
+ return t.startsWith('f(')
+ }
+
+ public static boolean SwigType_isconst(String t) {
+ int c = 0
+ if (t == null) return false
+ if (t.substring(c).startsWith("q(")) {
+ String q = SwigType_parm(t)
+ if (q.indexOf("const") >= 0) return true
+ }
+ /* Hmmm. Might be const through a typedef */
+ if (SwigType_issimple(t)) {
+ String td = SwigType_typedef_resolve(t)
+ if (td != t) return SwigType_isconst(td)
+ }
+ return false
+ }
+
+
+ private static String SwigType_parm(String t) {
+ int start = t.indexOf("(")
+ if (start < 0) return null
+ start++
+ int nparens = 0
+ int c = start
+ while (c < t.length()) {
+ if (t.charAt(c) == ')') {
+ if (nparens == 0) break;
+ nparens--;
+ }
+ else if (t.charAt(c) == '(') nparens++
+ c++;
+ }
+ return t.substring(start,c)
+ }
+
+ /* -----------------------------------------------------------------------------
+ * SwigType_parmlist()
+ *
+ * Splits a comma separated list of parameters into its component parts
+ * The input is expected to contain the parameter list within () brackets
+ * Returns 0 if no argument list in the input, ie there are no round brackets ()
+ * Returns an empty List if there are no parameters in the () brackets
+ * For example:
+ *
+ * Foo(std::string,p.f().Bar<(int,double)>)
+ *
+ * returns 2 elements in the list:
+ * std::string
+ * p.f().Bar<(int,double)>
+ * ----------------------------------------------------------------------------- */
+
+ private static List SwigType_parmlist(String p) {
+ List list = []
+ int itemstart
+
+ assert p, "Cannot pass null to SwigType_parmlist"
+ itemstart = p.indexOf('(')
+ assert p.indexOf('.') == -1 || p.indexOf('.') > itemstart, p + " is expected to contain sub elements of a type"
+ itemstart++
+ int c = itemstart
+ while (c < p.length()) {
+ if (p.charAt(c) == ',') {
+ list.add(p.substring(itemstart,c))
+ itemstart = c + 1
+ } else if (p.charAt(c) == '(') {
+ int nparens = 1
+ c++
+ while (c < p.length()) {
+ if (p.charAt(c) == '(') nparens++
+ if (p.charAt(c) == ')') {
+ nparens--
+ if (nparens == 0) break
+ }
+ c++
+ }
+ } else if (p.charAt(c) == ')') {
+ break;
+ }
+ if (c < p.length()) c++
+ }
+
+ if (c != itemstart) {
+ list.add(p.substring(itemstart,c))
+ }
+ return list;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * SwigType_namestr()
+ *
+ * Returns a string of the base type. Takes care of template expansions
+ * ----------------------------------------------------------------------------- */
+
+ private static String SwigType_namestr(String t) {
+ int d = 0
+ int c = t.indexOf("<(")
+
+ if (c < 0 || t.indexOf(')>',c+2) < 0) return t
+
+ String r = t.substring(0,c)
+ if (t.charAt(c - 1) == '<') r += ' '
+ r += '<'
+
+ List p = SwigType_parmlist(t.substring(c + 1))
+ for (int i = 0; i < p.size(); i++) {
+ String str = SwigType_str(p[i], null);
+ /* Avoid creating a <: token, which is the same as [ in C++ - put a space after '<'. */
+ if (i == 0 && str.length() > 0) r += ' '
+ r += str
+ if ((i + 1) < p.size()) r += ','
+ }
+ r += ' >'
+ String suffix = SwigType_templatesuffix(t);
+ if (suffix.length() > 0) {
+ String suffix_namestr = SwigType_namestr(suffix);
+ r += suffix_namestr
+ } else {
+ r += suffix
+ }
+ return r;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * SwigType_templatesuffix()
+ *
+ * Returns text after a template substitution. Used to handle scope names
+ * for example:
+ *
+ * Foo<(p.int)>::bar
+ *
+ * returns "::bar"
+ * ----------------------------------------------------------------------------- */
+
+ private static String SwigType_templatesuffix(String t) {
+ int c = 0
+ while (c < t.length()) {
+ if ((t.charAt(c) == '<') && (t.charAt(c + 1) == '(')) {
+ int nest = 1
+ c++
+ while (c < t.length() && nest != 0) {
+ if (t.charAt(c) == '<') nest++
+ if (t.charAt(c) == '>') nest--
+ c++
+ }
+ return t.substring(c)
+ }
+ c++
+ }
+ return ''
+ }
+
+ /* -----------------------------------------------------------------------------
+ * SwigType_split()
+ *
+ * Splits a type into it's component parts and returns a list of string.
+ * ----------------------------------------------------------------------------- */
+
+ private static List SwigType_split(String t) {
+ List list = []
+ int c = 0
+ int len
+
+ while (c < t.length()) {
+ len = element_size(t.substring(c))
+ String item = t.substring(c,c + len)
+ list += item
+ c = c + len
+ if (c < t.length() && t.charAt(c) == '.') c++
+ }
+ return list;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * static element_size()
+ *
+ * This utility function finds the size of a single type element in a type string.
+ * Type elements are always delimited by periods, but may be nested with
+ * parentheses. A nested element is always handled as a single item.
+ *
+ * Returns the integer size of the element (which can be used to extract a
+ * substring, to chop the element off, or for other purposes).
+ * ----------------------------------------------------------------------------- */
+
+ private static int element_size(String s) {
+ int nparen
+ int c = 0
+ while (c < s.length()) {
+ if (s.charAt(c) == '.') {
+ c++
+ return c
+ } else if (s.charAt(c) == '(') {
+ nparen = 1
+ c++
+ while (c < s.length()) {
+ if (s.charAt(c) == '(') nparen++
+ if (s.charAt(c) == ')') {
+ nparen--
+ if (nparen == 0) break
+ }
+ c++
+ }
+ }
+ if (c < s.length()) c++
+ }
+ return c;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * SwigType_pop()
+ *
+ * Pop one type element off the type.
+ * Example: t in: q(const).p.Integer
+ * t out: p.Integer
+ * result: q(const).
+ * ----------------------------------------------------------------------------- */
+
+ private static Tuple SwigType_pop(String t) {
+ String result
+ int c = 0
+
+ if (t == null)
+ return null
+
+ int sz = element_size(t.substring(c))
+ return [ t.substring(c,c + sz), t.substring(c+sz) ]
+ }
+
+ private static boolean SwigType_issimple(String t) {
+ int c = 0
+ if (!t) return false
+ while (c < t.length()) {
+ if (t.charAt(c) == '<') {
+ int nest = 1
+ c++
+ while (c < t.length() && nest != 0) {
+ if (t.charAt(c) == '<') nest++
+ if (t.charAt(c) == '>') nest--
+ c++
+ }
+ c--
+ }
+ if (t.charAt(c) == '.')
+ return false
+ c++
+ }
+ return true
+ }
+
+
+ public static void main(String[] args)
+ {
+ String xmlText = '''
+ <typetab>
+ <entry basetype="std::vector&lt;(p.XBMCAddon::xbmcgui::ListItem)&gt;" type="ListItemList" namespace="XBMCAddon::xbmcgui::"/>
+ </typetab>
+ '''
+ Node xml = new XmlParser().parseText(xmlText)
+
+ SwigTypeParser.appendTypeTable(xml)
+
+ // testPrint('f(int,int,int)','foo')
+ // testPrint('p.a(10).p.f(int,p.f(int).int)','foo')
+ // testPrint('p.q(const).char','foo')
+ // testPrint('f(r.q(const).String,p.q(const).XBMCAddon::xbmcgui::ListItem,bool)','foo')
+ // testPrint('r.q(const).String','foo')
+ // testPrint('q(const).p.q(const).char','foo')
+ //testPrint('std::vector<(p.String)>','foo')
+ // testPrint('r.q(const).String')
+ //System.out.println "${convertTypeToLType('bool')}"
+ //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList')
+ //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList')
+ testPrint('r.q(const).std::map<(String,String)>', 'foo')
+ }
+
+ private static void testPrint(String ty, String id = 'foo')
+ {
+ println SwigTypeParser.SwigType_ltype(ty) + "|" + SwigTypeParser.SwigType_str(SwigTypeParser.SwigType_ltype(ty),id) + ' ' + " = " + SwigTypeParser.SwigType_str(ty,id)
+ }
+}
diff --git a/tools/codegenerator/example/AddonModuleXbmc.i b/tools/codegenerator/example/AddonModuleXbmc.i
new file mode 100644
index 0000000000..e4670c0054
--- /dev/null
+++ b/tools/codegenerator/example/AddonModuleXbmc.i
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2005-2010 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+%module xbmc
+
+%include "native/ModuleXbmc.h"
diff --git a/tools/codegenerator/example/Example-lval-rval.template b/tools/codegenerator/example/Example-lval-rval.template
new file mode 100644
index 0000000000..77d506e4ac
--- /dev/null
+++ b/tools/codegenerator/example/Example-lval-rval.template
@@ -0,0 +1,18 @@
+<%
+import SwigTypeParser
+%>
+Module Name: ${module.@name}
+<%
+module.function.each { functionNode ->
+%>
+ function: ${functionNode.@name}
+<%
+ functionNode.parm.eachWithIndex { param, index ->
+%>
+ parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %>
+ type:${SwigTypeParser.SwigType_str(param.@type)}
+ lvalue-type:${SwigTypeParser.SwigType_lstr(param.@type)}
+<%
+ }
+}
+%>
diff --git a/tools/codegenerator/example/Example.call.template b/tools/codegenerator/example/Example.call.template
new file mode 100644
index 0000000000..f9e84ed185
--- /dev/null
+++ b/tools/codegenerator/example/Example.call.template
@@ -0,0 +1,48 @@
+<%
+import SwigTypeParser
+import Helper
+
+Helper.setup(null,
+ [ 'void': 'Py_INCREF(Py_None); ${result} = Py_None;' ],
+ null,
+ [ 'p.q(const).char':'${api} = convertSlString(${slarg});',
+ 'int':'${api} = convertSlInt(${slarg});'],
+ null)
+%>
+Module Name: ${module.@name}
+<%
+module.function.each { functionNode ->
+%>
+ function: ${functionNode.@name}
+
+<%
+ functionNode.parm.eachWithIndex { param, index ->
+%>
+ parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %>
+ lvalue:${SwigTypeParser.SwigType_lstr(param.@type)}
+ rvalue:${SwigTypeParser.SwigType_str(param.@type)}
+ code to handle parameter ${index} {
+ // declare and set the value that came in from the scripting languge
+ ScriptingLanguageType sl_${param.@name} = /* set the value from the scripting language */;
+ // declare and set the variable that will contain the api parameter
+ ${SwigTypeParser.SwigType_lstr(param.@type)} p_${param.@name};
+ ${Helper.getInConversion(param.@type,'p_' + param.@name,'sl_' + param.@name, functionNode)}
+ }
+<%
+ }
+%>
+ code to invoke the api method {
+ ${Helper.callingName(functionNode)}( <%
+ functionNode.parm.eachWithIndex { param, i ->
+ %> p_${param.@name}${i < functionNode.parm.size() - 1 ? "," : ""} <% } %> );
+ }
+
+ code to handle return value {
+ // This is an example of how Python handles return values
+ Py_Object* result;
+ ${Helper.getOutConversion(Helper.getReturnSwigType(functionNode),'result',functionNode)}
+ return result;
+ }
+<%
+}
+%>
diff --git a/tools/codegenerator/example/Example.intypemap.template b/tools/codegenerator/example/Example.intypemap.template
new file mode 100644
index 0000000000..de90963b60
--- /dev/null
+++ b/tools/codegenerator/example/Example.intypemap.template
@@ -0,0 +1,31 @@
+<%
+import SwigTypeParser
+import Helper
+
+Helper.setup(null,null,null,
+ [ 'p.q(const).char':'${api} = convertSlString(${slarg});',
+ 'int':'${api} = convertSlInt(${slarg});'],
+ null)
+%>
+Module Name: ${module.@name}
+<%
+module.function.each { functionNode ->
+%>
+ function: ${functionNode.@name}
+<%
+ functionNode.parm.eachWithIndex { param, index ->
+%>
+ parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %>
+ type:${SwigTypeParser.SwigType_str(param.@type)}
+ lvalue-type:${SwigTypeParser.SwigType_lstr(param.@type)}
+ code to handle parameter ${index} {
+ // declare and set the value that came in from the scripting languge
+ ScriptingLanguageType sl_${param.@name} = /* set the value from the scripting language */;
+ // declare and set the variable that will contain the api parameter
+ ${SwigTypeParser.SwigType_lstr(param.@type)} p_${param.@name};
+ ${Helper.getInConversion(param.@type,'p_' + param.@name,'sl_' + param.@name, functionNode)}
+ }
+<%
+ }
+}
+%>
diff --git a/tools/codegenerator/example/Example.returns.template b/tools/codegenerator/example/Example.returns.template
new file mode 100644
index 0000000000..074d30f8c5
--- /dev/null
+++ b/tools/codegenerator/example/Example.returns.template
@@ -0,0 +1,42 @@
+<%
+import SwigTypeParser
+import Helper
+
+Helper.setup(null,
+ [ 'void': 'Py_INCREF(Py_None); ${result} = Py_None;' ],
+ null,
+ [ 'p.q(const).char':'${api} = convertSlString(${slarg});',
+ 'int':'${api} = convertSlInt(${slarg});'],
+ null)
+%>
+Module Name: ${module.@name}
+<%
+module.function.each { functionNode ->
+%>
+ function: ${functionNode.@name}
+
+<%
+ functionNode.parm.eachWithIndex { param, index ->
+%>
+ parameter ${index}= name:${param.@name}, type:${param.@type}<% if (param.@value) { %>, default value: ${param.@value} <% } %>
+ type:${SwigTypeParser.SwigType_str(param.@type)}
+ lvalue-type:${SwigTypeParser.SwigType_lstr(param.@type)}
+ code to handle parameter ${index} {
+ // declare and set the value that came in from the scripting languge
+ ScriptingLanguageType sl_${param.@name} = /* set the value from the scripting language */;
+ // declare and set the variable that will contain the api parameter
+ ${SwigTypeParser.SwigType_lstr(param.@type)} p_${param.@name};
+ ${Helper.getInConversion(param.@type,'p_' + param.@name,'sl_' + param.@name, functionNode)}
+ }
+<%
+ }
+%>
+ code to handle return value {
+ // This is an example of how Python handles return values
+ Py_Object* result;
+ ${Helper.getOutConversion(Helper.getReturnSwigType(functionNode),'result',functionNode)}
+ return result;
+ }
+<%
+}
+%>
diff --git a/tools/codegenerator/example/native/ModuleXbmc.h b/tools/codegenerator/example/native/ModuleXbmc.h
new file mode 100644
index 0000000000..14dd80d762
--- /dev/null
+++ b/tools/codegenerator/example/native/ModuleXbmc.h
@@ -0,0 +1 @@
+void log(const char* msg, int level = LOGNOTICE);