~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to lib/kross/ruby/rubyextension.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2006-04-20 21:38:53 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060420213853-j5lxluqvymxt2zny
Tags: 1:1.5.0-0ubuntu2
UbuntuĀ uploadĀ 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 * rubyinterpreter.cpp
 
3
 * This file is part of the KDE project
 
4
 * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
 
5
 *
 
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"
 
20
 
 
21
#include <st.h>
 
22
 
 
23
#include <qmap.h>
 
24
#include <qstring.h>
 
25
 
 
26
#include "api/list.h"
 
27
 
 
28
#include "rubyconfig.h"
 
29
 
 
30
namespace Kross {
 
31
 
 
32
namespace Ruby {
 
33
 
 
34
    
 
35
class RubyExtensionPrivate {
 
36
    friend class RubyExtension;
 
37
    /// The \a Kross::Api::Object this RubyExtension wraps.
 
38
    Kross::Api::Object::Ptr m_object;
 
39
    /// 
 
40
    static VALUE s_krossObject;
 
41
    static VALUE s_krossException;
 
42
};
 
43
 
 
44
VALUE RubyExtensionPrivate::s_krossObject = 0;
 
45
VALUE RubyExtensionPrivate::s_krossException = 0;
 
46
    
 
47
VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
 
48
{
 
49
#ifdef KROSS_RUBY_EXTENSION_DEBUG
 
50
    kdDebug() << "method_missing(argc, argv, self)" << endl;
 
51
#endif
 
52
    if(argc < 1)
 
53
    {
 
54
        return 0;
 
55
    }
 
56
#ifdef KROSS_RUBY_EXTENSION_DEBUG
 
57
    kdDebug() << "Converting self to Kross::Api::Object" << endl;
 
58
#endif
 
59
    
 
60
    Kross::Api::Object::Ptr object = toObject( self );
 
61
    return RubyExtension::call_method(object, argc, argv);
 
62
}
 
63
 
 
64
VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
 
65
{
 
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;
 
70
#endif
 
71
    for(int i = 1; i < argc; i++)
 
72
    {
 
73
        Kross::Api::Object::Ptr obj = toObject(argv[i]);
 
74
        if(obj) argsList.append(obj);
 
75
    }
 
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;
 
82
#endif
 
83
                result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
 
84
            }
 
85
            else {
 
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;
 
88
#endif
 
89
                result = object->call(funcname, new Api::List(argsList));
 
90
            }
 
91
        } catch(Kross::Api::Exception::Ptr exception)
 
92
        {
 
93
#ifdef KROSS_RUBY_EXTENSION_DEBUG
 
94
            kdDebug() << "c++ exception catched, raise a ruby error" << endl;
 
95
#endif
 
96
            throw convertFromException(exception);
 
97
        }  catch(...)
 
98
        {
 
99
            throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n
 
100
        }
 
101
    } catch(VALUE v) {
 
102
         rb_exc_raise(v );
 
103
    }
 
104
    return toVALUE(result);
 
105
}
 
106
 
 
107
void RubyExtension::delete_object(void* object)
 
108
{
 
109
    kdDebug() << "delete_object" << endl;
 
110
    RubyExtension* obj = static_cast<RubyExtension*>(object);
 
111
    if(obj)
 
112
        delete obj;
 
113
}
 
114
 
 
115
void RubyExtension::delete_exception(void* object)
 
116
{
 
117
    Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object);
 
118
    exc->_KShared_unref();
 
119
}
 
120
 
 
121
    
 
122
RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
 
123
{
 
124
    d->m_object = object;
 
125
}
 
126
 
 
127
 
 
128
RubyExtension::~RubyExtension()
 
129
{
 
130
    kdDebug() << "Delete RubyExtension" << endl;
 
131
    delete d;
 
132
}
 
133
 
 
134
typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
 
135
 
 
136
int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE  vmap)
 
137
{
 
138
    QMap<QString, Kross::Api::Object::Ptr>* map; 
 
139
    Data_Get_Struct(vmap, mStrObj, map);
 
140
    if (key != Qundef)
 
141
    {
 
142
        Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
 
143
        if(o) map->replace(STR2CSTR(key), o);
 
144
    }
 
145
    return ST_CONTINUE;
 
146
}
 
147
 
 
148
bool RubyExtension::isOfExceptionType(VALUE value)
 
149
{
 
150
    VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException );
 
151
    return (TYPE(result) == T_TRUE);
 
152
}
 
153
 
 
154
bool RubyExtension::isOfObjectType(VALUE value)
 
155
{
 
156
    VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
 
157
    return (TYPE(result) == T_TRUE);
 
158
}
 
159
 
 
160
 
 
161
Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value)
 
162
{
 
163
    if( isOfExceptionType(value) )
 
164
    {
 
165
        Kross::Api::Exception* exception;
 
166
        Data_Get_Struct(value, Kross::Api::Exception, exception);
 
167
        return exception;
 
168
    }
 
169
    return 0;
 
170
}
 
171
 
 
172
VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc)
 
173
{
 
174
    if(RubyExtensionPrivate::s_krossException == 0)
 
175
    {
 
176
        RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError);
 
177
    }
 
