rubyextension.cpp

00001 /***************************************************************************
00002  * rubyinterpreter.cpp
00003  * This file is part of the KDE project
00004  * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this program; see the file COPYING.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
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;
00037     /// The \a Kross::Api::Object this RubyExtension wraps.
00038     Kross::Api::Object::Ptr m_object;
00039     /// 
00040     static VALUE s_krossObject;
00041 };
00042 
00043 VALUE RubyExtensionPrivate::s_krossObject = 0;
00044     
00045 VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
00046 {
00047 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00048     kdDebug() << "method_missing(argc, argv, self)" << endl;
00049 #endif
00050     if(argc < 1)
00051     {
00052         return 0;
00053     }
00054 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00055     kdDebug() << "Converting self to Kross::Api::Object" << endl;
00056 #endif
00057     
00058     Kross::Api::Object::Ptr object = toObject( self );
00059     return RubyExtension::call_method(object, argc, argv);
00060 }
00061 
00062 VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
00063 {
00064     QString funcname = rb_id2name(SYM2ID(argv[0]));
00065     QValueList<Api::Object::Ptr> argsList;
00066 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00067     kdDebug() << "Building arguments list for function : " << funcname << " there are " << (argc-1) << " arguments." << endl;
00068 #endif
00069     for(int i = 1; i < argc; i++)
00070     {
00071         Kross::Api::Object::Ptr obj = toObject(argv[i]);
00072         if(obj) argsList.append(obj);
00073     }
00074     Kross::Api::Object::Ptr result;
00075     if(object->hasChild(funcname)) {
00076 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00077         kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) << endl;
00078 #endif
00079         result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
00080     }
00081     else {
00082 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00083         kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) << endl;
00084 #endif
00085         result = object->call(funcname, new Api::List(argsList));
00086     }
00087     
00088     return toVALUE(result);
00089 }
00090     
00091 RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
00092 {
00093     d->m_object = object;
00094 }
00095 
00096 
00097 RubyExtension::~RubyExtension()
00098 {
00099 }
00100 
00101 typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
00102 
00103 int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE  vmap)
00104 {
00105     QMap<QString, Kross::Api::Object::Ptr>* map; 
00106     Data_Get_Struct(vmap, mStrObj, map);
00107     if (key != Qundef)
00108     {
00109         Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
00110         if(o) map->replace(STR2CSTR(key), o);
00111     }
00112     return ST_CONTINUE;
00113 }
00114 
00115 Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
00116 {
00117 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00118     kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
00119 #endif
00120     switch( TYPE( value ) )
00121     {
00122         case T_DATA:
00123         {
00124 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00125             kdDebug() << "Object is a Kross Object" << endl;
00126 #endif
00127             VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
00128             if(TYPE(result) == T_TRUE)
00129             {
00130                 RubyExtension* objectExtension;
00131                 Data_Get_Struct(value, RubyExtension, objectExtension);
00132                 Kross::Api::Object::Ptr object = objectExtension->d->m_object;
00133                 return object;
00134             } else {
00135                 kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
00136                 return 0;
00137             }
00138         }
00139         case T_FLOAT:
00140             return new Kross::Api::Variant(NUM2DBL(value));
00141         case T_STRING:
00142             return new Kross::Api::Variant(QString(STR2CSTR(value)));
00143         case T_ARRAY:
00144         {
00145             QValueList<Kross::Api::Object::Ptr> l;
00146             for(int i = 0; i < RARRAY(value)->len; i++)
00147             {
00148                 Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
00149                 if(o) l.append(o);
00150             }
00151             return new Kross::Api::List(l);
00152         }
00153         case T_FIXNUM:
00154             return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
00155         case T_HASH:
00156         {
00157             QMap<QString, Kross::Api::Object::Ptr> map;
00158             VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
00159             rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
00160             return new Kross::Api::Dict(map);
00161         }
00162         case T_BIGNUM:
00163         {
00164             return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
00165         }
00166         case T_TRUE:
00167         {
00168             return new Kross::Api::Variant(true);
00169         }
00170         case T_FALSE:
00171         {
00172             return new Kross::Api::Variant(false);
00173         }
00174         case T_SYMBOL:
00175         {
00176             return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
00177         }
00178         case T_MATCH:
00179         case T_OBJECT:
00180         case T_FILE:
00181         case T_STRUCT:
00182         case T_REGEXP:
00183         case T_MODULE:
00184         case T_ICLASS:
00185         case T_CLASS:
00186             kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
00187         default:
00188         case T_NIL:
00189             return 0;
00190     }
00191 }
00192 
00193 VALUE RubyExtension::toVALUE(const QString& s)
00194 {
00195     return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
00196 }
00197 
00198 VALUE RubyExtension::toVALUE(QStringList list)
00199 {
00200     VALUE l = rb_ary_new();
00201     for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00202         rb_ary_push(l, toVALUE(*it));
00203     return l;
00204 }
00205 
00206 
00207 VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
00208 {
00209     VALUE h = rb_hash_new();
00210     for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
00211         rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
00212     return h;
00213 
00214 }
00215 
00216 VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
00217 {
00218     VALUE l = rb_ary_new();
00219     for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
00220         rb_ary_push(l, toVALUE(*it));
00221     return l;
00222 }
00223 
00224 
00225 VALUE RubyExtension::toVALUE(const QVariant& variant)
00226 {
00227     
00228     switch(variant.type()) {
00229         case QVariant::Invalid:
00230             return Qnil;
00231         case QVariant::Bool:
00232             return (variant.toBool()) ? Qtrue : Qfalse;
00233         case QVariant::Int:
00234             return INT2FIX(variant.toInt());
00235         case QVariant::UInt:
00236             return UINT2NUM(variant.toUInt());
00237         case QVariant::Double:
00238             return rb_float_new(variant.toDouble());
00239         case QVariant::Date:
00240         case QVariant::Time:
00241         case QVariant::DateTime:
00242         case QVariant::ByteArray:
00243         case QVariant::BitArray:
00244         case QVariant::CString:
00245         case QVariant::String:
00246             return toVALUE(variant.toString());
00247         case QVariant::StringList:
00248             return toVALUE(variant.toStringList());
00249         case QVariant::Map:
00250             return toVALUE(variant.toMap());
00251         case QVariant::List:
00252             return toVALUE(variant.toList());
00253 
00254         // To handle following both cases is a bit difficult
00255         // cause Python doesn't spend an easy possibility
00256         // for such large numbers (TODO maybe BigInt?). So,
00257         // we risk overflows here, but well...
00258         case QVariant::LongLong: {
00259             return INT2NUM((long)variant.toLongLong());
00260         }
00261         case QVariant::ULongLong:
00262             return UINT2NUM((unsigned long)variant.toULongLong());
00263         default: {
00264             kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
00265             return Qundef;
00266         }
00267     }
00268 }
00269 
00270 VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
00271 {
00272     if(! object) {
00273         return 0;
00274     }
00275 
00276     if(object->getClassName() == "Kross::Api::Variant") {
00277         QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
00278         return toVALUE(v);
00279     }
00280 
00281     if(object->getClassName() == "Kross::Api::List") {
00282         Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
00283         return toVALUE((Kross::Api::List::Ptr)list);
00284     }
00285 
00286     if(object->getClassName() == "Kross::Api::Dict") {
00287         Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
00288         return toVALUE((Kross::Api::Dict::Ptr)dict);
00289     }
00290 
00291     if(RubyExtensionPrivate::s_krossObject == 0)
00292     {
00293         RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
00294         rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing",  (VALUE (*)(...))RubyExtension::method_missing, -1);
00295     }
00296     return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, free, new RubyExtension(object) );
00297 }
00298 
00299 VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
00300 {
00301     VALUE l = rb_ary_new();
00302     uint count = list ? list->count() : 0;
00303     for(uint i = 0; i < count; i++)
00304         rb_ary_push(l, toVALUE(list->item(i)));
00305     return l;
00306 
00307 }
00308 
00309 }
00310 
00311 }

Generated on Thu Feb 9 17:59:10 2006 for Kross by  doxygen 1.4.6