]> git.sesse.net Git - vlc/commitdiff
python-ctypes: first shot at generating java JNA glue code
authorOlivier Aubert <olivier.aubert@liris.cnrs.fr>
Wed, 23 Sep 2009 14:17:18 +0000 (16:17 +0200)
committerOlivier Aubert <olivier.aubert@liris.cnrs.fr>
Wed, 23 Sep 2009 14:19:03 +0000 (16:19 +0200)
Not perfect yet (esp. enum classes are not correctly translated for discontinuous enums).

bindings/python-ctypes/LibVlc-footer.java [new file with mode: 0644]
bindings/python-ctypes/LibVlc-header.java [new file with mode: 0644]
bindings/python-ctypes/boilerplate.java [new file with mode: 0644]
bindings/python-ctypes/generate.py

diff --git a/bindings/python-ctypes/LibVlc-footer.java b/bindings/python-ctypes/LibVlc-footer.java
new file mode 100644 (file)
index 0000000..5c34318
--- /dev/null
@@ -0,0 +1 @@
+}
diff --git a/bindings/python-ctypes/LibVlc-header.java b/bindings/python-ctypes/LibVlc-header.java
new file mode 100644 (file)
index 0000000..89f3bfe
--- /dev/null
@@ -0,0 +1,255 @@
+package org.videolan.jvlc.internal;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.NativeLong;
+import com.sun.jna.Platform;
+import com.sun.jna.Pointer;
+import com.sun.jna.PointerType;
+import com.sun.jna.Structure;
+import com.sun.jna.Union;
+
+
+public interface LibVlc extends Library
+{
+    LibVlc INSTANCE = (LibVlc) Native.loadLibrary(Platform.isWindows()? "libvlc" : "vlc", LibVlc.class);
+
+    LibVlc SYNC_INSTANCE = (LibVlc) Native.synchronizedLibrary(INSTANCE);
+
+    public static class libvlc_exception_t extends Structure
+    {
+        public int b_raised;
+    }
+
+    public static class libvlc_log_message_t extends Structure
+    {
+
+        public int sizeof_msg; /* sizeof() of message structure, must be filled in by user */
+
+        public int i_severity; /* 0=INFO, 1=ERR, 2=WARN, 3=DBG */
+
+        public String psz_type; /* module type */
+
+        public String psz_name; /* module name */
+
+        public String psz_header; /* optional header */
+
+        public String psz_message; /* message */
+    }
+
+    public static class libvlc_event_t extends Structure
+    {
+
+        public int type;
+
+        public Pointer p_obj;
+
+        public event_type_specific event_type_specific;
+
+    }
+
+    public class media_meta_changed extends Structure
+    {
+        // Enum !
+        public Pointer meta_type;
+    }
+
+    public class media_subitem_added extends Structure
+    {
+
+        public LibVlcMedia new_child;
+    }
+
+    public class media_duration_changed extends Structure
+    {
+
+        public NativeLong new_duration;
+    }
+
+    public class media_preparsed_changed extends Structure
+    {
+
+        public int new_status;
+    }
+
+    public class media_freed extends Structure
+    {
+
+        public LibVlcMedia md;
+    }
+
+    public class media_state_changed extends Structure
+    {
+
+        // @todo: check this one
+        public int new_state;
+    }
+
+    /* media instance */
+
+    public class media_player_position_changed extends Structure
+    {
+
+        public float new_position;
+    }
+
+    public class media_player_time_changed extends Structure
+    {
+
+        // @todo: check this one
+        public long new_time;
+    }
+
+    public class media_player_title_changed extends Structure
+    {
+        public int new_title;
+    }
+
+    public class media_player_seekable_changed extends Structure
+    {
+        public NativeLong new_seekable;
+    }
+
+    public class media_player_pausable_changed extends Structure
+    {
+        public NativeLong new_pausable;
+    }
+
+    /* media list */
+    public class media_list_item_added extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_will_add_item extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_item_deleted extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_will_delete_item extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    /* media list view */
+    public class media_list_view_item_added extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_view_will_add_item extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_view_item_deleted extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_view_will_delete_item extends Structure
+    {
+
+        public LibVlcMedia item;
+
+        public int index;
+    }
+
+    public class media_list_player_next_item_set extends Structure
+    {
+        public LibVlcMedia item;
+    }
+
+    public class media_player_snapshot_taken extends Structure
+    {
+        public String psz_filename;
+    }
+
+    public class media_player_length_changed extends Structure
+    {
+        // @todo: check the type
+        public long new_length;
+    }
+
+    public class vlm_media_event extends Structure
+    {
+        public String psz_media_name;
+        public String psz_instance_name;
+    }
+
+    public class event_type_specific extends Union
+    {
+
+        public media_meta_changed media_meta_changed;
+
+        public media_subitem_added media_subitem_added;
+
+        public media_duration_changed media_duration_changed;
+
+        public media_preparsed_changed media_preparsed_changed;
+
+        public media_freed media_freed;
+
+        public media_state_changed media_state_changed;
+
+        public media_player_position_changed media_player_position_changed;
+
+        public media_player_time_changed media_player_time_changed;
+
+        public media_player_title_changed media_player_title_changed;
+
+        public media_player_seekable_changed media_player_seekable_changed;
+
+        public media_player_pausable_changed media_player_pausable_changed;
+
+        public media_list_item_added media_list_item_added;
+
+        public media_list_will_add_item media_list_will_add_item;
+
+        public media_list_item_deleted media_list_item_deleted;
+
+        public media_list_will_delete_item media_list_will_delete_item;
+
+        public media_list_view_item_added media_list_view_item_added;
+
+        public media_list_view_will_add_item media_list_view_will_add_item;
+
+        public media_list_view_item_deleted media_list_view_item_deleted;
+
+        public media_list_view_will_delete_item media_list_view_will_delete_item;
+
+        public media_list_player_next_item_set media_list_player_next_item_set;
+
+        public media_player_snapshot_taken media_player_snapshot_taken;
+
+        public media_player_length_changed media_player_length_changed;
+
+        public vlm_media_event vlm_media_event;
+    }
diff --git a/bindings/python-ctypes/boilerplate.java b/bindings/python-ctypes/boilerplate.java
new file mode 100644 (file)
index 0000000..d2836af
--- /dev/null
@@ -0,0 +1,26 @@
+/*****************************************************************************
+ * VLC Java Bindings JNA Glue
+ *****************************************************************************
+ * Copyright (C) 1998-2009 the VideoLAN team
+ *
+ * Authors: Filippo Carone <filippo@carone.org>
+ *          VLC bindings generator
+ *
+ *
+ * $Id $
+ *
+ * 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 of the License, 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
index b00fa9f0c3e68e395ba6eed106878da9690cc37e..6804efd8ef07448a16e63c0efd2897da2e634250 100755 (executable)
@@ -29,6 +29,7 @@ wrappers for most methods.
 """
 
 import sys
+import os
 import re
 import time
 import operator
@@ -513,7 +514,7 @@ class PythonGenerator(object):
         self.output("""if hasattr(dll, '%s'):""" % method)
         if params:
             self.output("    prototype=ctypes.CFUNCTYPE(%s, %s)" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
-                                                                ",".join( self.type2class[p[0]] for p in params )))
+                                                                ", ".join( self.type2class[p[0]] for p in params )))
         else:
             self.output("    prototype=ctypes.CFUNCTYPE(%s)" % self.type2class.get(rtype, 'FIXME_%s' % rtype))
 
@@ -523,7 +524,7 @@ class PythonGenerator(object):
         elif len(params) == 1:
             flags="    paramflags=( (%d, ), )" % parameter_passing[params[0][0]]
         else:
-            flags="    paramflags=%s" % ",".join( '(%d,)' % parameter_passing[p[0]] for p in params )
+            flags="    paramflags=%s" % ", ".join( '(%d,)' % parameter_passing[p[0]] for p in params )
         self.output(flags)
         self.output('    %s = prototype( ("%s", dll), paramflags )' % (method, method))
         if '3' in flags:
@@ -690,6 +691,186 @@ class PythonGenerator(object):
 """ % method)
         return ret
 
+class JavaGenerator(object):
+    # C-type to java/jna type conversion.
+    # Note that enum types conversions are generated (cf convert_enum_names)
+    type2class={
+        'libvlc_exception_t*': 'libvlc_exception_t',
+        'libvlc_media_player_t*': 'LibVlcMediaPlayer',
+        'libvlc_instance_t*': 'LibVlcInstance',
+        'libvlc_media_t*': 'LibVlcMedia',
+        'libvlc_log_t*': 'LibVlcLog',
+        'libvlc_log_iterator_t*': 'LibVlcLogIterator',
+        'libvlc_log_message_t*': 'libvlc_log_message_t',
+        'libvlc_event_type_t': 'int',
+        'libvlc_event_manager_t*': 'LibVlcEventManager',
+        'libvlc_media_discoverer_t*': 'LibVlcMediaDiscoverer',
+        'libvlc_media_library_t*': 'LibVlcMediaLibrary',
+        'libvlc_media_list_t*': 'LibVlcMediaList',
+        'libvlc_media_list_player_t*': 'LibVlcMediaListPlayer',
+        'libvlc_media_list_view_t*': 'LibVlcMediaListView',
+
+        'libvlc_track_description_t*': 'LibVlcTrackDescription',
+        'libvlc_audio_output_t*': 'LibVlcAudioOutput',
+
+        'void': 'void',
+        'void*': 'Pointer',
+        'short': 'short',
+        'char*': 'String',
+        'char**': 'String[]',
+        'uint32_t': 'uint32',
+        'float': 'float',
+        'unsigned': 'int',
+        'int': 'int',
+        '...': 'FIXMEva_list',
+        'libvlc_callback_t': 'LibVlcCallback',
+        'libvlc_time_t': 'long',
+
+        'mediacontrol_RGBPicture*': 'Pointer',
+        'mediacontrol_PlaylistSeq*': 'Pointer',
+        'mediacontrol_StreamInformation*': 'Pointer',
+        }
+
+    def __init__(self, parser=None):
+        self.parser=parser
+
+        # Blacklist all mediacontrol methods
+        for (rt, met, params, c) in self.parser.methods:
+            if met.startswith('mediacontrol'):
+                blacklist.append(met)
+        # Generate Java names for enums
+        self.type2class.update(self.convert_enum_names(parser.enums))
+        self.check_types()
+
+    def save(self, dirname=None):
+        if dirname is None or dirname == '-':
+            dirname='internal'
+            if not os.path.isdir(dirname):
+                os.mkdir(dirname)
+
+        print "Generating java code in %s/" % dirname
+
+        # Generate enum files
+        self.generate_enums(dirname, self.parser.enums)
+
+        # Generate LibVlc.java code
+        self.generate_libvlc(dirname)
+
+    def output(self, fd, *p):
+        fd.write(" ".join(p))
+        fd.write("\n")
+
+    def check_types(self):
+        """Make sure that all types are properly translated.
+
+        This method must be called *after* convert_enum_names, since
+        the latter populates type2class with converted enum names.
+        """
+        for (rt, met, params, c) in self.parser.methods:
+            if met in blacklist:
+                continue
+            for typ, name in params:
+                if not typ in self.type2class:
+                    raise Exception("No conversion for %s (from %s:%s)" % (typ, met, name))
+
+    def convert_enum_names(self, enums):
+        """Convert enum names into Java names.
+        """
+        res={}
+        for (typ, name, values, comment) in enums:
+            if typ != 'enum':
+                raise Exception('This method only handles enums')
+            pyname=re.findall('(libvlc|mediacontrol)_(.+?)(_t)?$', name)[0][1]
+            if '_' in pyname:
+                pyname=pyname.title().replace('_', '')
+            elif not pyname[0].isupper():
+                pyname=pyname.capitalize()
+            res[name]=pyname
+        return res
+
+    def insert_code(self, fd, filename):
+        """Generate header/footer code.
+        """
+        f=open(filename, 'r')
+        for l in f:
+            if l.startswith('build_date'):
+                self.output(fd, 'build_date="%s";' % time.ctime())
+            else:
+                self.output(fd, l.rstrip())
+        f.close()
+
+    def generate_header(self, fd):
+        """Generate LibVlc header.
+        """
+        for (c_type, jna_type) in self.type2class.iteritems():
+            if c_type.endswith('*') and jna_type.startswith('LibVlc'):
+                self.output(fd, '''    public class %s extends PointerType
+    {
+    }
+''' % jna_type)
+
+    def generate_libvlc(self, dirname):
+        """Generate LibVlc.java JNA glue code.
+        """
+        filename=os.path.join(dirname, 'LibVlc.java')
+        fd=open(filename, 'w')
+
+        self.insert_code(fd, 'boilerplate.java')
+        self.insert_code(fd, 'LibVlc-header.java')
+        #wrapped_methods=self.generate_wrappers(self.parser.methods)
+        self.generate_header(fd)
+        for (rtype, method, params, comment) in self.parser.methods:
+            if method in blacklist:
+                # FIXME
+                continue
+            self.output(fd, "%s %s(%s);\n" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
+                                          method,
+                                          ", ".join( ("%s %s" % (self.type2class[p[0]],
+                                                                 p[1])) for p in params )))
+        self.insert_code(fd, 'LibVlc-footer.java')
+        fd.close()
+
+    def generate_enums(self, dirname, enums):
+        """Generate JNA glue code for enums
+        """
+        for (typ, name, values, comment) in enums:
+            if typ != 'enum':
+                raise Exception('This method only handles enums')
+            javaname=self.type2class[name]
+
+            filename=javaname+".java"
+
+            fd=open(os.path.join(dirname, filename), 'w')
+
+            self.insert_code(fd, 'boilerplate.java')
+            self.output(fd, """package org.videolan.jvlc.internal;
+
+
+public enum %s
+{
+""" % javaname)
+            # FIXME: write comment
+
+            for k, v in values:
+                self.output(fd, "        %s, // %s," % (k, v))
+            self.output(fd, "}")
+            fd.close()
+
+    def fix_python_comment(self, c):
+        """Fix comment by removing first and last parameters (self and exception)
+        """
+        data=c.replace('@{', '').replace('@see', 'See').splitlines()
+        body=itertools.takewhile(lambda l: not '@param' in l and not '@return' in l, data)
+        param=[ python_param_re.sub('\\1:\\2', l) for l in  itertools.ifilter(lambda l: '@param' in l, data) ]
+        ret=[ l.replace('@return', '@return:') for l in itertools.ifilter(lambda l: '@return' in l, data) ]
+
+        if len(param) >= 2:
+            param=param[1:-1]
+        elif len(param) == 1:
+            param=[]
+
+        return "\n".join(itertools.chain(body, param, ret))
+
 def process(output, list_of_includes):
     p=Parser(list_of_includes)
     g=PythonGenerator(p)
@@ -707,9 +888,13 @@ if __name__ == '__main__':
                       default=False,
                       help="Check mode")
 
+    opt.add_option("-j", "--java", dest="java", action="store_true",
+                      default=False,
+                      help="Generate java bindings (default is python)")
+
     opt.add_option("-o", "--output", dest="output", action="store",
                       type="str", default="-",
-                      help="Output filename")
+                      help="Output filename(python)/dirname(java)")
 
     (options, args) = opt.parse_args()
 
@@ -735,6 +920,9 @@ if __name__ == '__main__':
     if options.check or options.debug:
         sys.exit(0)
 
-    g=PythonGenerator(p)
+    if options.java:
+        g=JavaGenerator(p)
+    else:
+        g=PythonGenerator(p)
 
     g.save(options.output)