added methods for String

This commit is contained in:
Igor Korsukov 2022-06-08 11:53:44 +03:00
parent c46ca6b7bc
commit 6f9db08697
9 changed files with 415 additions and 71 deletions

View file

@ -143,11 +143,11 @@ ByteArray MscReader::readScoreFile() const
QString mscxFileName = mainFileName();
ByteArray data = fileData(mscxFileName);
if (data.empty() && reader()->isContainer()) {
QStringList files = reader()->fileList();
for (const QString& name : files) {
StringList files = reader()->fileList();
for (const String& name : files) {
// mscx file in the root dir
if (!name.contains("/") && name.endsWith(".mscx", Qt::CaseInsensitive)) {
mscxFileName = name;
if (!name.contains('/') && name.endsWith(".mscx", mu::CaseInsensitive)) {
mscxFileName = name.toQString();
break;
}
}
@ -164,10 +164,10 @@ std::vector<QString> MscReader::excerptNames() const
}
std::vector<QString> names;
QStringList files = reader()->fileList();
for (const QString& filePath : files) {
if (filePath.startsWith("Excerpts/") && filePath.endsWith(".mscx", Qt::CaseInsensitive)) {
names.push_back(FileInfo(filePath).completeBaseName());
StringList files = reader()->fileList();
for (const String& filePath : files) {
if (filePath.startsWith("Excerpts/") && filePath.endsWith(".mscx", mu::CaseInsensitive)) {
names.push_back(FileInfo(filePath.toQString()).completeBaseName());
}
}
return names;
@ -208,10 +208,10 @@ std::vector<QString> MscReader::imageFileNames() const
}
std::vector<QString> names;
QStringList files = reader()->fileList();
for (const QString& filePath : files) {
StringList files = reader()->fileList();
for (const String& filePath : files) {
if (filePath.startsWith("Pictures/")) {
names.push_back(FileInfo(filePath).fileName());
names.push_back(FileInfo(filePath.toQString()).fileName());
}
}
return names;
@ -285,13 +285,13 @@ bool MscReader::ZipFileReader::isContainer() const
return true;
}
QStringList MscReader::ZipFileReader::fileList() const
StringList MscReader::ZipFileReader::fileList() const
{
IF_ASSERT_FAILED(m_zip) {
return QStringList();
return StringList();
}
QStringList files;
StringList files;
std::vector<ZipReader::FileInfo> fileInfoList = m_zip->fileInfoList();
if (m_zip->status() != ZipReader::NoError) {
LOGD() << "failed read meta, status: " << m_zip->status();
@ -299,7 +299,7 @@ QStringList MscReader::ZipFileReader::fileList() const
for (const ZipReader::FileInfo& fi : fileInfoList) {
if (fi.isFile) {
files << fi.filePath;
files << String::fromQString(fi.filePath);
}
}
@ -354,14 +354,14 @@ bool MscReader::DirReader::isContainer() const
return FileInfo::exists(m_rootPath + "/META-INF/container.xml");
}
QStringList MscReader::DirReader::fileList() const
StringList MscReader::DirReader::fileList() const
{
QStringList files;
StringList files;
QDirIterator::IteratorFlags flags = QDirIterator::Subdirectories;
QDirIterator it(m_rootPath, QStringList(), QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable | QDir::Files, flags);
while (it.hasNext()) {
QString filePath = it.next();
String filePath = String::fromQString(it.next());
files << filePath.mid(m_rootPath.length() + 1);
}
@ -415,13 +415,13 @@ bool MscReader::XmlFileReader::isContainer() const
return true;
}
QStringList MscReader::XmlFileReader::fileList() const
StringList MscReader::XmlFileReader::fileList() const
{
if (!m_device) {
return QStringList();
return StringList();
}
QStringList files;
StringList files;
m_device->seek(0);
XmlStreamReader xml(m_device);
@ -437,7 +437,7 @@ QStringList MscReader::XmlFileReader::fileList() const
continue;
}
QString fileName = xml.attribute("name");
String fileName = xml.attribute("name");
files << fileName;
xml.skipCurrentElement();
}
@ -466,8 +466,8 @@ ByteArray MscReader::XmlFileReader::fileData(const QString& fileName) const
continue;
}
QString file = xml.attribute("name");
if (file != fileName) {
String file = xml.attribute("name");
if (file.toQString() != fileName) {
xml.skipCurrentElement();
continue;
}

View file

@ -25,6 +25,7 @@
#include <QString>
#include "io/iodevice.h"
#include "mscio.h"
#include "types/string.h"
namespace mu {
class ZipReader;
@ -83,7 +84,7 @@ private:
//! it may happen that we are not reading a container (a directory with a certain structure),
//! but only one file among others (`.mscx` from MU 3.x)
virtual bool isContainer() const = 0;
virtual QStringList fileList() const = 0;
virtual StringList fileList() const = 0;
virtual ByteArray fileData(const QString& fileName) const = 0;
};
@ -94,7 +95,7 @@ private:
void close() override;
bool isOpened() const override;
bool isContainer() const override;
QStringList fileList() const override;
StringList fileList() const override;
ByteArray fileData(const QString& fileName) const override;
private:
io::IODevice* m_device = nullptr;
@ -108,7 +109,7 @@ private:
void close() override;
bool isOpened() const override;
bool isContainer() const override;
QStringList fileList() const override;
StringList fileList() const override;
ByteArray fileData(const QString& fileName) const override;
private:
QString m_rootPath;
@ -120,7 +121,7 @@ private:
void close() override;
bool isOpened() const override;
bool isContainer() const override;
QStringList fileList() const override;
StringList fileList() const override;
ByteArray fileData(const QString& fileName) const override;
private:
io::IODevice* m_device = nullptr;

View file

@ -267,7 +267,7 @@ void XmlReader::htmlToString(int level, QString* s)
{
*s += QString("<%1").arg(name().toQLatin1String());
for (const Attribute& a : attributes()) {
*s += QString(" %1=\"%2\"").arg(a.name, a.value);
*s += QString(" %1=\"%2\"").arg(a.name.ascii(), a.value.toQString());
}
*s += ">";
++level;

View file

@ -34,7 +34,7 @@
namespace mu::engraving {
class ReadContext;
class XmlReader : public mu::XmlStreamReader
class XmlReader : public XmlStreamReader
{
public:
@ -55,7 +55,7 @@ public:
void unknown();
// attribute helper routines:
QString attribute(const char* s) const { return mu::XmlStreamReader::attribute(s); }
QString attribute(const char* s) const { return mu::XmlStreamReader::attribute(s).toQString(); }
QString attribute(const char* s, const QString&) const;
int intAttribute(const char* s) const;
int intAttribute(const char* s, int _default) const;

View file

@ -178,21 +178,21 @@ void XmlStreamReader::tryParseEntity(Xml* xml)
const char* str = xml->node->Value();
if (std::strncmp(str, ENTITY, 6) == 0) {
QString val(str);
QStringList list = val.split(' ');
if (list.length() == 3) {
QString name = list.at(1);
QString val = list.at(2);
m_entities["&" + name + ";"] = val.mid(1, val.size() - 2);
String val(str);
StringList list = val.split(' ');
if (list.size() == 3) {
String name = list.at(1);
String val = list.at(2);
m_entities[u"&" + name + u";"] = val.mid(1, val.size() - 2);
} else {
LOGW() << "unknown ENTITY: " << val;
}
}
}
QString XmlStreamReader::nodeValue(Xml* xml) const
String XmlStreamReader::nodeValue(Xml* xml) const
{
QString str = xml->node->Value();
String str = String::fromUtf8(xml->node->Value());
if (!m_entities.empty()) {
for (const auto& p : m_entities) {
str.replace(p.first, p.second);
@ -245,17 +245,17 @@ AsciiStringView XmlStreamReader::name() const
return (m_xml->node && m_xml->node->ToElement()) ? m_xml->node->Value() : AsciiStringView();
}
QString XmlStreamReader::attribute(const char* name) const
String XmlStreamReader::attribute(const char* name) const
{
if (m_token != TokenType::StartElement) {
return QString();
return String();
}
XMLElement* e = m_xml->node->ToElement();
if (!e) {
return QString();
return String();
}
return e->Attribute(name);
return String(e->Attribute(name));
}
AsciiStringView XmlStreamReader::asciiAttribute(const char* name) const
@ -308,14 +308,14 @@ std::vector<XmlStreamReader::Attribute> XmlStreamReader::attributes() const
QString XmlStreamReader::readElementText()
{
if (isStartElement()) {
QString result;
String result;
while (1) {
switch (readNext()) {
case Characters:
result.append(nodeValue(m_xml));
result = nodeValue(m_xml);
break;
case EndElement:
return result;
return result.toQString();
case Comment:
break;
case StartElement:
@ -331,7 +331,7 @@ QString XmlStreamReader::readElementText()
QString XmlStreamReader::text() const
{
if (m_xml->node && (m_xml->node->ToText() || m_xml->node->ToComment())) {
return nodeValue(m_xml);
return nodeValue(m_xml).toQString();
}
return QString();
}

View file

@ -59,8 +59,8 @@ public:
struct Attribute
{
QString name;
QString value;
AsciiStringView name;
String value;
};
XmlStreamReader();
@ -90,7 +90,7 @@ public:
AsciiStringView name() const;
QString attribute(const char* name) const;
String attribute(const char* name) const;
AsciiStringView asciiAttribute(const char* name) const;
bool hasAttribute(const char* name) const;
std::vector<Attribute> attributes() const;
@ -112,12 +112,12 @@ private:
struct Xml;
void tryParseEntity(Xml* xml);
QString nodeValue(Xml* xml) const;
String nodeValue(Xml* xml) const;
Xml* m_xml = nullptr;
TokenType m_token = TokenType::NoToken;
std::map<QString, QString> m_entities;
std::map<String, String> m_entities;
};
}

View file

@ -109,6 +109,43 @@ TEST_F(Global_Types_StringTests, String_Convert)
}
}
TEST_F(Global_Types_StringTests, String_Compare)
{
{
//! GIVEN Some Strings
String str1(u"123abc");
String str2(u"123abc");
String str3(u"abc123");
String str4(u"abc");
//! CHECK
EXPECT_TRUE(str1 == str2);
EXPECT_TRUE(str1 != str3);
EXPECT_TRUE(str1 != str4);
EXPECT_TRUE(str1 == u"123abc");
EXPECT_TRUE(str1 != u"abc123");
EXPECT_TRUE(str1 != u"abc");
EXPECT_TRUE(u"123abc" == str1);
EXPECT_TRUE(u"abc123" != str1);
EXPECT_TRUE(u"abc" != str1);
}
{
//! GIVEN Some ASCII Strings
String str1(u"02");
String str2(u"20");
String str3(u"22");
String str4(u"002");
//! CHECK
EXPECT_TRUE(str1 < str2);
EXPECT_TRUE(str2 < str3);
EXPECT_TRUE(str4 < str3);
}
}
TEST_F(Global_Types_StringTests, String_Access)
{
{
@ -130,6 +167,107 @@ TEST_F(Global_Types_StringTests, String_Access)
}
}
TEST_F(Global_Types_StringTests, String_Replace)
{
{
//! GIVEN Some String
String str = u"123abcПыф";
//! DO
str.replace(u"abc", u"def").replace(u"Пыф", u"Длж");
//! CHECK
EXPECT_EQ(str, u"123defДлж");
}
{
//! GIVEN Some String
String str = u"123abcПыф";
//! DO
str.replace(u"abc", u"de");
//! CHECK
EXPECT_EQ(str, u"123deПыф");
}
{
//! GIVEN Some String
String str = u"123abc456abc";
//! DO
str.replace(u"abc", u"de");
//! CHECK
EXPECT_EQ(str, u"123de456de");
}
}
TEST_F(Global_Types_StringTests, String_Modify)
{
{
//! GIVEN Some String
String str = u"123abc";
//! DO
String newStr = str + u"Пыф";
//! CHECK
EXPECT_EQ(newStr, u"123abcПыф");
}
{
//! GIVEN Some String
String str = u"123abc";
//! DO
str += u"Пыф";
//! CHECK
EXPECT_EQ(str, u"123abcПыф");
}
}
TEST_F(Global_Types_StringTests, String_SubStr)
{
{
//! GIVEN Some String
String str = u"123abc";
//! DO
String newStr = str.mid(2, 2);
//! CHECK
EXPECT_EQ(newStr, u"3a");
}
{
//! GIVEN Some String
String str = u"123abc";
//! DO
String newStr = str.mid(2);
//! CHECK
EXPECT_EQ(newStr, u"3abc");
}
}
TEST_F(Global_Types_StringTests, String_Split)
{
{
//! GIVEN Some String
String str = u"wwww gggg ыыыы";
//! DO
StringList list = str.split(u' ');
//! CHECK
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(list.at(0), u"wwww");
EXPECT_EQ(list.at(1), u"gggg");
EXPECT_EQ(list.at(2), u"ыыыы");
}
}
TEST_F(Global_Types_StringTests, String_StartEndWith)
{
{
//! GIVEN Some String
String str = u"123abc";
//! CHECK
EXPECT_TRUE(str.startsWith("12"));
EXPECT_FALSE(str.startsWith("13"));
//! CHECK
EXPECT_TRUE(str.endsWith("bc"));
EXPECT_FALSE(str.endsWith("ac"));
}
}
TEST_F(Global_Types_StringTests, AsciiString_Construct)
{
//! GIVEN Some ASCII String

View file

@ -22,6 +22,8 @@
#include "string.h"
#include <cstring>
#include <cstdlib>
#include <locale>
#include <cctype>
#include "global/thirdparty/utfcpp-3.2.1/utf8.h"
@ -32,6 +34,20 @@ using namespace mu;
// ============================
// Char
// ============================
char Char::ascii(bool* ok) const
{
if (m_ch > 0xff) {
if (ok) {
*ok = false;
}
return '?';
} else {
if (ok) {
*ok = true;
}
return static_cast<char>(m_ch);
}
}
// ============================
// UtfCodec
@ -60,12 +76,48 @@ String::String(const char16_t* str)
m_data = std::make_shared<std::u16string>(str);
}
const std::u16string& String::constStr() const
{
return *m_data.get();
}
std::u16string& String::mutStr()
{
detach();
return *m_data.get();
}
void String::detach()
{
if (!m_data) {
return;
}
if (m_data.use_count() == 1) {
return;
}
m_data = std::make_shared<std::u16string>(*m_data);
}
String& String::operator=(const char16_t* str)
{
m_data = std::make_shared<std::u16string>(str);
return *this;
}
String& String::operator +=(const String& s)
{
mutStr() += s.constStr();
return *this;
}
String& String::operator +=(const char16_t* s)
{
mutStr() += s;
return *this;
}
String String::fromUtf8(const char* str)
{
String s;
@ -90,15 +142,12 @@ ByteArray String::toAscii(bool* ok) const
}
for (size_t i = 0; i < size(); ++i) {
char16_t ch = m_data->at(i);
if (ch > 0xff) {
ba[i] = '?';
if (ok) {
*ok = false;
}
} else {
ba[i] = static_cast<uint8_t>(ch);
bool cok = false;
char ch = Char(m_data->at(i)).ascii(&cok);
if (!cok && ok) {
*ok = false;
}
ba[i] = static_cast<uint8_t>(ch);
}
return ba;
}
@ -153,6 +202,106 @@ Char String::at(size_t i) const
return Char(m_data->at(i));
}
bool String::contains(const Char& ch) const
{
return constStr().find(ch.unicode()) != std::u16string::npos;
}
bool String::startsWith(const AsciiStringView& str, CaseSensitivity cs) const
{
if (str.size() > size()) {
return false;
}
for (size_t i = 0; i < str.size(); ++i) {
if (Char(m_data->at(i)).ascii() == str.at(i).ascii()) {
continue;
}
if (cs == CaseInsensitive) {
if (AsciiChar::toLower(Char(m_data->at(i)).ascii()) == AsciiChar::toLower(str.at(i).ascii())) {
continue;
}
}
return false;
}
return true;
}
bool String::endsWith(const AsciiStringView& str, CaseSensitivity cs) const
{
if (str.size() > size()) {
return false;
}
size_t start = size() - str.size();
for (size_t i = 0; i < str.size(); ++i) {
if (Char(m_data->at(start + i)).ascii() == str.at(i).ascii()) {
continue;
}
if (cs == CaseInsensitive) {
if (AsciiChar::toLower(Char(m_data->at(start + i)).ascii()) == AsciiChar::toLower(str.at(i).ascii())) {
continue;
}
}
return false;
}
return true;
}
StringList String::split(const Char& ch) const
{
StringList out;
std::size_t current, previous = 0;
current = constStr().find(ch.unicode());
while (current != std::string::npos) {
String sub = mid(previous, current - previous);
out.push_back(std::move(sub));
previous = current + 1;
current = constStr().find(ch.unicode(), previous);
}
String sub = mid(previous, current - previous);
out.push_back(std::move(sub));
return out;
}
String& String::replace(const String& before, const String& after)
{
size_t start_pos = 0;
while ((start_pos = m_data->find(before.constStr(), start_pos)) != std::string::npos) {
m_data->replace(start_pos, before.size(), after.constStr());
start_pos += after.size(); // Handles case where 'after' is a substring of 'before'
}
return *this;
}
String String::mid(size_t pos, size_t count) const
{
String s;
s.mutStr() = constStr().substr(pos, count);
return s;
}
// ============================
// AsciiStringView
// ============================
char AsciiChar::toLower(char ch)
{
return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}
char AsciiChar::toUpper(char ch)
{
return static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
}
// ============================
// AsciiStringView
// ============================

View file

@ -32,6 +32,11 @@
#include "global/logstream.h"
namespace mu {
enum CaseSensitivity {
CaseInsensitive = 0,
CaseSensitive = 1
};
// ============================
// AsciiChar (ASCII)
// ============================
@ -45,6 +50,9 @@ public:
inline char ascii() const noexcept { return ch; }
inline char16_t unicode() const noexcept { return char16_t(ch); }
static char toLower(char ch);
static char toUpper(char ch);
private:
char ch = 0;
};
@ -58,16 +66,22 @@ public:
Char() = default;
Char(char16_t c)
: ch(c) {}
: m_ch(c) {}
Char(AsciiChar c)
: ch(c.unicode()) {}
: m_ch(c.unicode()) {}
inline bool operator ==(Char c) const { return ch == c.ch; }
inline bool operator ==(char16_t c) const { return ch == c; }
inline bool operator ==(AsciiChar c) const { return ch == c.unicode(); }
inline bool operator ==(Char c) const { return m_ch == c.m_ch; }
inline bool operator !=(Char c) const { return !operator ==(c); }
inline bool operator ==(char16_t c) const { return m_ch == c; }
inline bool operator !=(char16_t c) const { return !operator ==(c); }
inline bool operator ==(AsciiChar c) const { return m_ch == c.unicode(); }
inline bool operator !=(AsciiChar c) const { return !operator ==(c); }
inline char16_t unicode() const { return m_ch; }
inline char ascii(bool* ok = nullptr) const;
private:
char16_t ch = 0;
char16_t m_ch = 0;
};
// ============================
@ -83,6 +97,8 @@ public:
// ============================
// String (UTF-16)
// ============================
class StringList;
class AsciiStringView;
class String
{
public:
@ -94,12 +110,12 @@ public:
String(const char16_t* str);
String& operator=(const char16_t* str);
bool operator ==(const String& s) const { return *m_data.get() == *s.m_data.get(); }
bool operator !=(const String& s) const { return !operator ==(s); }
inline bool operator ==(const String& s) const { return constStr() == s.constStr(); }
inline bool operator !=(const String& s) const { return !operator ==(s); }
inline bool operator <(const String& s) const { return constStr() < s.constStr(); }
size_t size() const;
bool empty() const;
Char at(size_t i) const;
String& operator +=(const String& s);
String& operator +=(const char16_t* s);
static String fromUtf8(const char* str);
ByteArray toUtf8() const;
@ -115,10 +131,35 @@ public:
QString toQString() const;
//#endif
size_t size() const;
bool empty() const;
Char at(size_t i) const;
bool contains(const Char& ch) const;
//! NOTE Now implemented only compare with ASCII
bool startsWith(const AsciiStringView& str, CaseSensitivity cs = CaseSensitive) const;
bool endsWith(const AsciiStringView& str, CaseSensitivity cs = CaseSensitive) const;
StringList split(const Char& ch) const;
String& replace(const String& before, const String& after);
String mid(size_t pos, size_t count = npos) const;
private:
const std::u16string& constStr() const;
std::u16string& mutStr();
void detach();
std::shared_ptr<std::u16string> m_data;
};
class StringList : public std::vector<String>
{
public:
StringList& operator <<(const String& s) { push_back(s); return *this; }
};
// ============================
// AsciiStringView (ASCII)
// Be carefully!!, this class just hold pointer to string (no copy), so source string should be present, while use view
@ -173,6 +214,21 @@ private:
};
}
// ============================
// String (UTF-16)
// ============================
inline bool operator ==(const char16_t* s1, const mu::String& s2) { return s2 == s1; }
inline bool operator !=(const char16_t* s1, const mu::String& s2) { return s2 != s1; }
inline const mu::String operator+(const mu::String& s1, const mu::String& s2) { mu::String t(s1); t += s2; return t; }
inline const mu::String operator+(const mu::String& s1, const char16_t* s2) { mu::String t(s1); t += s2; return t; }
inline const mu::String operator+(const char16_t* s1, const mu::String& s2) { mu::String t(s1); t += s2; return t; }
inline mu::logger::Stream& operator<<(mu::logger::Stream& s, const mu::String& str)
{
s << str.toUtf8().constChar();
return s;
}
// ============================
// AsciiStringView (ASCII)
// ============================