1
/***************************************************************************
3
* This file is part of the KDE project
4
* copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Library General Public
8
* License as published by the Free Software Foundation; either
9
* version 2 of the License, or (at your option) any later version.
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 GNU
13
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public License
15
* along with this program; see the file COPYING. If not, write to
16
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
18
***************************************************************************/
19
#include "rubyextension.h"
28
#include "rubyconfig.h"
35
class RubyExtensionPrivate {
36
friend class RubyExtension;
37
/// The \a Kross::Api::Object this RubyExtension wraps.
38
Kross::Api::Object::Ptr m_object;
40
static VALUE s_krossObject;
41
static VALUE s_krossException;
44
VALUE RubyExtensionPrivate::s_krossObject = 0;
45
VALUE RubyExtensionPrivate::s_krossException = 0;
47
VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
49
#ifdef KROSS_RUBY_EXTENSION_DEBUG
50
kdDebug() << "method_missing(argc, argv, self)" << endl;
56
#ifdef KROSS_RUBY_EXTENSION_DEBUG
57
kdDebug() << "Converting self to Kross::Api::Object" << endl;
60
Kross::Api::Object::Ptr object = toObject( self );
61
return RubyExtension::call_method(object, argc, argv);
64
VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
66
QString funcname = rb_id2name(SYM2ID(argv[0]));
67
QValueList<Api::Object::Ptr> argsList;
68
#ifdef KROSS_RUBY_EXTENSION_DEBUG
69
kdDebug() << "Building arguments list for function : " << funcname << " there are " << (argc-1) << " arguments." << endl;
71
for(int i = 1; i < argc; i++)
73
Kross::Api::Object::Ptr obj = toObject(argv[i]);
74
if(obj) argsList.append(obj);
76
Kross::Api::Object::Ptr result;
77
try { // We need a double try/catch because, the cleaning is only done at the end of the catch, so if we had only one try/catch, kross would crash after the call to rb_exc_raise
78
try { // We can't let a C++ exceptions propagate in the C mechanism
79
if(object->hasChild(funcname)) {
80
#ifdef KROSS_RUBY_EXTENSION_DEBUG
81
kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) << endl;
83
result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
86
#ifdef KROSS_RUBY_EXTENSION_DEBUG
87
kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) << endl;
89
result = object->call(funcname, new Api::List(argsList));
91
} catch(Kross::Api::Exception::Ptr exception)
93
#ifdef KROSS_RUBY_EXTENSION_DEBUG
94
kdDebug() << "c++ exception catched, raise a ruby error" << endl;
96
throw convertFromException(exception);
99
throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n
104
return toVALUE(result);
107
void RubyExtension::delete_object(void* object)
109
kdDebug() << "delete_object" << endl;
110
RubyExtension* obj = static_cast<RubyExtension*>(object);
115
void RubyExtension::delete_exception(void* object)
117
Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object);
118
exc->_KShared_unref();
122
RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
124
d->m_object = object;
128
RubyExtension::~RubyExtension()
130
kdDebug() << "Delete RubyExtension" << endl;
134
typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
136
int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap)
138
QMap<QString, Kross::Api::Object::Ptr>* map;
139
Data_Get_Struct(vmap, mStrObj, map);
142
Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
143
if(o) map->replace(STR2CSTR(key), o);
148
bool RubyExtension::isOfExceptionType(VALUE value)
150
VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException );
151
return (TYPE(result) == T_TRUE);
154
bool RubyExtension::isOfObjectType(VALUE value)
156
VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
157
return (TYPE(result) == T_TRUE);
161
Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value)
163
if( isOfExceptionType(value) )
165
Kross::Api::Exception* exception;
166
Data_Get_Struct(value, Kross::Api::Exception, exception);
172
VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc)
174
if(RubyExtensionPrivate::s_krossException == 0)
176
RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError);
179
return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() );
183
Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
185
#ifdef KROSS_RUBY_EXTENSION_DEBUG
186
kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
188
switch( TYPE( value ) )
192
#ifdef KROSS_RUBY_EXTENSION_DEBUG
193
kdDebug() << "Object is a Kross Object" << endl;
195
if( isOfObjectType(value) )
197
RubyExtension* objectExtension;
198
Data_Get_Struct(value, RubyExtension, objectExtension);
199
Kross::Api::Object::Ptr object = objectExtension->d->m_object;
202
kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
207
return new Kross::Api::Variant(NUM2DBL(value));
209
return new Kross::Api::Variant(QString(STR2CSTR(value)));
212
QValueList<Kross::Api::Object::Ptr> l;
213
for(int i = 0; i < RARRAY(value)->len; i++)
215
Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
218
return new Kross::Api::List(l);
221
return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
224
QMap<QString, Kross::Api::Object::Ptr> map;
225
VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
226
rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
227
return new Kross::Api::Dict(map);
231
return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
235
return new Kross::Api::Variant(true);
239
return new Kross::Api::Variant(false);
243
return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
253
kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
260
VALUE RubyExtension::toVALUE(const QString& s)
262
return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
265
VALUE RubyExtension::toVALUE(QStringList list)
267
VALUE l = rb_ary_new();
268
for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
269
rb_ary_push(l, toVALUE(*it));
274
VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
276
VALUE h = rb_hash_new();
277
for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
278
rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
283
VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
285
VALUE l = rb_ary_new();
286
for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
287
rb_ary_push(l, toVALUE(*it));
292
VALUE RubyExtension::toVALUE(const QVariant& variant)
295
switch(variant.type()) {
296
case QVariant::Invalid:
299
return (variant.toBool()) ? Qtrue : Qfalse;
301
return INT2FIX(variant.toInt());
303
return UINT2NUM(variant.toUInt());
304
case QVariant::Double:
305
return rb_float_new(variant.toDouble());
308
case QVariant::DateTime:
309
case QVariant::ByteArray:
310
case QVariant::BitArray:
311
case QVariant::CString:
312
case QVariant::String:
313
return toVALUE(variant.toString());
314
case QVariant::StringList:
315
return toVALUE(variant.toStringList());
317
return toVALUE(variant.toMap());
319
return toVALUE(variant.toList());
321
// To handle following both cases is a bit difficult
322
// cause Python doesn't spend an easy possibility
323
// for such large numbers (TODO maybe BigInt?). So,
324
// we risk overflows here, but well...
325
case QVariant::LongLong: {
326
return INT2NUM((long)variant.toLongLong());
328
case QVariant::ULongLong:
329
return UINT2NUM((unsigned long)variant.toULongLong());
331
kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
337
VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
343
if(object->getClassName() == "Kross::Api::Variant") {
344
QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
348
if(object->getClassName() == "Kross::Api::List") {
349
Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
350
return toVALUE((Kross::Api::List::Ptr)list);
353
if(object->getClassName() == "Kross::Api::Dict") {
354
Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
355
return toVALUE((Kross::Api::Dict::Ptr)dict);
358
if(RubyExtensionPrivate::s_krossObject == 0)
360
RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
361
rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing", (VALUE (*)(...))RubyExtension::method_missing, -1);
363
return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) );
366
VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
368
VALUE l = rb_ary_new();
369
uint count = list ? list->count() : 0;
370
for(uint i = 0; i < count; i++)
371
rb_ary_push(l, toVALUE(list->item(i)));