File indexing completed on 2024-07-14 14:35:16

0001 /* This file is part of the KDE libraries
0002     Copyright (C) 2005, 2006 Ian Reinhart Geiser <geiseri@kde.org>
0003     Copyright (C) 2005, 2006 Matt Broadstone <mbroadst@gmail.com>
0004     Copyright (C) 2005, 2006 Richard J. Moore <rich@kde.org>
0005     Copyright (C) 2005, 2006 Erik L. Bunce <kde@bunce.us>
0006     Copyright (C) 2007, 2008 Sebastian Sauer <mail@dipe.org>
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Library General Public
0010     License as published by the Free Software Foundation; either
0011     version 2 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Library General Public License for more details.
0017 
0018     You should have received a copy of the GNU Library General Public License
0019     along with this library; see the file COPYING.LIB.  If not, write to
0020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021     Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "variant_binding.h"
0025 
0026 #include <stdlib.h>
0027 
0028 #include <kjs/PropertyNameArray.h>
0029 #include <kjs/array_instance.h>
0030 
0031 #include <QBitRef>
0032 #include <QByteRef>
0033 #include <QDebug>
0034 #include <QObject>
0035 #include <QWidget>
0036 
0037 #include "kjseglobal.h"
0038 #include "qobject_binding.h"
0039 
0040 //#define KJSEMBED_VARIANT_DEBUG
0041 
0042 using namespace KJSEmbed;
0043 
0044 const KJS::ClassInfo VariantBinding::info = { "VariantBinding", nullptr, nullptr, nullptr };
0045 
0046 VariantBinding::VariantBinding(KJS::ExecState *exec, const QVariant &value)
0047     : ProxyBinding(exec), m_value(value)
0048 {
0049     StaticBinding::publish(exec, this, VariantFactory::methods());
0050 }
0051 
0052 void *VariantBinding::pointer()
0053 {
0054     return m_value.data();
0055 }
0056 
0057 KJS::UString VariantBinding::toString(KJS::ExecState *) const
0058 {
0059     return toUString(m_value.toString());
0060 }
0061 
0062 KJS::UString VariantBinding::className() const
0063 {
0064     return m_value.typeName();
0065 }
0066 
0067 QVariant VariantBinding::variant() const
0068 {
0069     return m_value;
0070 }
0071 
0072 void VariantBinding::setValue(const QVariant &val)
0073 {
0074     m_value = val;
0075 }
0076 
0077 QGenericArgument VariantBinding::arg(const char *type) const
0078 {
0079     const void *p = m_value.constData();
0080     //qDebug("Ptr %0x", p );
0081     //qDebug() << p;
0082     return QGenericArgument(type, p);
0083 }
0084 
0085 KJS::JSValue *callName(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args)
0086 {
0087     Q_UNUSED(args);
0088     KJSEmbed::VariantBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::VariantBinding>(exec,  self);
0089     return imp ? KJS::jsString(imp->variant().typeName()) : KJS::jsNull();
0090 }
0091 
0092 KJS::JSValue *callCast(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args)
0093 {
0094     KJSEmbed::VariantBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::VariantBinding>(exec,  self);
0095     if (imp) {
0096         QVariant val = imp->variant();
0097         QVariant::Type type = QVariant::nameToType(args[0]->toString(exec).ascii());
0098         KJS::JSValue *returnValue = KJS::jsBoolean(val.convert(type));
0099         imp->setValue(val);
0100         return returnValue;
0101     }
0102     return KJS::jsNull();
0103 }
0104 
0105 KJS::JSValue *callToString(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args)
0106 {
0107     Q_UNUSED(args);
0108     KJSEmbed::VariantBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::VariantBinding>(exec,  self);
0109     if (imp) {
0110         //qDebug("Call value to string");
0111         QVariant val = imp->variant();
0112         QString stringVal = val.toString();
0113         if (!stringVal.isEmpty()) {
0114             return KJS::jsString(val.toString());
0115         }
0116         return KJS::jsString(val.typeName());
0117     }
0118     return KJS::jsNull();
0119 }
0120 
0121 const Method VariantFactory::VariantMethods[] = {
0122     {"cast", 1, KJS::DontDelete | KJS::ReadOnly | KJS::DontEnum, &callCast },
0123     {"toString", 0, KJS::DontDelete | KJS::ReadOnly | KJS::DontEnum, &callToString },
0124     {nullptr, 0, 0, nullptr }
0125 };
0126 
0127 enum JavaScriptArrayType { None, List, Map };
0128 
0129 JavaScriptArrayType checkArray(KJS::ExecState *exec, KJS::JSValue *val)
0130 {
0131     KJS::JSObject *obj = val->toObject(exec);
0132     if (toQString(obj->className()) == "Array") {
0133         if (!obj->hasProperty(exec, KJS::Identifier("length"))) {
0134             return Map;
0135         }
0136         KJS::JSValue *jslen = obj->get(exec, KJS::Identifier("length"));
0137         const int len = jslen->toNumber(exec);
0138         if (len > 0) {
0139             QByteArray buff;
0140             buff.setNum(len - 1);
0141             if (!obj->hasProperty(exec, KJS::Identifier(buff.data()))) {
0142                 return Map;
0143             }
0144         }
0145         return List;
0146     } else {
0147         return None;
0148     }
0149 }
0150 
0151 QMap<QString, QVariant> KJSEmbed::convertArrayToMap(KJS::ExecState *exec, KJS::JSValue *value)
0152 {
0153     QMap<QString, QVariant> returnMap;
0154     KJS::JSObject *obj = value->toObject(exec);
0155     KJS::PropertyNameArray lst;
0156     obj->getPropertyNames(exec, lst);
0157     KJS::PropertyNameArrayIterator idx = lst.begin();
0158     for (; idx != lst.end(); idx++) {
0159         KJS::Identifier id = *idx;
0160         KJS::JSValue *val  = obj->get(exec, id);
0161         returnMap[toQString(id)] = convertToVariant(exec, val);
0162     }
0163     return returnMap;
0164 }
0165 
0166 QList<QVariant> KJSEmbed::convertArrayToList(KJS::ExecState *exec, KJS::JSValue *value)
0167 {
0168     QList<QVariant> returnList;
0169     /*
0170     KJS::JSObject *obj = value->toObject( exec );
0171     if ( toQString(obj->className() == "Array" )
0172     {
0173         int length = int(obj->get( exec, KJS::Identifier( "length" ) )->toInteger( exec ) );
0174         for ( int index = 0; index < length; ++index )
0175         {
0176             QByteArray buff;
0177             buff.setNum(index);
0178             KJS::JSValue *val = obj->get(exec, KJS::Identifier( buff.data() ) );
0179             if( val )
0180                 returnList += convertToVariant(exec, val );
0181             else
0182                 returnList += QVariant();
0183         }
0184     }
0185     */
0186     KJS::ArrayInstance *arrayImp = extractBindingImp<KJS::ArrayInstance>(exec, value);
0187     if (arrayImp) {
0188         const unsigned numItems = arrayImp->getLength();
0189         for (unsigned i = 0; i < numItems; ++i) {
0190             returnList.append(convertToVariant(exec, arrayImp->getItem(i)));
0191         }
0192     }
0193     return returnList;
0194 }
0195 
0196 QStringList KJSEmbed::convertArrayToStringList(KJS::ExecState *exec, KJS::JSValue *value)
0197 {
0198     QStringList returnList;
0199     /*
0200     KJS::JSObject *obj = value->toObject( exec );
0201     if ( toQString(obj->className()) == "Array" )
0202     {
0203         int length = int( obj->get( exec, KJS::Identifier( "length" ) )->toInteger( exec ) );
0204         for ( int index = 0; index < length; ++index )
0205         {
0206             QByteArray buff;
0207             buff.setNum(index);
0208             KJS::JSValue *val = obj->get(exec, KJS::Identifier( buff.data() ) );
0209             if( val )
0210                 returnList += convertToVariant(exec, val ).value<QString>();
0211             else
0212                 returnList += QString();
0213         }
0214     }
0215     */
0216     KJS::ArrayInstance *arrayImp = extractBindingImp<KJS::ArrayInstance>(exec, value);
0217     if (arrayImp) {
0218         const unsigned numItems = arrayImp->getLength();
0219         for (unsigned i = 0; i < numItems; ++i) {
0220             returnList.append(convertToVariant(exec, arrayImp->getItem(i)).toString());
0221         }
0222     }
0223     return returnList;
0224 }
0225 
0226 QDateTime convertDateToDateTime(KJS::ExecState *exec, KJS::JSValue *value)
0227 {
0228     KJS::List args;
0229     QDateTime returnDateTime;
0230     KJS::JSObject *obj = value->toObject(exec);
0231     if (toQString(obj->className()) == "Date") {
0232         int seconds = int(obj->get(exec, KJS::Identifier("getSeconds"))->toObject(exec)->call(exec, obj, args)->toInteger(exec));
0233         int minutes = int(obj->get(exec, KJS::Identifier("getMinutes"))->toObject(exec)->call(exec, obj, args)->toInteger(exec));
0234         int hours = int(obj->get(exec, KJS::Identifier("getHours"))->toObject(exec)->call(exec, obj, args)->toInteger(exec));
0235         int month = int(obj->get(exec, KJS::Identifier("getMonth"))->toObject(exec)->call(exec, obj, args)->toInteger(exec));
0236         int day = int(obj->get(exec, KJS::Identifier("getDate"))->toObject(exec)->call(exec, obj, args)->toInteger(exec));
0237         int year = int(obj->get(exec, KJS::Identifier("getFullYear"))->toObject(exec)->call(exec, obj, args)->toInteger(exec));
0238         returnDateTime.setDate(QDate(year, month + 1, day));
0239         returnDateTime.setTime(QTime(hours, minutes, seconds));
0240     } else {
0241         // Throw error
0242     }
0243 
0244     return returnDateTime;
0245 }
0246 
0247 QVariant KJSEmbed::convertToVariant(KJS::ExecState *exec, KJS::JSValue *value)
0248 {
0249 #ifdef KJSEMBED_VARIANT_DEBUG
0250     qDebug() << "KJSEmbed::convertToVariant";
0251 #endif
0252 
0253     QVariant returnValue;
0254     switch (value->type()) {
0255     case KJS::UndefinedType:
0256     case KJS::NullType:
0257         break;
0258     case KJS::StringType:
0259         returnValue = toQString(value->toString(exec));
0260         break;
0261     case KJS::NumberType:
0262         returnValue = value->toNumber(exec);
0263         break;
0264     case KJS::BooleanType:
0265         returnValue = value->toBoolean(exec);
0266         break;
0267     case KJS::ObjectType: {
0268         KJS::JSObject *obj = value->toObject(exec);
0269         //qDebug() << "Object type: " << toQString(obj->className());
0270         if (toQString(obj->className()) == "Array") {
0271             if (checkArray(exec, value) == List) {
0272                 returnValue = convertArrayToList(exec, value);
0273             } else {
0274                 returnValue = convertArrayToMap(exec, value);
0275             }
0276         } else if (toQString(obj->className()) == "Date") {
0277             returnValue = convertDateToDateTime(exec, value);
0278         } else {
0279             returnValue = extractVariant(exec, value);
0280         }
0281         //if( returnValue.isNull() ) returnValue = toQString(value->toString(exec));
0282     } break;
0283     default:
0284         returnValue = extractVariant(exec, value);
0285         //if( returnValue.isNull() ) returnValue = toQString(value->toString(exec));
0286         break;
0287     }
0288     return returnValue;
0289 }
0290 
0291 KJS::JSValue *KJSEmbed::convertToValue(KJS::ExecState *exec, const QVariant &value)
0292 {
0293 #ifdef KJSEMBED_VARIANT_DEBUG
0294     qDebug() << "KJSEmbed::convertToValue typeid=" << value.type() << "typename=" << value.typeName() << "toString=" << value.toString();
0295 #endif
0296 
0297     KJS::JSValue *returnValue;
0298     switch (value.type()) {
0299     case QVariant::Invalid:
0300         returnValue = KJS::jsNull();
0301         break;
0302     case QVariant::Int:
0303         returnValue = KJS::jsNumber(value.value<int>());
0304         break;
0305     case QVariant::UInt:
0306         returnValue = KJS::jsNumber(value.value<unsigned int>());
0307         break;
0308     case QVariant::LongLong:
0309         returnValue = KJS::jsNumber(value.value<qlonglong>());
0310         break;
0311     case QVariant::ULongLong:
0312         returnValue = KJS::jsNumber(value.value<qulonglong>());
0313         break;
0314     case QVariant::Double:
0315         returnValue = KJS::jsNumber(value.value<double>());
0316         break;
0317     case QVariant::Bool:
0318         returnValue = KJS::jsBoolean(value.value<bool>());
0319         break;
0320     case QVariant::ByteArray:
0321         returnValue = KJS::jsString(QString(value.value<QByteArray>()));
0322         break;
0323     case QVariant::String:
0324         returnValue = KJS::jsString(value.value<QString>());
0325         break;
0326     case QVariant::StringList: {
0327         KJS::List items;
0328         QStringList lst = value.value<QStringList>();
0329         QStringList::Iterator idx = lst.begin();
0330         for (; idx != lst.end(); ++idx) {
0331             items.append(KJS::jsString((*idx)));
0332         }
0333         returnValue = exec->lexicalInterpreter()->builtinArray()->construct(exec, items);
0334         break;
0335     }
0336     case QVariant::Date: // fall through
0337     case QVariant::DateTime: // fall through
0338     case QVariant::Time: {
0339         QDateTime dt = QDateTime::currentDateTime();
0340         if (value.type() == QVariant::Date) {
0341             dt.setDate(value.toDate());
0342         } else if (value.type() == QVariant::Time) {
0343             dt.setTime(value.toTime());
0344         } else {
0345             dt = value.toDateTime();
0346         }
0347 
0348         KJS::List items;
0349         items.append(KJS::jsNumber(dt.date().year()));
0350         items.append(KJS::jsNumber(dt.date().month() - 1));
0351         items.append(KJS::jsNumber(dt.date().day()));
0352         items.append(KJS::jsNumber(dt.time().hour()));
0353         items.append(KJS::jsNumber(dt.time().minute()));
0354         items.append(KJS::jsNumber(dt.time().second()));
0355         items.append(KJS::jsNumber(dt.time().msec()));
0356         returnValue = exec->lexicalInterpreter()->builtinDate()->construct(exec, items);
0357         break;
0358     }
0359     case QVariant::List: {
0360         KJS::List items;
0361         QList<QVariant> lst = value.toList();
0362         foreach (const QVariant &item, lst) {
0363             items.append(convertToValue(exec, item));
0364         }
0365         returnValue = exec->lexicalInterpreter()->builtinArray()->construct(exec, items);
0366         break;
0367     }
0368     case QVariant::Map: {
0369         QMap<QString, QVariant> map = value.toMap();
0370         QMap<QString, QVariant>::Iterator idx = map.begin();
0371         KJS::JSObject *obj = exec->lexicalInterpreter()->builtinObject()->construct(exec, KJS::List());
0372         for (; idx != map.end(); ++idx) {
0373             obj->put(exec, KJS::Identifier(toUString(idx.key())), convertToValue(exec,  idx.value()));
0374         }
0375         returnValue =  obj;
0376         break;
0377     }
0378     default: {
0379         if (value.canConvert< QWidget * >()) {
0380             QWidget *widget = qvariant_cast< QWidget * >(value);
0381             returnValue = widget ? createQObject(exec, widget, KJSEmbed::ObjectBinding::CPPOwned) : KJS::jsNull();
0382         } else if (value.canConvert< QObject * >()) {
0383             QObject *object = qvariant_cast< QObject * >(value);
0384             returnValue = object ? createQObject(exec, object, KJSEmbed::ObjectBinding::CPPOwned) : KJS::jsNull();
0385         } else {
0386             returnValue = createVariant(exec, value.typeName(), value);
0387             if (returnValue->isNull()) {
0388                 returnValue = KJS::jsString(value.value<QString>());
0389             }
0390         }
0391         break;
0392     }
0393     }
0394     return returnValue;
0395 }
0396 
0397 QVariant KJSEmbed::extractVariant(KJS::ExecState *exec, KJS::JSValue *value)
0398 {
0399 #ifdef KJSEMBED_VARIANT_DEBUG
0400     qDebug() << "KJSEmbed::extractVariant";
0401 #endif
0402 
0403     KJSEmbed::VariantBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::VariantBinding>(exec,  value);
0404     if (imp) {
0405         return imp->variant();
0406     }
0407     if (value->type() == KJS::StringType) {
0408         return QVariant(toQString(value->toString(exec)));
0409     }
0410     if (value->type() == KJS::NumberType) {
0411         return QVariant(value->toNumber(exec));
0412     }
0413     if (value->type() == KJS::BooleanType) {
0414         return QVariant(value->toBoolean(exec));
0415     }
0416 
0417     KJS::JSObject *obj = value->toObject(exec);
0418     if (obj) {
0419         if (QObjectBinding *objImp = KJSEmbed::extractBindingImp<QObjectBinding>(exec, value)) {
0420             QVariant v;
0421             if (QObject *qobj = objImp->qobject<QObject>()) {
0422                 v.setValue(qobj);
0423             }
0424             return v;
0425         }
0426         if (toQString(obj->className()) == "Array") {
0427             return convertArrayToList(exec, value);
0428         }
0429     }
0430     return QVariant();
0431 }
0432