Merge pull request #4401 from Jalle19/fix-recording-thumbnail
[vuplus_xbmc] / tools / codegenerator / SwigTypeParser.groovy
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 /**
22  * These methods are somewhat ugly because they have been copied out of
23  * the Swig source code and simply made compilable with groovy. They could 
24  * all be much cleaner and smaller if they were completely groovyfied but
25  * I have no intention of doing that since they are complicated and they work
26  * and I don't want to try to trace down problems that would be inevitable 
27  * with such a refactor.
28  */
29 public class SwigTypeParser
30 {
31    /**
32     * This holds a mapping for typedefs from a type to it's base type.
33     */
34    private static Map typeTable = [:]
35
36    /**
37     * Add a typedef node to the global list of typedefs to be used later in 
38     *  parsing types.
39     */
40    public static void appendTypeTable(Node typetab) { typetab.each { typeTable[it.@namespace + it.@type] = it.@basetype } }
41
42    /**
43     * Convert the type to an ltype considering the overloaded conversions.
44     */
45    public static String convertTypeToLTypeForParam(String ty)
46    {
47       // in the case where we're converting from a type to an ltype for a parameter,
48       //  and the type is a r.*, we are going to assume the ltype is
49       //  a "pass-by-value" on the stack.
50       return (ty.trim().startsWith('r.') ? SwigTypeParser.SwigType_ltype(ty.trim().substring(2)) : SwigTypeParser.SwigType_ltype(ty.trim()))
51    }
52
53   /**
54    * This method will return the base type for the provided type string. For example,
55    * if the type string is p.MyType you will get MyType. If the string is 
56    * p.q(const).int you will get 'int'
57    */
58   public static String getRootType(String ty)
59   { 
60     int li = ty.lastIndexOf('.')
61     return li >= 0 ? ty.substring(li + 1) : ty
62   }
63
64    /**
65     * SwigType_str()
66     *
67     * Create a C string representation of a datatype.
68     */
69    public static String SwigType_str(String ty, String id = null)
70    {
71       String result = id ? id : ''
72       String nextelement
73       String forwardelement
74       List elements = SwigType_split(ty)
75       if (elements == null) elements = []
76       int nelements = elements.size()
77       String element = nelements > 0 ? elements[0] : null
78
79       /* Now, walk the type list and start emitting */
80       for (int i = 0; i < nelements; i++) {
81          if (i < (nelements - 1)) {
82             nextelement = elements[i + 1]
83             forwardelement = nextelement
84             if (nextelement.startsWith('q(')) {
85                if (i < (nelements - 2)) forwardelement = elements[i + 2]
86             }
87          } else {
88             nextelement = null
89             forwardelement = null
90          }
91          if (element.startsWith('q(')) {
92             String q = SwigType_parm(element)
93             result = q + ' ' + result
94          } else if (SwigType_ispointer(element)) {
95             result = "*" + result
96             if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
97                result = "(" + result + ")"
98             }
99          } else if (SwigType_ismemberpointer(element)) {
100             String q = SwigType_parm(element);
101             result = q + "::*" + result
102             if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
103                result = '(' + result + ')'
104             }
105          } else if (SwigType_isreference(element)) {
106             result = '&' + result
107             if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
108                result = '(' + result + ')'
109             }
110          } else if (SwigType_isarray(element)) {
111             result += '[' + SwigType_parm(element) + ']'
112          } else if (SwigType_isfunction(element)) {
113             result += '('
114             List parms = SwigType_parmlist(element)
115             boolean didOne = false
116             for (String cur : parms) {
117                String p = SwigType_str(cur)
118                result += (didOne ? ',' : '') + p
119                didOne = true
120             }
121             result += ')'
122          } else {
123             if (element.startsWith("v(...)")) result = result + "..."
124             else {
125                String bs = SwigType_namestr(element);
126                result = bs + ' ' + result
127             }
128          }
129          element = nextelement;
130       }
131       // convert template parameters
132       return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>')
133    }
134
135    /**
136     * This will resolve the typedefs given the parameter passed is a simple type.
137     *  see SwigType_resolve_all_typedefs which will handle qualifiers, pointers,
138     *  references, and typedef of typedefs to resolve all the way down to the
139     *  most basic types.
140     */
141    public static String SwigType_typedef_resolve(String t)
142    {
143       String td = typeTable[t]
144       String ret = td == null ? t : td
145       return ret
146    }
147
148    /**
149     * This will resolve typedefs anbd handle qualifiers, pointers,
150     *  references, and typedef of typedefs to resolve all the way down to the
151     *  most basic types.
152     */
153    public static String SwigType_resolve_all_typedefs(String s)
154    { 
155       String result = ''
156       String tc = s
157
158       /* Nuke all leading qualifiers, appending them to the result*/
159       while (SwigType_isqualifier(tc)) {
160          List tmpl = SwigType_pop(tc)
161          tc = tmpl[1]
162          result += tmpl[0]
163       }
164
165       if (SwigType_issimple(tc)) {
166          /* Resolve any typedef definitions */
167          String tt = tc
168          String td
169          while ((td = SwigType_typedef_resolve(tt)) != tt) {
170             if (td != tt) {
171                tt = td
172                break
173             }
174             else if (td != tt) tt = td
175          }
176          tc = td
177
178          return tc
179       }
180
181       List tmpl = SwigType_pop(tc)
182       result += tmpl[0]
183       result += SwigType_resolve_all_typedefs(tmpl[1])
184       return result
185    }
186
187    /**
188     * SwigType_ltype(const SwigType *ty)
189     *
190     * Create a locally assignable type
191     */
192    public static String SwigType_ltype(String s) {
193       String result = ''
194       String tc = s
195
196       /* Nuke all leading qualifiers */
197       while (SwigType_isqualifier(tc)) {
198          tc = SwigType_pop(tc)[1]
199       }
200
201       if (SwigType_issimple(tc)) {
202          /* Resolve any typedef definitions */
203          String tt = tc
204          String td
205          while ((td = SwigType_typedef_resolve(tt)) != tt) {
206             if ((td != tt) && (SwigType_isconst(td) || SwigType_isarray(td) || SwigType_isreference(td))) {
207                /* We need to use the typedef type */
208                tt = td
209                break
210             }
211             else if (td != tt) tt = td
212          }
213          tc = td
214       }
215       List elements = SwigType_split(tc)
216       int nelements = elements.size()
217
218       /* Now, walk the type list and start emitting */
219       boolean notypeconv = false
220       boolean firstarray = true
221       for (int i = 0; i < nelements; i++) {
222          String element = elements[i]
223          /* when we see a function, we need to preserve the following types */
224          if (SwigType_isfunction(element)) {
225             notypeconv = true
226          }
227          if (SwigType_isqualifier(element)) {
228             /* Do nothing. Ignore */
229          } else if (SwigType_ispointer(element)) {
230             result += element
231             // this is a bit of a short circuit to avoid having to import the entire SwigType_typedef_resolve method which
232             // handles pointers to typedefed types, etc.
233             // collapse the rest of the list
234             String tmps = ''
235             for (int j = i + 1; j < nelements; j++) tmps += elements[j]
236             return result + SwigType_ltype(tmps)
237             //firstarray = false
238          } else if (SwigType_ismemberpointer(element)) {
239             result += element
240             firstarray = false
241          } else if (SwigType_isreference(element)) {
242             if (notypeconv) {
243                result += element
244             } else {
245                result += "p."
246             }
247             firstarray = false
248          } else if (SwigType_isarray(element) && firstarray) {
249             if (notypeconv) {
250                result += element
251             } else {
252                result += "p."
253             }
254             firstarray = false;
255          } else if (SwigType_isenum(element)) {
256             boolean anonymous_enum = (element == "enum ")
257             if (notypeconv || !anonymous_enum) {
258                result += element
259             } else {
260                result += "int"
261             }
262          } else {
263             result += element
264          }
265       }
266
267       return result
268       // convert template parameters
269       //return result.replaceAll('<\\(', '<').replaceAll('\\)>', '>')
270    }
271    
272    /**
273    * This creates the C++ declaration for a valid ltype for the type string
274    * given. For example, if the type is a "const char*" which is equivalent
275    * to the type string 'p.q(const).char', the return value from this method
276    * will be "char *".
277    */
278    public static String SwigType_lstr(String type)
279    {
280       return SwigType_str(convertTypeToLTypeForParam(type))
281    }
282
283    public static boolean SwigType_ispointer(String t)
284    {
285       if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1)
286       return t.startsWith('p.')
287    }
288
289    public static String SwigType_makepointer(String t)
290    {
291      String prefix = (t.startsWith('q(')) ? t.substring(0,t.indexOf('.') + 1) : ""
292      String remainder = (t.startsWith('q(')) ? t.substring(t.indexOf('.') + 1) : t
293      
294      return prefix + "p." + remainder
295    }
296
297    public static boolean SwigType_isarray(String t) { return t.startsWith('a(') }
298
299    public static boolean SwigType_ismemberpointer(String t) { return t?.startsWith('m(') }
300
301    public static boolean SwigType_isqualifier(String t) { return t?.startsWith('q(') }
302
303    public static boolean SwigType_isreference(String t) { return t.startsWith('r.') }
304
305    public static boolean SwigType_isenum(String t) { return t.startsWith('enum') }
306
307    public static String SwigType_istemplate(String t) {
308       int c = t.indexOf("<(")
309       return (c >= 0 && t.indexOf(')>',c+2) >= 0)
310    }
311
312    public static boolean SwigType_isfunction(String t)
313    {
314       if (t.startsWith('q(')) t = t.substring(t.indexOf('.') + 1,)
315       return t.startsWith('f(')
316    }
317
318    public static boolean SwigType_isconst(String t) {
319       int c = 0
320       if (t == null) return false
321       if (t.substring(c).startsWith("q(")) {
322          String q = SwigType_parm(t)
323          if (q.indexOf("const") >= 0) return true
324       }
325       /* Hmmm. Might be const through a typedef */
326       if (SwigType_issimple(t)) {
327          String td = SwigType_typedef_resolve(t)
328          if (td != t) return SwigType_isconst(td)
329       }
330       return false
331    }
332
333
334    private static String SwigType_parm(String t) {
335       int start = t.indexOf("(")
336       if (start < 0) return null
337       start++
338       int nparens = 0
339       int c = start
340       while (c < t.length()) {
341          if (t.charAt(c) == ')') {
342             if (nparens == 0) break;
343             nparens--;
344          }
345          else if (t.charAt(c) == '(') nparens++
346          c++;
347       }
348       return t.substring(start,c)
349    }
350
351   public static List SwigType_templateparmlist(String t)
352   {
353     int i = t.indexOf('<');
354     return SwigType_parmlist(t.substring(i))
355   }
356
357    /* -----------------------------------------------------------------------------
358     * SwigType_parmlist()
359     *
360     * Splits a comma separated list of parameters into its component parts
361     * The input is expected to contain the parameter list within () brackets
362     * Returns 0 if no argument list in the input, ie there are no round brackets ()
363     * Returns an empty List if there are no parameters in the () brackets
364     * For example:
365     *
366     *     Foo(std::string,p.f().Bar<(int,double)>)
367     *
368     * returns 2 elements in the list:
369     *    std::string
370     *    p.f().Bar<(int,double)>
371     * ----------------------------------------------------------------------------- */
372
373    private static List SwigType_parmlist(String p) {
374       List list = []
375       int itemstart
376
377       assert p, "Cannot pass null to SwigType_parmlist"
378       itemstart = p.indexOf('(')
379       assert p.indexOf('.') == -1 || p.indexOf('.') > itemstart, p + " is expected to contain sub elements of a type"
380       itemstart++
381       int c = itemstart
382       while (c < p.length()) {
383          if (p.charAt(c) == ',') {
384             list.add(p.substring(itemstart,c))
385             itemstart = c + 1
386          } else if (p.charAt(c) == '(') {
387             int nparens = 1
388             c++
389             while (c < p.length()) {
390                if (p.charAt(c) == '(') nparens++
391                if (p.charAt(c) == ')') {
392                   nparens--
393                   if (nparens == 0) break
394                }
395                c++
396             }
397          } else if (p.charAt(c) == ')') {
398             break;
399          }
400          if (c < p.length()) c++
401       }
402
403       if (c != itemstart) {
404          list.add(p.substring(itemstart,c))
405       }
406       return list;
407    }
408
409    /* -----------------------------------------------------------------------------
410     * SwigType_namestr()
411     *
412     * Returns a string of the base type.  Takes care of template expansions
413     * ----------------------------------------------------------------------------- */
414
415    private static String SwigType_namestr(String t) {
416       int d = 0
417       int c = t.indexOf("<(")
418
419       if (c < 0 || t.indexOf(')>',c+2) < 0) return t
420
421       String r = t.substring(0,c)
422       if (t.charAt(c - 1) == '<') r += ' '
423       r += '<'
424
425       List p = SwigType_parmlist(t.substring(c + 1))
426       for (int i = 0; i < p.size(); i++) {
427          String str = SwigType_str(p[i], null);
428          /* Avoid creating a <: token, which is the same as [ in C++ - put a space after '<'. */
429          if (i == 0 && str.length() > 0) r += ' '
430          r += str
431          if ((i + 1) < p.size()) r += ','
432       }
433       r += ' >'
434       String suffix = SwigType_templatesuffix(t);
435       if (suffix.length() > 0) {
436          String suffix_namestr = SwigType_namestr(suffix);
437          r += suffix_namestr
438       } else {
439          r += suffix
440       }
441       return r;
442    }
443
444    /* -----------------------------------------------------------------------------
445     * SwigType_templatesuffix()
446     *
447     * Returns text after a template substitution.  Used to handle scope names
448     * for example:
449     *
450     *        Foo<(p.int)>::bar
451     *
452     * returns "::bar"
453     * ----------------------------------------------------------------------------- */
454
455    private static String SwigType_templatesuffix(String t) {
456       int c = 0
457       while (c < t.length()) {
458          if ((t.charAt(c) == '<') && (t.charAt(c + 1) == '(')) {
459             int nest = 1
460             c++
461             while (c < t.length() && nest != 0) {
462                if (t.charAt(c) == '<') nest++
463                if (t.charAt(c) == '>') nest--
464                c++
465             }
466             return t.substring(c)
467          }
468          c++
469       }
470       return ''
471    }
472
473    /* -----------------------------------------------------------------------------
474     * SwigType_split()
475     *
476     * Splits a type into it's component parts and returns a list of string.
477     * ----------------------------------------------------------------------------- */
478
479    private static List SwigType_split(String t) {
480       List list = []
481       int c = 0
482       int len
483
484       while (c < t.length()) {
485          len = element_size(t.substring(c))
486          String item = t.substring(c,c + len)
487          list += item
488          c = c + len
489          if (c < t.length() && t.charAt(c) == '.') c++
490       }
491       return list;
492    }
493
494    /* -----------------------------------------------------------------------------
495     * static element_size()
496     *
497     * This utility function finds the size of a single type element in a type string.
498     * Type elements are always delimited by periods, but may be nested with
499     * parentheses.  A nested element is always handled as a single item.
500     *
501     * Returns the integer size of the element (which can be used to extract a 
502     * substring, to chop the element off, or for other purposes).
503     * ----------------------------------------------------------------------------- */
504
505    private static int element_size(String s) {
506       int nparen
507       int c = 0
508       while (c < s.length()) {
509          if (s.charAt(c) == '.') {
510             c++
511             return c
512          } else if (s.charAt(c) == '(') {
513             nparen = 1
514             c++
515             while (c < s.length()) {
516                if (s.charAt(c) == '(') nparen++
517                if (s.charAt(c) == ')') {
518                   nparen--
519                   if (nparen == 0) break
520                }
521                c++
522             }
523          }
524          if (c < s.length()) c++
525       }
526       return c;
527    }
528
529    /* -----------------------------------------------------------------------------
530     * SwigType_pop()
531     *
532     * Pop one type element off the type.
533     * Example: t in:  q(const).p.Integer
534     *          t out: p.Integer
535     *    result: q(const).
536     * ----------------------------------------------------------------------------- */
537
538    private static Tuple SwigType_pop(String t) {
539       String result
540       int c = 0
541
542       if (t == null)
543          return null
544
545       int sz = element_size(t.substring(c))
546       return [ t.substring(c,c + sz), t.substring(c+sz) ]
547    }
548
549    private static boolean SwigType_issimple(String t) {
550       int c = 0
551       if (!t) return false
552       while (c < t.length()) {
553          if (t.charAt(c) == '<') {
554             int nest = 1
555             c++
556             while (c < t.length() && nest != 0) {
557                if (t.charAt(c) == '<') nest++
558                if (t.charAt(c) == '>') nest--
559                c++
560             }
561             c--
562          }
563          if (t.charAt(c) == '.')
564             return false
565          c++
566       }
567       return true
568    }
569
570
571    public static void main(String[] args)
572    {
573       String xmlText = '''
574     <typetab>
575       <entry basetype="std::vector&lt;(p.XBMCAddon::xbmcgui::ListItem)&gt;" type="ListItemList" namespace="XBMCAddon::xbmcgui::"/>
576     </typetab>
577       '''
578       Node xml = new XmlParser().parseText(xmlText)
579       
580       SwigTypeParser.appendTypeTable(xml)
581       
582       //      testPrint('f(int,int,int)','foo')
583       //      testPrint('p.a(10).p.f(int,p.f(int).int)','foo')
584       //      testPrint('p.q(const).char','foo')
585       //      testPrint('f(r.q(const).String,p.q(const).XBMCAddon::xbmcgui::ListItem,bool)','foo')
586       //      testPrint('r.q(const).String','foo')
587       //      testPrint('q(const).p.q(const).char','foo')
588       //testPrint('std::vector<(p.String)>','foo')
589       //      testPrint('r.q(const).String')
590       //System.out.println "${convertTypeToLType('bool')}"
591       //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList')
592       //testPrint('p.q(const).XBMCAddon::xbmcgui::ListItemList')
593       //testPrint(SwigTypeParser.SwigType_makepointer('r.q(const).std::map<(String,String)>'), 'foo')
594       testPrint(SwigTypeParser.SwigType_makepointer('q(const).p.q(const).char'),'bfoo')
595    }
596
597    private static void testPrint(String ty, String id = 'foo')
598    {
599       println SwigTypeParser.SwigType_ltype(ty) + "|" + SwigTypeParser.SwigType_str(SwigTypeParser.SwigType_ltype(ty),id) + ' ' + " = " + ty + '|' + SwigTypeParser.SwigType_str(ty,id)
600    }
601 }