diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kross/ruby/rubyextension.cpp | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib/kross/ruby/rubyextension.cpp')
-rw-r--r-- | lib/kross/ruby/rubyextension.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/lib/kross/ruby/rubyextension.cpp b/lib/kross/ruby/rubyextension.cpp new file mode 100644 index 000000000..2c022cee9 --- /dev/null +++ b/lib/kross/ruby/rubyextension.cpp @@ -0,0 +1,378 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#include "rubyextension.h" + +#include <st.h> + +#include <qmap.h> +#include <qstring.h> + +#include "api/list.h" + +#include "rubyconfig.h" + +namespace Kross { + +namespace Ruby { + + +class RubyExtensionPrivate { + friend class RubyExtension; + /// The \a Kross::Api::Object this RubyExtension wraps. + Kross::Api::Object::Ptr m_object; + /// + static VALUE s_krossObject; + static VALUE s_krossException; +}; + +VALUE RubyExtensionPrivate::s_krossObject = 0; +VALUE RubyExtensionPrivate::s_krossException = 0; + +VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self) +{ +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("method_missing(argc, argv, self)"); +#endif + if(argc < 1) + { + return 0; + } +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("Converting self to Kross::Api::Object"); +#endif + + Kross::Api::Object::Ptr object = toObject( self ); + return RubyExtension::call_method(object, argc, argv); +} + +VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv) +{ + QString funcname = rb_id2name(SYM2ID(argv[0])); + QValueList<Api::Object::Ptr> argsList; +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug(QString("Building arguments list for function: %1 there are %2 arguments.").arg(funcname).arg(argc-1)); +#endif + for(int i = 1; i < argc; i++) + { + Kross::Api::Object::Ptr obj = toObject(argv[i]); + if(obj) argsList.append(obj); + } + Kross::Api::Object::Ptr result; + 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 + try { // We can't let a C++ exceptions propagate in the C mechanism + Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(object.data()); + if(callable && callable->hasChild(funcname)) { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug( QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) ); +#endif + result = callable->getChild(funcname)->call(QString::null, new Api::List(argsList)); + } + else { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug( QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) ); +#endif + result = object->call(funcname, new Api::List(argsList)); + } + } catch(Kross::Api::Exception::Ptr exception) + { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("c++ exception catched, raise a ruby error"); +#endif + throw convertFromException(exception); + } catch(...) + { + throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n + } + } catch(VALUE v) { + rb_exc_raise(v ); + } + return toVALUE(result); +} + +void RubyExtension::delete_object(void* object) +{ + krossdebug("delete_object"); + RubyExtension* obj = static_cast<RubyExtension*>(object); + if(obj) + delete obj; +} + +void RubyExtension::delete_exception(void* object) +{ + Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object); + exc->_KShared_unref(); +} + + +RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate()) +{ + d->m_object = object; +} + + +RubyExtension::~RubyExtension() +{ + krossdebug("Delete RubyExtension"); + delete d; +} + +typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj; + +int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap) +{ + QMap<QString, Kross::Api::Object::Ptr>* map; + Data_Get_Struct(vmap, mStrObj, map); + if (key != Qundef) + { + Kross::Api::Object::Ptr o = RubyExtension::toObject( value ); + if(o) map->replace(STR2CSTR(key), o); + } + return ST_CONTINUE; +} + +bool RubyExtension::isOfExceptionType(VALUE value) +{ + VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException ); + return (TYPE(result) == T_TRUE); +} + +bool RubyExtension::isOfObjectType(VALUE value) +{ + VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject ); + return (TYPE(result) == T_TRUE); +} + + +Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value) +{ + if( isOfExceptionType(value) ) + { + Kross::Api::Exception* exception; + Data_Get_Struct(value, Kross::Api::Exception, exception); + return exception; + } + return 0; +} + +VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc) +{ + if(RubyExtensionPrivate::s_krossException == 0) + { + RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError); + } + exc->_KShared_ref(); + return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() ); +} + + +Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value) +{ +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug(QString("RubyExtension::toObject of type %1").arg(TYPE(value))); +#endif + switch( TYPE( value ) ) + { + case T_DATA: + { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("Object is a Kross Object"); +#endif + if( isOfObjectType(value) ) + { + RubyExtension* objectExtension; + Data_Get_Struct(value, RubyExtension, objectExtension); + Kross::Api::Object::Ptr object = objectExtension->d->m_object; + return object; + } else { + krosswarning("Cannot yet convert standard ruby type to kross object"); + return 0; + } + } + case T_FLOAT: + return new Kross::Api::Variant(NUM2DBL(value)); + case T_STRING: + return new Kross::Api::Variant(QString(STR2CSTR(value))); + case T_ARRAY: + { + QValueList<Kross::Api::Object::Ptr> l; + for(int i = 0; i < RARRAY(value)->len; i++) + { + Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) ); + if(o) l.append(o); + } + return new Kross::Api::List(l); + } + case T_FIXNUM: + return new Kross::Api::Variant((Q_LLONG)FIX2INT(value)); + case T_HASH: + { + QMap<QString, Kross::Api::Object::Ptr> map; + VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map); + rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap); + return new Kross::Api::Dict(map); + } + case T_BIGNUM: + { + return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value)); + } + case T_TRUE: + { + return new Kross::Api::Variant(true); + } + case T_FALSE: + { + return new Kross::Api::Variant(false); + } + case T_SYMBOL: + { + return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value)))); + } + case T_MATCH: + case T_OBJECT: + case T_FILE: + case T_STRUCT: + case T_REGEXP: + case T_MODULE: + case T_ICLASS: + case T_CLASS: + krosswarning(QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value))); + default: + case T_NIL: + return 0; + } +} + +VALUE RubyExtension::toVALUE(const QString& s) +{ + return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1()); +} + +VALUE RubyExtension::toVALUE(QStringList list) +{ + VALUE l = rb_ary_new(); + for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + rb_ary_push(l, toVALUE(*it)); + return l; +} + + +VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map) +{ + VALUE h = rb_hash_new(); + for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it) + rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) ); + return h; + +} + +VALUE RubyExtension::toVALUE(QValueList<QVariant> list) +{ + VALUE l = rb_ary_new(); + for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it) + rb_ary_push(l, toVALUE(*it)); + return l; +} + + +VALUE RubyExtension::toVALUE(const QVariant& variant) +{ + + switch(variant.type()) { + case QVariant::Invalid: + return Qnil; + case QVariant::Bool: + return (variant.toBool()) ? Qtrue : Qfalse; + case QVariant::Int: + return INT2FIX(variant.toInt()); + case QVariant::UInt: + return UINT2NUM(variant.toUInt()); + case QVariant::Double: + return rb_float_new(variant.toDouble()); + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + case QVariant::ByteArray: + case QVariant::BitArray: + case QVariant::CString: + case QVariant::String: + return toVALUE(variant.toString()); + case QVariant::StringList: + return toVALUE(variant.toStringList()); + case QVariant::Map: + return toVALUE(variant.toMap()); + case QVariant::List: + return toVALUE(variant.toList()); + + // To handle following both cases is a bit difficult + // cause Python doesn't spend an easy possibility + // for such large numbers (TODO maybe BigInt?). So, + // we risk overflows here, but well... + case QVariant::LongLong: { + return INT2NUM((long)variant.toLongLong()); + } + case QVariant::ULongLong: + return UINT2NUM((unsigned long)variant.toULongLong()); + default: { + krosswarning( QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) ); + return Qundef; + } + } +} + +VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object) +{ + if(! object.data()) { + return 0; + } + if(object->getClassName() == "Kross::Api::Variant") { + QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue(); + return toVALUE(v); + } + + if(object->getClassName() == "Kross::Api::List") { + Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() ); + return toVALUE((Kross::Api::List::Ptr)list); + } + + if(object->getClassName() == "Kross::Api::Dict") { + Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() ); + return toVALUE((Kross::Api::Dict::Ptr)dict); + } + + if(RubyExtensionPrivate::s_krossObject == 0) + { + RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject); + rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing", (VALUE (*)(...))RubyExtension::method_missing, -1); + } + return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) ); +} + +VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list) +{ + VALUE l = rb_ary_new(); + uint count = list ? list->count() : 0; + for(uint i = 0; i < count; i++) + rb_ary_push(l, toVALUE(list->item(i))); + return l; + +} + +} + +} |