178
    exc->_KShared_ref();
 
179
    return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() );
 
180
}
 
181
 
 
182
 
 
183
Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
 
184
{
 
185
#ifdef KROSS_RUBY_EXTENSION_DEBUG
 
186
    kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
 
187
#endif
 
188
    switch( TYPE( value ) )
 
189
    {
 
190
        case T_DATA:
 
191
        {
 
192
#ifdef KROSS_RUBY_EXTENSION_DEBUG
 
193
            kdDebug() << "Object is a Kross Object" << endl;
 
194
#endif
 
195
            if( isOfObjectType(value) )
 
196
            {
 
197
                RubyExtension* objectExtension;
 
198
                Data_Get_Struct(value, RubyExtension, objectExtension);
 
199
                Kross::Api::Object::Ptr object = objectExtension->d->m_object;
 
200
                return object;
 
201
            } else {
 
202
                kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
 
203
                return 0;
 
204
            }
 
205
        }
 
206
        case T_FLOAT:
 
207
            return new Kross::Api::Variant(NUM2DBL(value));
 
208
        case T_STRING:
 
209
            return new Kross::Api::Variant(QString(STR2CSTR(value)));
 
210
        case T_ARRAY:
 
211
        {
 
212
            QValueList<Kross::Api::Object::Ptr> l;
 
213
            for(int i = 0; i < RARRAY(value)->len; i++)
 
214
            {
 
215
                Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
 
216
                if(o) l.append(o);
 
217
            }
 
218
            return new Kross::Api::List(l);
 
219
        }
 
220
        case T_FIXNUM:
 
221
            return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
 
222
        case T_HASH:
 
223
        {
 
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);
 
228
        }
 
229
        case T_BIGNUM:
 
230
        {
 
231
            return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
 
232
        }
 
233
        case T_TRUE:
 
234
        {
 
235
            return new Kross::Api::Variant(true);
 
236
        }
 
237
        case T_FALSE:
 
238
        {
 
239
            return new Kross::Api::Variant(false);
 
240
        }
 
241
        case T_SYMBOL:
 
242
        {
 
243
            return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
 
244
        }
 
245
        case T_MATCH:
 
246
        case T_OBJECT:
 
247
        case T_FILE:
 
248
        case T_STRUCT:
 
249
        case T_REGEXP:
 
250
        case T_MODULE:
 
251
        case T_ICLASS:
 
252
        case T_CLASS:
 
253
            kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
 
254
        default:
 
255
        case T_NIL:
 
256
            return 0;
 
257
    }
 
258
}
 
259
 
 
260
VALUE RubyExtension::toVALUE(const QString& s)
 
261
{
 
262
    return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
 
263
}
 
264
 
 
265
VALUE RubyExtension::toVALUE(QStringList list)
 
266
{
 
267
    VALUE l = rb_ary_new();
 
268
    for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
 
269
        rb_ary_push(l, toVALUE(*it));
 
270
    return l;
 
271
}
 
272
 
 
273
 
 
274
VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
 
275
{
 
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()) );
 
279
    return h;
 
280
 
 
281
}
 
282
 
 
283
VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
 
284
{
 
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));
 
288
    return l;
 
289
}
 
290
 
 
291
 
 
292
VALUE RubyExtension::toVALUE(const QVariant& variant)
 
293
{
 
294
    
 
295
    switch(variant.type()) {
 
296
        case QVariant::Invalid:
 
297
            return Qnil;
 
298
        case QVariant::Bool:
 
299
            return (variant.toBool()) ? Qtrue : Qfalse;
 
300
        case QVariant::Int:
 
301
            return INT2FIX(variant.toInt());
 
302
        case QVariant::UInt:
 
303
            return UINT2NUM(variant.toUInt());
 
304
        case QVariant::Double:
 
305
            return rb_float_new(variant.toDouble());
 
306
        case QVariant::Date:
 
307
        case QVariant::Time:
 
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());
 
316
        case QVariant::Map:
 
317
            return toVALUE(variant.toMap());
 
318
        case QVariant::List:
 
319
            return toVALUE(variant.toList());
 
320
 
 
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());
 
327
        }
 
328
        case QVariant::ULongLong:
 
329
            return UINT2NUM((unsigned long)variant.toULongLong());
 
330
        default: {
 
331
            kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
 
332
            return Qundef;
 
333
        }
 
334
    }
 
335
}
 
336
 
 
337
VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
 
338
{
 
339
    if(! object) {
 
340
        return 0;
 
341
    }
 
342
 
 
343
    if(object->getClassName() == "Kross::Api::Variant") {
 
344
        QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
 
345
        return toVALUE(v);
 
346
    }
 
347
 
 
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);
 
351
    }
 
352
 
 
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);
 
356
    }
 
357
 
 
358
    if(RubyExtensionPrivate::s_krossObject == 0)
 
359
    {
 
360
        RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
 
361
        rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing",  (VALUE (*)(...))RubyExtension::method_missing, -1);
 
362
    }
 
363
    return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) );
 
364
}
 
365
 
 
366
VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
 
367
{
 
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)));
 
372
    return l;
 
373
 
 
374
}
 
375
 
 
376
}
 
377
 
 
378
}