File indexing completed on 2024-06-30 05:51:31

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2011, 2012 Alex Richardson <alex.richardson@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "stringscriptclass.hpp"
0010 #include "../../datatypes/strings/stringdatainformation.hpp"
0011 #include "../../parsers/parserutils.hpp"
0012 #include <structureslogging.hpp>
0013 
0014 StringScriptClass::StringScriptClass(QScriptEngine* eng, ScriptHandlerInfo* handlerInfo)
0015     : DefaultScriptClass(eng, handlerInfo)
0016 {
0017     mIterableProperties.reserve(mIterableProperties.size() + 6);
0018     // read-only properties
0019     s_lengthInCodepoints = eng->toStringHandle(ParserStrings::PROPERTY_CHAR_COUNT());
0020     mIterableProperties.append(qMakePair(s_lengthInCodepoints, QScriptValue::ReadOnly | QScriptValue::Undeletable));
0021     s_lengthInBytes = eng->toStringHandle(ParserStrings::PROPERTY_BYTE_COUNT());
0022     mIterableProperties.append(qMakePair(s_lengthInBytes, QScriptValue::ReadOnly | QScriptValue::Undeletable));
0023 
0024     // read-write properties
0025     s_maxByteCount = eng->toStringHandle(ParserStrings::PROPERTY_MAX_BYTE_COUNT());
0026     mIterableProperties.append(qMakePair(s_maxByteCount, QScriptValue::PropertyFlags(QScriptValue::Undeletable)));
0027     s_maxCharCount = eng->toStringHandle(ParserStrings::PROPERTY_MAX_CHAR_COUNT());
0028     mIterableProperties.append(qMakePair(s_maxCharCount, QScriptValue::PropertyFlags(QScriptValue::Undeletable)));
0029     s_terminatedBy = eng->toStringHandle(ParserStrings::PROPERTY_TERMINATED_BY());
0030     mIterableProperties.append(qMakePair(s_terminatedBy, QScriptValue::PropertyFlags(QScriptValue::Undeletable)));
0031     s_encoding = eng->toStringHandle(ParserStrings::PROPERTY_ENCODING());
0032     mIterableProperties.append(qMakePair(s_encoding, QScriptValue::PropertyFlags(QScriptValue::Undeletable)));
0033 
0034     mStringPrototype = eng->newObject();
0035     mStringPrototype.setProperty(QStringLiteral("toString"), eng->newFunction(String_proto_toString));
0036 }
0037 
0038 StringScriptClass::~StringScriptClass() = default;
0039 
0040 bool StringScriptClass::queryAdditionalProperty(const DataInformation* data, const QScriptString& name, QScriptClass::QueryFlags* flags, uint* id)
0041 {
0042     Q_UNUSED(data)
0043     // no need to modify flags since both read and write are handled
0044     if (name == s_maxByteCount || name == s_maxCharCount || name == s_terminatedBy
0045         || name == s_encoding) {
0046         return true;
0047     }
0048     if (name == s_lengthInCodepoints || name == s_lengthInBytes) {
0049         *flags &= ~HandlesWriteAccess;
0050         return true;
0051     }
0052 
0053     bool isArrayIndex;
0054     quint32 pos = name.toArrayIndex(&isArrayIndex);
0055     if (isArrayIndex && pos <= uint(data->asString()->stringLength())) {
0056         *id = pos + 1; // add 1 to distinguish from the default value of 0
0057         *flags &= ~HandlesWriteAccess; // writing is not yet supported
0058         return true;
0059     }
0060     return false; // not found
0061 }
0062 
0063 bool StringScriptClass::additionalPropertyFlags(const DataInformation* data, const QScriptString& name, uint id, QScriptValue::PropertyFlags* flags)
0064 {
0065     Q_UNUSED(data)
0066     Q_UNUSED(name)
0067     if (id != 0) {
0068         *flags |= QScriptValue::ReadOnly;
0069         return true;
0070     }
0071     return false;
0072 }
0073 
0074 QScriptValue StringScriptClass::additionalProperty(const DataInformation* data, const QScriptString& name, uint id)
0075 {
0076     const StringDataInformation* sData = data->asString();
0077 
0078     if (id != 0) {
0079         quint32 pos = id - 1;
0080         if (pos >= uint(sData->stringLength())) {
0081             return engine()->currentContext()->throwError(QScriptContext::RangeError,
0082                                                           QStringLiteral("Attempting to access string index %1, but length is %2").arg(
0083                                                               QString::number(pos), QString::number(sData->stringLength())));
0084         }
0085         return sData->valueAt(pos);
0086     }
0087     if (name == s_lengthInCodepoints) {
0088         return sData->stringLength();
0089     }
0090     if (name == s_lengthInBytes) {
0091         return sData->stringByteLength();
0092     }
0093     if (name == s_encoding) {
0094         return sData->encodingName();
0095     }
0096     if (name == s_maxCharCount) {
0097         return sData->maxCharCount();
0098     }
0099     if (name == s_maxByteCount) {
0100         return sData->maxByteCount();
0101     }
0102     if (name == s_terminatedBy) {
0103         return sData->terminationCodePoint();
0104     }
0105     return {};
0106 }
0107 
0108 bool StringScriptClass::setAdditionalProperty(DataInformation* data, const QScriptString& name, uint, const QScriptValue& value)
0109 {
0110     StringDataInformation* sData = data->asString();
0111 
0112     if (name == s_maxCharCount) {
0113         if (value.isNull()) {
0114             sData->logInfo() << "Unsetting max char count.";
0115             sData->unsetTerminationMode(StringData::CharCount);
0116         } else {
0117             ParsedNumber<uint> result = ParserUtils::uintFromScriptValue(value);
0118             if (result.isValid) {
0119                 sData->setMaxCharCount(result.value);
0120             } else {
0121                 sData->logError() << "Could not set maximum char count, invalid argument: " << value.toString();
0122             }
0123         }
0124         return true;
0125     }
0126     if (name == s_maxByteCount) {
0127         if (value.isNull()) {
0128             sData->logInfo() << "Unsetting max byte count.";
0129             sData->unsetTerminationMode(StringData::ByteCount);
0130         } else {
0131             ParsedNumber<uint> result = ParserUtils::uintFromScriptValue(value);
0132             if (result.isValid) {
0133                 sData->setMaxByteCount(result.value);
0134             } else {
0135                 sData->logError() << "Could not set maximum byte count, invalid argument: " << value.toString();
0136             }
0137         }
0138         return true;
0139     }
0140     if (name == s_terminatedBy) {
0141         if (value.isNull()) {
0142             sData->logInfo() << "Unsetting termination character.";
0143             sData->unsetTerminationMode(StringData::Sequence);
0144         } else {
0145             if (value.isString()) {
0146                 QString str = value.toString();
0147                 // we don't handle surrogate pairs, if you want to set that use a number instead.
0148                 if (str.length() != 1) {
0149                     sData->logError() << "Setting termination char: expected one char or a code point number"
0150                         ", got a string with length " << str.length();
0151                 } else {
0152                     sData->setTerminationCodePoint(str[0].unicode());
0153                 }
0154             } else {
0155                 ParsedNumber<uint> result = ParserUtils::uintFromScriptValue(value);
0156                 if (result.isValid) {
0157                     sData->setTerminationCodePoint(result.value);
0158                 } else {
0159                     sData->logError() << "Could not set maximum byte count, invalid argument: " << value.toString();
0160                 }
0161             }
0162         }
0163         return true;
0164     }
0165     if (name == s_encoding) {
0166         QString enc = value.toString();
0167         StringDataInformation::StringType encoding = ParserUtils::toStringEncoding(enc,
0168                                                                                    LoggerWithContext(sData->logger(), sData->fullObjectPath()));
0169         if (encoding == StringDataInformation::StringType::InvalidEncoding) {
0170             sData->logError() << "Attempting to set invalid encoding:" << enc;
0171         } else {
0172             sData->setEncoding(encoding);
0173         }
0174         return true;
0175     }
0176     return false;
0177 }
0178 
0179 QScriptValue StringScriptClass::prototype() const
0180 {
0181     return mStringPrototype;
0182 }
0183 
0184 QScriptValue StringScriptClass::String_proto_toString(QScriptContext* ctx, QScriptEngine* eng)
0185 {
0186     DataInformation* data = toDataInformation(ctx->thisObject().data());
0187     if (!data) {
0188         qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "could not cast data";
0189         return eng->undefinedValue();
0190     }
0191     return data->wasAbleToRead() ? data->valueString() : eng->undefinedValue();
0192 }