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 krossdebug("method_missing(argc, argv, self)");
00051 #endif
00052 if(argc < 1)
00053 {
00054 return 0;
00055 }
00056 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00057 krossdebug("Converting self to Kross::Api::Object");
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 krossdebug(QString("Building arguments list for function: %1 there are %2 arguments.").arg(funcname).arg(argc-1));
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 Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(object.data());
00080 if(callable && callable->hasChild(funcname)) {
00081 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00082 krossdebug( QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) );
00083 #endif
00084 result = callable->getChild(funcname)->call(QString::null, new Api::List(argsList));
00085 }
00086 else {
00087 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00088 krossdebug( QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) );
00089 #endif
00090 result = object->call(funcname, new Api::List(argsList));
00091 }
00092 } catch(Kross::Api::Exception::Ptr exception)
00093 {
00094 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00095 krossdebug("c++ exception catched, raise a ruby error");
00096 #endif
00097 throw convertFromException(exception);
00098 } catch(...)
00099 {
00100 throw convertFromException(new Kross::Api::Exception( "Unknow error" ));
00101 }
00102 } catch(VALUE v) {
00103 rb_exc_raise(v );
00104 }
00105 return toVALUE(result);
00106 }
00107
00108 void RubyExtension::delete_object(void* object)
00109 {
00110 krossdebug("delete_object");
00111 RubyExtension* obj = static_cast<RubyExtension*>(object);
00112 if(obj)
00113 delete obj;
00114 }
00115
00116 void RubyExtension::delete_exception(void* object)
00117 {
00118 Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object);
00119 exc->_KShared_unref();
00120 }
00121
00122
00123 RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
00124 {
00125 d->m_object = object;
00126 }
00127
00128
00129 RubyExtension::~RubyExtension()
00130 {
00131 krossdebug("Delete RubyExtension");
00132 delete d;
00133 }
00134
00135 typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
00136
00137 int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap)
00138 {
00139 QMap<QString, Kross::Api::Object::Ptr>* map;
00140 Data_Get_Struct(vmap, mStrObj, map);
00141 if (key != Qundef)
00142 {
00143 Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
00144 if(o) map->replace(STR2CSTR(key), o);
00145 }
00146 return ST_CONTINUE;
00147 }
00148
00149 bool RubyExtension::isOfExceptionType(VALUE value)
00150 {
00151 VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException );
00152 return (TYPE(result) == T_TRUE);
00153 }
00154
00155 bool RubyExtension::isOfObjectType(VALUE value)
00156 {
00157 VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
00158 return (TYPE(result) == T_TRUE);
00159 }
00160
00161
00162 Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value)
00163 {
00164 if( isOfExceptionType(value) )
00165 {
00166 Kross::Api::Exception* exception;
00167 Data_Get_Struct(value, Kross::Api::Exception, exception);
00168 return exception;
00169 }
00170 return 0;
00171 }
00172
00173 VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc)
00174 {
00175 if(RubyExtensionPrivate::s_krossException == 0)
00176 {
00177 RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError);
00178 }
00179 exc->_KShared_ref();
00180 return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() );
00181 }
00182
00183
00184 Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
00185 {
00186 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00187 krossdebug(QString("RubyExtension::toObject of type %1").arg(TYPE(value)));
00188 #endif
00189 switch( TYPE( value ) )
00190 {
00191 case T_DATA:
00192 {
00193 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00194 krossdebug("Object is a Kross Object");
00195 #endif
00196 if( isOfObjectType(value) )
00197 {
00198 RubyExtension* objectExtension;
00199 Data_Get_Struct(value, RubyExtension, objectExtension);
00200 Kross::Api::Object::Ptr object = objectExtension->d->m_object;
00201 return object;
00202 } else {
00203 krosswarning("Cannot yet convert standard ruby type to kross object");
00204 return 0;
00205 }
00206 }
00207 case T_FLOAT:
00208 return new Kross::Api::Variant(NUM2DBL(value));
00209 case T_STRING:
00210 return new Kross::Api::Variant(QString(STR2CSTR(value)));
00211 case T_ARRAY:
00212 {
00213 QValueList<Kross::Api::Object::Ptr> l;
00214 for(int i = 0; i < RARRAY(value)->len; i++)
00215 {
00216 Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
00217 if(o) l.append(o);
00218 }
00219 return new Kross::Api::List(l);
00220 }
00221 case T_FIXNUM:
00222 return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
00223 case T_HASH:
00224 {
00225 QMap<QString, Kross::Api::Object::Ptr> map;
00226 VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
00227 rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
00228 return new Kross::Api::Dict(map);
00229 }
00230 case T_BIGNUM:
00231 {
00232 return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
00233 }
00234 case T_TRUE:
00235 {
00236 return new Kross::Api::Variant(true);
00237 }
00238 case T_FALSE:
00239 {
00240 return new Kross::Api::Variant(false);
00241 }
00242 case T_SYMBOL:
00243 {
00244 return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
00245 }
00246 case T_MATCH:
00247 case T_OBJECT:
00248 case T_FILE:
00249 case T_STRUCT:
00250 case T_REGEXP:
00251 case T_MODULE:
00252 case T_ICLASS:
00253 case T_CLASS:
00254 krosswarning(QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)));
00255 default:
00256 case T_NIL:
00257 return 0;
00258 }
00259 }
00260
00261 VALUE RubyExtension::toVALUE(const QString& s)
00262 {
00263 return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
00264 }
00265
00266 VALUE RubyExtension::toVALUE(QStringList list)
00267 {
00268 VALUE l = rb_ary_new();
00269 for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00270 rb_ary_push(l, toVALUE(*it));
00271 return l;
00272 }
00273
00274
00275 VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
00276 {
00277 VALUE h = rb_hash_new();
00278 for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
00279 rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
00280 return h;
00281
00282 }
00283
00284 VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
00285 {
00286 VALUE l = rb_ary_new();
00287 for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
00288 rb_ary_push(l, toVALUE(*it));
00289 return l;
00290 }
00291
00292
00293 VALUE RubyExtension::toVALUE(const QVariant& variant)
00294 {
00295
00296 switch(variant.type()) {
00297 case QVariant::Invalid:
00298 return Qnil;
00299 case QVariant::Bool:
00300 return (variant.toBool()) ? Qtrue : Qfalse;
00301 case QVariant::Int:
00302 return INT2FIX(variant.toInt());
00303 case QVariant::UInt:
00304 return UINT2NUM(variant.toUInt());
00305 case QVariant::Double:
00306 return rb_float_new(variant.toDouble());
00307 case QVariant::Date:
00308 case QVariant::Time:
00309 case QVariant::DateTime:
00310 case QVariant::ByteArray:
00311 case QVariant::BitArray:
00312 case QVariant::CString:
00313 case QVariant::String:
00314 return toVALUE(variant.toString());
00315 case QVariant::StringList:
00316 return toVALUE(variant.toStringList());
00317 case QVariant::Map:
00318 return toVALUE(variant.toMap());
00319 case QVariant::List:
00320 return toVALUE(variant.toList());
00321
00322
00323
00324
00325
00326 case QVariant::LongLong: {
00327 return INT2NUM((long)variant.toLongLong());
00328 }
00329 case QVariant::ULongLong:
00330 return UINT2NUM((unsigned long)variant.toULongLong());
00331 default: {
00332 krosswarning( QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) );
00333 return Qundef;
00334 }
00335 }
00336 }
00337
00338 VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
00339 {
00340 if(! object.data()) {
00341 return 0;
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 }