summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/python/PythonTools.groovy
blob: e41db6cb18bc62d603729bd6d6b67f9c5fdbe0b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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
  }
}