From 0fbc80872297a91a1868bba02372e60fe8492dee Mon Sep 17 00:00:00 2001 From: Olivier Aubert Date: Wed, 23 Sep 2009 16:17:18 +0200 Subject: [PATCH] python-ctypes: first shot at generating java JNA glue code Not perfect yet (esp. enum classes are not correctly translated for discontinuous enums). --- bindings/python-ctypes/LibVlc-footer.java | 1 + bindings/python-ctypes/LibVlc-header.java | 255 ++++++++++++++++++++++ bindings/python-ctypes/boilerplate.java | 26 +++ bindings/python-ctypes/generate.py | 196 ++++++++++++++++- 4 files changed, 474 insertions(+), 4 deletions(-) create mode 100644 bindings/python-ctypes/LibVlc-footer.java create mode 100644 bindings/python-ctypes/LibVlc-header.java create mode 100644 bindings/python-ctypes/boilerplate.java diff --git a/bindings/python-ctypes/LibVlc-footer.java b/bindings/python-ctypes/LibVlc-footer.java new file mode 100644 index 0000000000..5c34318c21 --- /dev/null +++ b/bindings/python-ctypes/LibVlc-footer.java @@ -0,0 +1 @@ +} diff --git a/bindings/python-ctypes/LibVlc-header.java b/bindings/python-ctypes/LibVlc-header.java new file mode 100644 index 0000000000..89f3bfed98 --- /dev/null +++ b/bindings/python-ctypes/LibVlc-header.java @@ -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 index 0000000000..d2836afd09 --- /dev/null +++ b/bindings/python-ctypes/boilerplate.java @@ -0,0 +1,26 @@ +/***************************************************************************** + * VLC Java Bindings JNA Glue + ***************************************************************************** + * Copyright (C) 1998-2009 the VideoLAN team + * + * Authors: Filippo Carone + * 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. + *****************************************************************************/ + diff --git a/bindings/python-ctypes/generate.py b/bindings/python-ctypes/generate.py index b00fa9f0c3..6804efd8ef 100755 --- a/bindings/python-ctypes/generate.py +++ b/bindings/python-ctypes/generate.py @@ -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) -- 2.39.2