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 #ifndef VARIANT_BINDING_H
0025 #define VARIANT_BINDING_H
0026 
0027 #include <QVariant>
0028 
0029 #include <kjs/object.h>
0030 #include <kjs/interpreter.h>
0031 
0032 #include "static_binding.h"
0033 
0034 /**
0035 * A simple variant style method.
0036 * This will extract the QVariant, cast it to the native type and place it in "value".
0037 * Any data that should be returned from this method should be placed into "result";
0038 *
0039 */
0040 #define START_VARIANT_METHOD( METHODNAME, TYPE) \
0041     KJS::JSValue *METHODNAME( KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args ) \
0042     { \
0043         Q_UNUSED(exec);\
0044         Q_UNUSED(self);\
0045         Q_UNUSED(args);\
0046         KJS::JSValue *result = KJS::jsNull(); \
0047         KJSEmbed::VariantBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::VariantBinding>(exec, self ); \
0048         if( imp ) \
0049         { \
0050             TYPE value = imp->value<TYPE>();
0051 /**
0052 * End a variant method started by START_VARIANT_METHOD
0053 */
0054 #define END_VARIANT_METHOD \
0055     imp->setValue(QVariant::fromValue(value)); \
0056     } \
0057     else \
0058     {\
0059         KJS::throwError(exec, KJS::GeneralError, "We have a problem baby");\
0060     }\
0061     return result; \
0062     }
0063 
0064 #define KJSO_VARIANT_SIMPLE_BINDING_CTOR( NAME, JSNAME, TYPE, BASENAME ) \
0065     NAME::NAME(KJS::ExecState *exec, const char* typeName ) \
0066         : BASENAME( exec, typeName )                    \
0067     { \
0068         StaticBinding::publish( exec, this, NAME::methods() ); \
0069     } \
0070     NAME::NAME(KJS::ExecState *exec, const TYPE & value) \
0071         : BASENAME( exec, QVariant::fromValue(value))                          \
0072     { \
0073         StaticBinding::publish( exec, this, NAME::methods() ); \
0074     }
0075 
0076 namespace KJSEmbed
0077 {
0078 /**
0079 * QVariant based binding.  This class wraps the implementation.
0080 * You should never have to use this class directly instead use KJSEmbed::createVariant.
0081 */
0082 
0083 /**
0084 * QVariant bindinging implementation.
0085 */
0086 class KJSEMBED_EXPORT VariantBinding : public ProxyBinding
0087 {
0088 public:
0089     /**
0090     * Create a new binding implementation with a QVariant to wrap
0091     */
0092     VariantBinding(KJS::ExecState *exec, const QVariant &value);
0093     ~VariantBinding() override {}
0094 
0095     void *pointer();
0096 
0097     KJS::UString toString(KJS::ExecState *) const override;
0098     KJS::UString className() const override;
0099 
0100     /**
0101     * Return the wrapped QVariant
0102     */
0103     QVariant variant() const;
0104 
0105     /**
0106     * Extract the actual value from the wrapper.  This method wraps QVariant::value, so it will support
0107     * some aspects of casting.  If the cast fails a default value of T will be returned.
0108     */
0109     template<typename T>
0110     T value() const
0111     {
0112         return m_value.value<T>();
0113     }
0114     /**
0115     * Set the internal value of the QVariant.
0116     */
0117     void setValue(const QVariant &val);
0118 
0119     /**
0120     * Constructs a QGenericArgument that is used with QMetaObject::invokeMember
0121     */
0122     QGenericArgument arg(const char *type) const;
0123 
0124     static const KJS::ClassInfo info;
0125 
0126 private:
0127     const KJS::ClassInfo *classInfo() const override
0128     {
0129         return &info;
0130     }
0131     QVariant m_value;
0132 
0133 };
0134 
0135 /**
0136 * Extracts a QVariant from a KJS::JSValue if the conversion fails a QVariant::Null is returned.
0137 */
0138 QVariant KJSEMBED_EXPORT extractVariant(KJS::ExecState *exec, KJS::JSValue *value);
0139 
0140 /**
0141 * Extracts a value based type from a VariantBinding object.  This method calls @ref extractVariant so if
0142 * the conversions fails then the default value will be returned.  Care should be taken that this method
0143 * is not used with KJSEmbed::ObjectBinding objects because the cast will fail.
0144 */
0145 template< typename T>
0146 T extractVariant(KJS::ExecState *exec, KJS::JSValue *arg, const T &defaultValue)
0147 {
0148     if (!arg) {
0149         return defaultValue;
0150     } else {
0151         QVariant variant = extractVariant(exec, arg);
0152         if (!variant.isNull()) {
0153             if (variant.canConvert<T>()) {
0154                 return variant.value<T>();
0155             } else {
0156                 throwError(exec, KJS::TypeError, "Cast failed");
0157                 return defaultValue;
0158             }
0159         } else {
0160             return defaultValue;
0161         }
0162     }
0163 }
0164 
0165 /**
0166 * Extracts a value from a KJS::List of KJS::JSValues.  If the argument is out of range the default value
0167 * is returned.
0168 */
0169 template< typename T>
0170 T extractVariant(KJS::ExecState *exec, const KJS::List &args, int idx, const T &defaultValue = T())
0171 {
0172     if (args.size() >= idx) {
0173         return extractVariant<T>(exec, args[idx], defaultValue);
0174     } else {
0175         return defaultValue;
0176     }
0177 }
0178 
0179 /**
0180 * Can create any known KJSEmbed::VariantBinding object and set the value.
0181 * On failure a KJS::jsNull will be returned and the exception set. Only values
0182 * that are supported by QVariant will work.
0183 */
0184 template< typename T>
0185 KJS::JSValue *createVariant(KJS::ExecState *exec, const KJS::UString &className, const T &value)
0186 {
0187     KJS::JSObject *parent;
0188     parent = exec->dynamicInterpreter()->globalObject();
0189     KJS::JSObject *returnValue = StaticConstructor::construct(exec, parent, className);
0190     if (returnValue) {
0191         // If it is a value type setValue
0192         KJSEmbed::VariantBinding *imp = extractBindingImp<KJSEmbed::VariantBinding>(exec, returnValue);
0193         if (imp) {
0194             imp->setValue(QVariant::fromValue(value));
0195         } else {
0196             throwError(exec, KJS::TypeError, toUString(QString("Created failed to cast to %1 failed").arg(toQString(className))));
0197             return KJS::jsNull();
0198         }
0199     } else {
0200         throwError(exec, KJS::TypeError, toUString(QString("Could not construct a %1").arg(toQString(className))));
0201         return KJS::jsNull();
0202     }
0203     return returnValue;
0204 }
0205 
0206 /**
0207 * Convert a KJS::JSValue that contains an associative array into a QMap.  If you call
0208 * this on a normal Javascript object you will get each property as the key and its
0209 * data as the value.  A normal array will give you a QMap with each key being the
0210 * index.
0211 */
0212 QMap<QString, QVariant> KJSEMBED_EXPORT convertArrayToMap(KJS::ExecState *exec, KJS::JSValue *value);
0213 
0214 /**
0215 * Convert a KJS::JSValue into a QList.  If the list contains only strings, or objects that can be
0216 * converted to strings you can call to @ref convertArrayToStringList.  If you call this on an
0217 * associative array or a javascript object the list will return empty.  Unless there happens to be
0218 * a property with a numeric index present, then all bets are off.
0219 */
0220 QList<QVariant> KJSEMBED_EXPORT convertArrayToList(KJS::ExecState *exec, KJS::JSValue *value);
0221 
0222 /**
0223 * Convert a KJS::JSValue into a QStringList. @see convertArrayToList.
0224 */
0225 QStringList KJSEMBED_EXPORT convertArrayToStringList(KJS::ExecState *exec, KJS::JSValue *value);
0226 
0227 /**
0228 * Convert a KJS::JSValue into a QVariant object
0229 */
0230 QVariant KJSEMBED_EXPORT convertToVariant(KJS::ExecState *exec, KJS::JSValue *value);
0231 
0232 /**
0233 * Convert a QVariant to a KJS::JSValue.  If the type is a known type the bindings will be added.
0234 * If the type is supported by QVariant, but is not supported by KJSEmbed then it will just be wrapped.
0235 * This wrapped value can be used just like normal value bindings, save for the lack of methods available to the object.
0236 */
0237 KJSEMBED_EXPORT KJS::JSValue *convertToValue(KJS::ExecState *exec, const QVariant &value);
0238 
0239 /**
0240 * The Bindings for the KJSEmbed::VariantBinding
0241 */
0242 struct Method;
0243 class KJSEMBED_EXPORT VariantFactory
0244 {
0245 public:
0246     static const Method VariantMethods[];
0247     static const Method *methods()
0248     {
0249         return VariantMethods;
0250     }
0251 };
0252 
0253 }
0254 #endif
0255