00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "rubyextension.h"
00020
00021 #include <st.h>
00022
00023 #include <qmap.h>
00024 #include <qstring.h>
00025
00026 #include "api/list.h"
00027
00028 #include "rubyconfig.h"
00029
00030 namespace Kross {
00031
00032 namespace Ruby {
00033
00034
00035 class RubyExtensionPrivate {
00036 friend class RubyExtension;
00038 Kross::Api::Object::Ptr m_object;
00040 static VALUE s_krossObject;
00041 static VALUE s_krossException;
00042 };
00043
00044 VALUE RubyExtensionPrivate::s_krossObject = 0;
00045 VALUE RubyExtensionPrivate::s_krossException = 0;
00046
00047 VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
00048 {
00049 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00050 kdDebug() << "method_missing(argc, argv, self)" << endl;
00051 #endif
00052 if(argc < 1)
00053 {
00054 return 0;
00055 }
00056 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00057 kdDebug() << "Converting self to Kross::Api::Object" << endl;
00058 #endif
00059
00060 Kross::Api::Object::Ptr object = toObject( self );
00061 return RubyExtension::call_method(object, argc, argv);
00062 }
00063
00064 VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
00065 {
00066 QString funcname = rb_id2name(SYM2ID(argv[0]));
00067 QValueList<Api::Object::Ptr> argsList;
00068 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00069 kdDebug() << "Building arguments list for function : " << funcname << " there are " << (argc-1) << " arguments." << endl;
00070 #endif
00071 for(int i = 1; i < argc; i++)
00072 {
00073 Kross::Api::Object::Ptr obj = toObject(argv[i]);
00074 if(obj) argsList.append(obj);
00075 }
00076 Kross::Api::Object::Ptr result;
00077 try {
00078 try {
00079 if(object->hasChild(funcname)) {
00080 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00081 kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) << endl;
00082 #endif
00083 result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
00084 }
00085 else {
00086 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00087 kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) << endl;
00088 #endif
00089 result = object->call(funcname, new Api::List(argsList));
00090 }
00091 } catch(Kross::Api::Exception::Ptr exception)
00092 {
00093 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00094 kdDebug() << "c++ exception catched, raise a ruby error" << endl;
00095 #endif
00096 throw convertFromException(exception);
00097 } catch(...)
00098 {
00099 throw convertFromException(new Kross::Api::Exception( "Unknow error" ));
00100 }
00101 } catch(VALUE v) {
00102 rb_exc_raise(v );
00103 }
00104 return toVALUE(result);
00105 }
00106
00107 void RubyExtension::delete_object(void* object)
00108 {
00109 kdDebug() << "delete_object" << endl;
00110 RubyExtension* obj = static_cast<RubyExtension*>(object);
00111 if(obj)
00112 delete obj;
00113 }
00114
00115 void RubyExtension::delete_exception(void* object)
00116 {
00117 Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object);
00118 exc->_KShared_unref();
00119 }
00120
00121
00122 RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
00123 {
00124 d->m_object = object;
00125 }
00126
00127
00128 RubyExtension::~RubyExtension()
00129 {
00130 kdDebug() << "Delete RubyExtension" << endl;
00131 delete d;
00132 }
00133
00134 typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
00135
00136 int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap)
00137 {
00138 QMap<QString, Kross::Api::Object::Ptr>* map;
00139 Data_Get_Struct(vmap, mStrObj, map);
00140 if (key != Qundef)
00141 {
00142 Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
00143 if(o) map->replace(STR2CSTR(key), o);
00144 }
00145 return ST_CONTINUE;
00146 }
00147
00148 bool RubyExtension::isOfExceptionType(VALUE value)
00149 {
00150 VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException );
00151 return (TYPE(result) == T_TRUE);
00152 }
00153
00154 bool RubyExtension::isOfObjectType(VALUE value)
00155 {
00156 VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
00157 return (TYPE(result) == T_TRUE);
00158 }
00159
00160
00161 Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value)
00162 {
00163 if( isOfExceptionType(value) )
00164 {
00165 Kross::Api::Exception* exception;
00166 Data_Get_Struct(value, Kross::Api::Exception, exception);
00167 return exception;
00168 }
00169 return 0;
00170 }
00171
00172 VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc)
00173 {
00174 if(RubyExtensionPrivate::s_krossException == 0)
00175 {
00176 RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError);
00177 }
00178 exc->_KShared_ref();
00179 return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() );
00180 }
00181
00182
00183 Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
00184 {
00185 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00186 kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
00187 #endif
00188 switch( TYPE( value ) )
00189 {
00190 case T_DATA:
00191 {
00192 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00193 kdDebug() << "Object is a Kross Object" << endl;
00194 #endif
00195 if( isOfObjectType(value) )
00196 {
00197 RubyExtension* objectExtension;
00198 Data_Get_Struct(value, RubyExtension, objectExtension);
00199 Kross::Api::Object::Ptr object = objectExtension->d->m_object;
00200 return object;
00201 } else {
00202 kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
00203 return 0;
00204 }
00205 }
00206 case T_FLOAT:
00207 return new Kross::Api::Variant(NUM2DBL(value));
00208 case T_STRING:
00209 return new Kross::Api::Variant(QString(STR2CSTR(value)));
00210 case T_ARRAY:
00211 {
00212 QValueList<Kross::Api::Object::Ptr> l;
00213 for(int i = 0; i < RARRAY(value)->len; i++)
00214 {
00215 Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
00216 if(o) l.append(o);
00217 }
00218 return new Kross::Api::List(l);
00219 }
00220 case T_FIXNUM:
00221 return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
00222 case T_HASH:
00223 {
00224 QMap<QString, Kross::Api::Object::Ptr> map;
00225 VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
00226 rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
00227 return new Kross::Api::Dict(map);
00228 }
00229 case T_BIGNUM:
00230 {
00231 return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
00232 }
00233 case T_TRUE:
00234 {
00235 return new Kross::Api::Variant(true);
00236 }
00237 case T_FALSE:
00238 {
00239 return new Kross::Api::Variant(false);
00240 }
00241 case T_SYMBOL:
00242 {
00243 return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
00244 }
00245 case T_MATCH:
00246 case T_OBJECT:
00247 case T_FILE:
00248 case T_STRUCT:
00249 case T_REGEXP:
00250 case T_MODULE:
00251 case T_ICLASS:
00252 case T_CLASS:
00253 kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
00254 default:
00255 case T_NIL:
00256 return 0;
00257 }
00258 }
00259
00260 VALUE RubyExtension::toVALUE(const QString& s)
00261 {
00262 return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
00263 }
00264
00265 VALUE RubyExtension::toVALUE(QStringList list)
00266 {
00267 VALUE l = rb_ary_new();
00268 for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00269 rb_ary_push(l, toVALUE(*it));
00270 return l;
00271 }
00272
00273
00274 VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
00275 {
00276 VALUE h = rb_hash_new();
00277 for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
00278 rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
00279 return h;
00280
00281 }
00282
00283 VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
00284 {
00285 VALUE l = rb_ary_new();
00286 for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
00287 rb_ary_push(l, toVALUE(*it));
00288 return l;
00289 }
00290
00291
00292 VALUE RubyExtension::toVALUE(const QVariant& variant)
00293 {
00294
00295 switch(variant.type()) {
00296 case QVariant::Invalid:
00297 return Qnil;
00298 case QVariant::Bool:
00299 return (variant.toBool()) ? Qtrue : Qfalse;
00300 case QVariant::Int:
00301 return INT2FIX(variant.toInt());
00302 case QVariant::UInt:
00303 return UINT2NUM(variant.toUInt());
00304 case QVariant::Double:
00305 return rb_float_new(variant.toDouble());
00306 case QVariant::Date:
00307 case QVariant::Time:
00308 case QVariant::DateTime:
00309 case QVariant::ByteArray:
00310 case QVariant::BitArray:
00311 case QVariant::CString:
00312 case QVariant::String:
00313 return toVALUE(variant.toString());
00314 case QVariant::StringList:
00315 return toVALUE(variant.toStringList());
00316 case QVariant::Map:
00317 return toVALUE(variant.toMap());
00318 case QVariant::List:
00319 return toVALUE(variant.toList());
00320
00321
00322
00323
00324
00325 case QVariant::LongLong: {
00326 return INT2NUM((long)variant.toLongLong());
00327 }
00328 case QVariant::ULongLong:
00329 return UINT2NUM((unsigned long)variant.toULongLong());
00330 default: {
00331 kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
00332 return Qundef;
00333 }
00334 }
00335 }
00336
00337 VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
00338 {
00339 if(! object) {
00340 return 0;
00341 }
00342
00343 if(object->getClassName() == "Kross::Api::Variant") {
00344 QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
00345 return toVALUE(v);
00346 }
00347
00348 if(object->getClassName() == "Kross::Api::List") {
00349 Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
00350 return toVALUE((Kross::Api::List::Ptr)list);
00351 }
00352
00353 if(object->getClassName() == "Kross::Api::Dict") {
00354 Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
00355 return toVALUE((Kross::Api::Dict::Ptr)dict);
00356 }
00357
00358 if(RubyExtensionPrivate::s_krossObject == 0)
00359 {
00360 RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
00361 rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing", (VALUE (*)(...))RubyExtension::method_missing, -1);
00362 }
00363 return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) );
00364 }
00365
00366 VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
00367 {
00368 VALUE l = rb_ary_new();
00369 uint count = list ? list->count() : 0;
00370 for(uint i = 0; i < count; i++)
00371 rb_ary_push(l, toVALUE(list->item(i)));
00372 return l;
00373
00374 }
00375
00376 }
00377
00378 }