diff --git a/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.cpp b/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.cpp index 541a9c9a..22b41c1d 100644 --- a/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.cpp +++ b/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.cpp @@ -1,329 +1,341 @@ /* * This file is part of the Okteta Kasten Framework, made within the KDE community. * * Copyright 2011, 2012 Alex Richardson * Copyright 2016 Aaron Bishop * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "stringdatainformation.hpp" #include "../dummydatainformation.hpp" #include "../topleveldatainformation.hpp" #include "utf32stringdata.hpp" #include "utf16stringdata.hpp" #include "utf8stringdata.hpp" #include "asciistringdata.hpp" #include "ebcdicstringdata.hpp" #include "latin1stringdata.hpp" #include "../../script/classes/stringscriptclass.hpp" #include "../../script/scripthandlerinfo.hpp" #include "../../script/scriptlogger.hpp" #include #include #include #include +const QString StringDataInformation::encodingNames[static_cast(StringDataInformation::StringType::EBCDIC)+2] = { + QStringLiteral(""), // -1 + QStringLiteral("ascii"), + QStringLiteral("latin1"), + QStringLiteral("utf-8"), + QStringLiteral("utf-16le"), + QStringLiteral("utf-16-be"), + QStringLiteral("utf32-le"), + QStringLiteral("utf32-be"), + QStringLiteral("ebcdic"), +}; + StringDataInformation::StringDataInformation(const QString& name, StringType encoding, DataInformationBase* parent) : DataInformationWithDummyChildren(name, parent) , mDummy(new DummyDataInformation(this)) , mData(nullptr) { setEncoding(encoding); // sets mData } StringDataInformation::StringDataInformation(const StringDataInformation& d) : DataInformationWithDummyChildren(d) , mDummy(new DummyDataInformation(this)) { setEncoding(d.mEncoding); // sets mData mData->copyTerminationFrom(d.mData.data()); } StringDataInformation::~StringDataInformation() = default; DataInformation* StringDataInformation::childAt(unsigned int index) const { Q_ASSERT(index < childCount()); mDummy->setDummyIndex(index); return mDummy.data(); } bool StringDataInformation::setData(const QVariant&, Okteta::AbstractByteArrayModel*, Okteta::Address, BitCount64, quint8) { Q_ASSERT_X(false, "StringDataInformation::setData()", "this should never be called!"); return false; } bool StringDataInformation::setChildData(uint row, const QVariant& value, Okteta::AbstractByteArrayModel* out, Okteta::Address address, BitCount64 bitsRemaining, quint8 bitOffset) { Q_UNUSED(row) Q_UNUSED(value) Q_UNUSED(out) Q_UNUSED(address) Q_UNUSED(bitsRemaining) Q_UNUSED(bitOffset) logWarn() << "setChildData not implemented yet!"; return false; } qint64 StringDataInformation::readData(Okteta::AbstractByteArrayModel* input, Okteta::Address address, BitCount64 bitsRemaining, quint8* bitOffset) { Q_ASSERT(mHasBeenUpdated); // update must have been called prior to reading if (*bitOffset != 0) { logWarn() << "while reading string bit offset was: " << *bitOffset << ", adding padding and continuing at next byte (address=" << address << ")"; bitsRemaining -= 8 - *bitOffset; *bitOffset = 0; address += 1; Q_ASSERT((bitsRemaining % 8) == 0); // must be mod 8 } qint64 ret = mData->read(input, address, bitsRemaining); mWasAbleToRead = ret >= 0; return ret; } BitCount32 StringDataInformation::size() const { return mData->size(); } void StringDataInformation::setWidgetData(QWidget*) const { // TODO } QVariant StringDataInformation::dataFromWidget(const QWidget*) const { // TODO Q_ASSERT(false); return {}; } QWidget* StringDataInformation::createEditWidget(QWidget*) const { // TODO Q_ASSERT(false); return nullptr; } QString StringDataInformation::typeNameImpl() const { return mData->typeName(); } unsigned int StringDataInformation::childCount() const { return mData->count(); } Qt::ItemFlags StringDataInformation::flags(int column, bool fileLoaded) const { return DataInformation::flags(column, fileLoaded); } QVariant StringDataInformation::childData(int row, int column, int role) const { Q_ASSERT(row >= 0 && (unsigned)row < childCount()); Q_ASSERT(column < COLUMN_COUNT); if (role == Qt::DisplayRole) { if (column == ColumnName) { // TODO termination char return QString(QLatin1Char('[') + QString::number(row) + QLatin1Char(']')); } if (column == ColumnType) { return mData->charType(); } if (column == ColumnValue) { return mData->stringValue(row); } } // TODO mark eof reached, don't add extra item. i.e. add icon or colour return {}; } QString StringDataInformation::valueStringImpl() const { Q_ASSERT(mWasAbleToRead); return mData->completeString(); } Qt::ItemFlags StringDataInformation::childFlags(int row, int column, bool fileLoaded) const { Q_UNUSED(fileLoaded); Q_UNUSED(row); Q_UNUSED(column); return Qt::ItemIsSelectable | Qt::ItemIsEnabled; // not editable atm // TODO make editable } void StringDataInformation::setEncoding(StringDataInformation::StringType encoding) { if (mData && mEncoding == encoding) { return; } if (mData && ((mEncoding == StringType::UTF16_LE && encoding == StringType::UTF16_BE) || (mEncoding == StringType::UTF16_BE && encoding == StringType::UTF16_LE))) { // only set endianess, since is already utf 16 mData->setLittleEndian(encoding == StringType::UTF16_LE); } else if (mData && ((mEncoding == StringType::UTF32_LE && encoding == StringType::UTF32_BE) || (mEncoding == StringType::UTF32_BE && encoding == StringType::UTF32_LE))) { // only set endianess, since is already utf 32 mData->setLittleEndian(encoding == StringType::UTF32_LE); } else { StringData* data = nullptr; switch (encoding) { case StringType::ASCII: data = new AsciiStringData(this); break; case StringType::Latin1: data = new Latin1StringData(this); break; case StringType::UTF8: data = new Utf8StringData(this); break; case StringType::UTF16_LE: data = new Utf16StringData(this); data->setLittleEndian(true); break; case StringType::UTF16_BE: data = new Utf16StringData(this); data->setLittleEndian(false); break; case StringType::UTF32_LE: data = new Utf32StringData(this); data->setLittleEndian(true); break; case StringType::UTF32_BE: data = new Utf32StringData(this); data->setLittleEndian(false); break; case StringType::EBCDIC: data = new EbcdicStringData(this); break; default: data = new AsciiStringData(this); // TODO add the other classes break; } if (mData) { data->copyTerminationFrom(mData.data()); } mData.reset(data); } mEncoding = encoding; } // TODO implement string editing BitCount32 StringDataInformation::childSize(uint index) const { return mData->sizeAt(index); } QString StringDataInformation::childTypeName(uint index) const { Q_UNUSED(index) return {}; // XXX should there be something here? } void StringDataInformation::setChildWidgetData(uint index, QWidget* w) const { Q_ASSERT(false); Q_ASSERT(index < mData->count()); Q_UNUSED(index) Q_UNUSED(w) } QVariant StringDataInformation::dataFromChildWidget(uint index, const QWidget* w) const { Q_ASSERT(index < mData->count()); Q_ASSERT(false); Q_UNUSED(w) Q_UNUSED(index) return {}; } QWidget* StringDataInformation::createChildEditWidget(uint index, QWidget* parent) const { Q_ASSERT(false); Q_UNUSED(parent) Q_UNUSED(index) return nullptr; } QScriptValue StringDataInformation::childToScriptValue(uint index, QScriptEngine*, ScriptHandlerInfo*) const { // just return as a string return mData->stringValue(index); } BitCount64 StringDataInformation::childPosition(const DataInformation* child, Okteta::Address start) const { Q_UNUSED(start) Q_ASSERT(child->isDummy()); Q_ASSERT(child->parent() == this); Q_ASSERT(child == mDummy.data()); Q_UNUSED(child) uint index = mDummy->dummyIndex(); Q_ASSERT(index < mData->count()); BitCount32 offs = 0; for (uint i = 0; i < index; ++i) { offs += mData->sizeAt(i); } return offs; } QVariant StringDataInformation::data(int column, int role) const { if (mData->wasEof()) { if (role == Qt::BackgroundRole) { return QBrush(Qt::yellow); } if (role == Qt::ToolTipRole) { return i18n("End of file reached prematurely"); } } return DataInformation::data(column, role); } bool StringDataInformation::isString() const { return true; } void StringDataInformation::unsetTerminationMode(StringData::TerminationMode mode) { // clear the mode and set to null terminated of none is left mData->setTerminationMode(StringData::TerminationMode(mData->terminationMode() & ~mode)); if (mData->terminationMode() == StringData::None) { mData->setTerminationCodePoint(0); } } QScriptClass* StringDataInformation::scriptClass(ScriptHandlerInfo* handlerInfo) const { return handlerInfo->mStringClass.data(); } bool StringDataInformation::canHaveChildren() const { return true; } diff --git a/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.hpp b/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.hpp index 29055b84..d2a6c12b 100644 --- a/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.hpp +++ b/kasten/controllers/view/structures/datatypes/strings/stringdatainformation.hpp @@ -1,173 +1,174 @@ /* * This file is part of the Okteta Kasten Framework, made within the KDE community. * * Copyright 2011 Alex Richardson * Copyright 2016 Aaron Bishop * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KASTEN_STRINGDATAINFORMATION_HPP #define KASTEN_STRINGDATAINFORMATION_HPP #include "../datainformation.hpp" #include "../dummydatainformation.hpp" #include "stringdata.hpp" class DummyDataInformation; -// TODO QStringLiteral -const QLatin1String stringEncodings[] = { - QLatin1String("ascii"), QLatin1String("latin1"), QLatin1String("utf-8"), QLatin1String("utf-16le"), - QLatin1String("utf-16-be"), QLatin1String("utf32-le"), QLatin1String("utf32-be"), QLatin1String("ebcdic") -}; - class StringDataInformation : public DataInformationWithDummyChildren { DATAINFORMATION_CLONE_DECL(StringDataInformation, DataInformationWithDummyChildren); public: enum class StringType { InvalidEncoding = -1, ASCII = 0, Latin1, UTF8, UTF16_LE, UTF16_BE, UTF32_LE, UTF32_BE, EBCDIC }; StringDataInformation(const QString& name, StringType encoding, DataInformationBase* parent = nullptr); ~StringDataInformation() override; bool canHaveChildren() const override; DataInformation* childAt(unsigned int) const override; bool setData(const QVariant& value, Okteta::AbstractByteArrayModel* input, Okteta::Address address, BitCount64 bitsRemaining, quint8 bitOffset) override; bool setChildData(uint row, const QVariant& value, Okteta::AbstractByteArrayModel* out, Okteta::Address address, BitCount64 bitsRemaining, quint8 bitOffset) override; qint64 readData(Okteta::AbstractByteArrayModel* input, Okteta::Address address, BitCount64 bitsRemaining, quint8* bitOffset) override; BitCount32 size() const override; void setWidgetData(QWidget* w) const override; QVariant dataFromWidget(const QWidget* w) const override; QWidget* createEditWidget(QWidget* parent) const override; unsigned int childCount() const override; Qt::ItemFlags flags(int column, bool fileLoaded = true) const override; bool isString() const override; QVariant data(int column, int role) const override; QVariant childData(int row, int column, int role) const override; Qt::ItemFlags childFlags(int row, int column, bool fileLoaded = true) const override; BitCount32 childSize(uint index) const override; QString childTypeName(uint index) const override; void setChildWidgetData(uint index, QWidget* w) const override; QVariant dataFromChildWidget(uint index, const QWidget* w) const override; QWidget* createChildEditWidget(uint index, QWidget* parent) const override; QScriptValue childToScriptValue(uint index, QScriptEngine* engine, ScriptHandlerInfo* handlerInfo) const override; BitCount64 childPosition(const DataInformation* child, Okteta::Address start) const override; StringType encoding() const; + QString encodingName() const; void setEncoding(StringType encoding); uint terminationCodePoint() const; void setTerminationCodePoint(uint term); uint maxCharCount() const; void setMaxCharCount(uint count); uint maxByteCount() const; void setMaxByteCount(uint count); int stringLength() const; int stringByteLength() const; uint terminationMode() const; QString valueAt(int index) const; /** Removes this mode from the termination modes. If none is left, changes string to null terminated * @param mode The mode to remove */ void unsetTerminationMode(StringData::TerminationMode mode); private: QScriptClass* scriptClass(ScriptHandlerInfo* handlerInfo) const override; QString typeNameImpl() const override; QString valueStringImpl() const override; private: QScopedPointer mDummy; QScopedPointer mData; StringType mEncoding = StringType::InvalidEncoding; + + static const QString encodingNames[static_cast(StringDataInformation::StringType::EBCDIC)+2]; }; inline StringDataInformation::StringType StringDataInformation::encoding() const { return mEncoding; } +inline QString StringDataInformation::encodingName() const +{ + return encodingNames[static_cast(mEncoding)+1]; +} inline uint StringDataInformation::maxByteCount() const { return mData->maxByteCount(); } inline void StringDataInformation::setMaxByteCount(uint count) { mData->setMaxByteCount(count); } inline uint StringDataInformation::maxCharCount() const { return mData->terminationCodePoint(); } inline void StringDataInformation::setMaxCharCount(uint count) { mData->setMaxCharCount(count); } inline uint StringDataInformation::terminationCodePoint() const { return mData->terminationCodePoint(); } inline void StringDataInformation::setTerminationCodePoint(uint term) { mData->setTerminationCodePoint(term); } inline int StringDataInformation::stringLength() const { return mData->count(); } inline int StringDataInformation::stringByteLength() const { return mData->size() / 8; } inline uint StringDataInformation::terminationMode() const { return mData->terminationMode(); } inline QString StringDataInformation::valueAt(int index) const { Q_ASSERT((uint)index < mData->count()); return mData->stringValue(index); } #endif // KASTEN_STRINGDATAINFORMATION_HPP diff --git a/kasten/controllers/view/structures/script/classes/stringscriptclass.cpp b/kasten/controllers/view/structures/script/classes/stringscriptclass.cpp index c10025d3..57b69226 100644 --- a/kasten/controllers/view/structures/script/classes/stringscriptclass.cpp +++ b/kasten/controllers/view/structures/script/classes/stringscriptclass.cpp @@ -1,206 +1,206 @@ /* * This file is part of the Okteta Kasten Framework, made within the KDE community. * * Copyright 2011, 2012 Alex Richardson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "stringscriptclass.hpp" #include "../../datatypes/strings/stringdatainformation.hpp" #include "../../parsers/parserutils.hpp" #include "../../structlogging.hpp" StringScriptClass::StringScriptClass(QScriptEngine* eng, ScriptHandlerInfo* handlerInfo) : DefaultScriptClass(eng, handlerInfo) { mIterableProperties.reserve(mIterableProperties.size() + 6); // read-only properties s_lengthInCodepoints = eng->toStringHandle(ParserStrings::PROPERTY_CHAR_COUNT()); mIterableProperties.append(qMakePair(s_lengthInCodepoints, QScriptValue::ReadOnly | QScriptValue::Undeletable)); s_lengthInBytes = eng->toStringHandle(ParserStrings::PROPERTY_BYTE_COUNT()); mIterableProperties.append(qMakePair(s_lengthInBytes, QScriptValue::ReadOnly | QScriptValue::Undeletable)); // read-write properties s_maxByteCount = eng->toStringHandle(ParserStrings::PROPERTY_MAX_BYTE_COUNT()); mIterableProperties.append(qMakePair(s_maxByteCount, QScriptValue::PropertyFlags(QScriptValue::Undeletable))); s_maxCharCount = eng->toStringHandle(ParserStrings::PROPERTY_MAX_CHAR_COUNT()); mIterableProperties.append(qMakePair(s_maxCharCount, QScriptValue::PropertyFlags(QScriptValue::Undeletable))); s_terminatedBy = eng->toStringHandle(ParserStrings::PROPERTY_TERMINATED_BY()); mIterableProperties.append(qMakePair(s_terminatedBy, QScriptValue::PropertyFlags(QScriptValue::Undeletable))); s_encoding = eng->toStringHandle(ParserStrings::PROPERTY_ENCODING()); mIterableProperties.append(qMakePair(s_encoding, QScriptValue::PropertyFlags(QScriptValue::Undeletable))); mStringPrototype = eng->newObject(); mStringPrototype.setProperty(QStringLiteral("toString"), eng->newFunction(String_proto_toString)); } StringScriptClass::~StringScriptClass() = default; bool StringScriptClass::queryAdditionalProperty(const DataInformation* data, const QScriptString& name, QScriptClass::QueryFlags* flags, uint* id) { Q_UNUSED(data) // no need to modify flags since both read and write are handled if (name == s_maxByteCount || name == s_maxCharCount || name == s_terminatedBy || name == s_encoding) { return true; } if (name == s_lengthInCodepoints || name == s_lengthInBytes) { *flags &= ~HandlesWriteAccess; return true; } bool isArrayIndex; quint32 pos = name.toArrayIndex(&isArrayIndex); if (isArrayIndex && pos <= uint(data->asString()->stringLength())) { *id = pos + 1; // add 1 to distinguish from the default value of 0 *flags &= ~HandlesWriteAccess; // writing is not yet supported return true; } return false; // not found } bool StringScriptClass::additionalPropertyFlags(const DataInformation* data, const QScriptString& name, uint id, QScriptValue::PropertyFlags* flags) { Q_UNUSED(data) Q_UNUSED(name) if (id != 0) { *flags |= QScriptValue::ReadOnly; return true; } return false; } QScriptValue StringScriptClass::additionalProperty(const DataInformation* data, const QScriptString& name, uint id) { const StringDataInformation* sData = data->asString(); if (id != 0) { quint32 pos = id - 1; if (pos >= uint(sData->stringLength())) { return engine()->currentContext()->throwError(QScriptContext::RangeError, QStringLiteral("Attempting to access string index %1, but length is %2").arg( QString::number(pos), QString::number(sData->stringLength()))); } return sData->valueAt(pos); } if (name == s_lengthInCodepoints) { return sData->stringLength(); } if (name == s_lengthInBytes) { return sData->stringByteLength(); } if (name == s_encoding) { - return stringEncodings[static_cast(sData->encoding())]; + return sData->encodingName(); } if (name == s_maxCharCount) { return sData->maxCharCount(); } if (name == s_maxByteCount) { return sData->maxByteCount(); } if (name == s_terminatedBy) { return sData->terminationCodePoint(); } return {}; } bool StringScriptClass::setAdditionalProperty(DataInformation* data, const QScriptString& name, uint, const QScriptValue& value) { StringDataInformation* sData = data->asString(); if (name == s_maxCharCount) { if (value.isNull()) { sData->logInfo() << "Unsetting max char count."; sData->unsetTerminationMode(StringData::CharCount); } else { ParsedNumber result = ParserUtils::uintFromScriptValue(value); if (result.isValid) { sData->setMaxCharCount(result.value); } else { sData->logError() << "Could not set maximum char count, invalid argument: " << value.toString(); } } return true; } if (name == s_maxByteCount) { if (value.isNull()) { sData->logInfo() << "Unsetting max byte count."; sData->unsetTerminationMode(StringData::ByteCount); } else { ParsedNumber result = ParserUtils::uintFromScriptValue(value); if (result.isValid) { sData->setMaxByteCount(result.value); } else { sData->logError() << "Could not set maximum byte count, invalid argument: " << value.toString(); } } return true; } if (name == s_terminatedBy) { if (value.isNull()) { sData->logInfo() << "Unsetting termination character."; sData->unsetTerminationMode(StringData::Sequence); } else { if (value.isString()) { QString str = value.toString(); // we don't handle surrogate pairs, if you want to set that use a number instead. if (str.length() != 1) { sData->logError() << "Setting termination char: expected one char or a code point number" ", got a string with length " << str.length(); } else { sData->setTerminationCodePoint(str[0].unicode()); } } else { ParsedNumber result = ParserUtils::uintFromScriptValue(value); if (result.isValid) { sData->setTerminationCodePoint(result.value); } else { sData->logError() << "Could not set maximum byte count, invalid argument: " << value.toString(); } } } return true; } if (name == s_encoding) { QString enc = value.toString(); StringDataInformation::StringType encoding = ParserUtils::toStringEncoding(enc, LoggerWithContext(sData->logger(), sData->fullObjectPath())); if (encoding == StringDataInformation::StringType::InvalidEncoding) { sData->logError() << "Attempting to set invalid encoding:" << enc; } else { sData->setEncoding(encoding); } return true; } return false; } QScriptValue StringScriptClass::prototype() const { return mStringPrototype; } QScriptValue StringScriptClass::String_proto_toString(QScriptContext* ctx, QScriptEngine* eng) { DataInformation* data = toDataInformation(ctx->thisObject().data()); if (!data) { qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "could not cast data"; return eng->undefinedValue(); } return data->wasAbleToRead() ? data->valueString() : eng->undefinedValue(); }