1 /* 2 This file is part of Konsole, a terminal emulator for KDE. 3 4 SPDX-FileCopyrightText: 2018 Mariusz Glebocki <mglb@arccos-1.net> 5 6 SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 9 #ifndef TEMPLATE_H 10 #define TEMPLATE_H 11 12 #include <QMap> 13 #include <QString> 14 #include <QVector> 15 16 // QVariant doesn't offer modification in place. Var does. 17 class Var 18 { 19 public: 20 using Number = qint64; 21 using String = QString; 22 using Map = QMap<String, Var>; 23 using Vector = QVector<Var>; 24 25 enum class DataType { 26 Invalid, 27 Number, 28 String, 29 Vector, 30 Map, 31 }; 32 dataTypeAsString()33 const QString dataTypeAsString() const 34 { 35 switch (dataType()) { 36 case DataType::Invalid: 37 return QStringLiteral("Invalid"); 38 case DataType::Number: 39 return QStringLiteral("Number"); 40 case DataType::String: 41 return QStringLiteral("String"); 42 case DataType::Vector: 43 return QStringLiteral("Vector"); 44 case DataType::Map: 45 return QStringLiteral("Map"); 46 default: 47 return QStringLiteral("Unknown?"); 48 } 49 } 50 Var()51 Var() 52 : num(0) 53 , _dataType(DataType::Invalid) 54 { 55 } Var(const Var & other)56 Var(const Var &other) 57 { 58 *this = other; 59 } 60 Var(const Number & newNum)61 Var(const Number &newNum) 62 : _dataType(DataType::Number) 63 { 64 new (&num) auto(newNum); 65 } Var(const String & newStr)66 Var(const String &newStr) 67 : _dataType(DataType::String) 68 { 69 new (&str) auto(newStr); 70 } Var(const Vector & newVec)71 Var(const Vector &newVec) 72 : _dataType(DataType::Vector) 73 { 74 new (&vec) auto(newVec); 75 } Var(const Map & newMap)76 Var(const Map &newMap) 77 : _dataType(DataType::Map) 78 { 79 new (&map) auto(newMap); 80 } 81 82 // Allow initialization without type name Var(const char * newStr)83 Var(const char *newStr) 84 : _dataType(DataType::String) 85 { 86 new (&str) String(QString::fromUtf8(newStr)); 87 } Var(std::initializer_list<Var> newVec)88 Var(std::initializer_list<Var> newVec) 89 : _dataType(DataType::Vector) 90 { 91 new (&vec) Vector(newVec); 92 } 93 ~Var()94 ~Var() 95 { 96 switch (dataType()) { 97 case DataType::String: 98 str.~String(); 99 break; 100 case DataType::Vector: 101 vec.~Vector(); 102 break; 103 case DataType::Map: 104 map.~Map(); 105 break; 106 default: 107 break; 108 } 109 } 110 111 Var &operator=(const Var &other) 112 { 113 _dataType = other.dataType(); 114 switch (other.dataType()) { 115 case DataType::Number: 116 new (&num) auto(other.num); 117 break; 118 case DataType::String: 119 new (&str) auto(other.str); 120 break; 121 case DataType::Vector: 122 new (&vec) auto(other.vec); 123 break; 124 case DataType::Map: 125 new (&map) auto(other.map); 126 break; 127 default: 128 break; 129 } 130 return *this; 131 } 132 133 Var &operator[](unsigned index) 134 { 135 Q_ASSERT(_dataType == DataType::Vector); 136 return vec.data()[index]; 137 } 138 const Var &operator[](unsigned index) const 139 { 140 Q_ASSERT(_dataType == DataType::Vector); 141 return vec.constData()[index]; 142 } 143 Var &operator[](const String &key) 144 { 145 Q_ASSERT(_dataType == DataType::Map); 146 return map[key]; 147 } 148 const Var &operator[](const String &key) const 149 { 150 Q_ASSERT(_dataType == DataType::Map); 151 return *map.find(key); 152 } 153 dataType()154 DataType dataType() const 155 { 156 return _dataType; 157 } 158 159 union { 160 Number num; 161 String str; 162 Vector vec; 163 Map map; 164 }; 165 166 private: 167 DataType _dataType; 168 }; 169 170 class Template 171 { 172 public: 173 Template(const QString &text); 174 void parse(); 175 QString generate(const Var &data); 176 177 struct Element { 178 Element(const Element *parent = nullptr, const QString &name = QString()) outerElement179 : outer() 180 , inner() 181 , name(name) 182 , fmt() 183 , line(0) 184 , column(0) 185 , isComment(false) 186 , children() 187 , parent(parent) 188 { 189 } 190 ElementElement191 Element(const Element &other) 192 : outer(other.outer) 193 , inner(other.inner) 194 , name(other.name) 195 , fmt(other.fmt) 196 , line(other.line) 197 , column(other.column) 198 , isComment(other.isComment) 199 , parent(other.parent) 200 { 201 for (const auto &child : other.children) { 202 children.append(child); 203 } 204 } 205 206 const QString findFmt(Var::DataType type) const; 207 QString path() const; isCommandElement208 bool isCommand() const 209 { 210 return name.startsWith(QLatin1Char('!')); 211 } hasNameElement212 bool hasName() const 213 { 214 return !isCommand() && !name.isEmpty(); 215 } 216 217 static const QString defaultFmt(Var::DataType type); 218 static bool isValidFmt(const QString &fmt, Var::DataType type); 219 220 QStringRef outer; 221 QStringRef inner; 222 QString name; 223 QString fmt; 224 uint line; 225 uint column; 226 bool isComment; 227 QList<Element> children; 228 const Element *parent; 229 }; 230 231 private: 232 void executeCommand(Element &element, const Element &childStub, const QStringList &argv); 233 void parseRecursively(Element &element); 234 int generateRecursively(QString &result, const Element &element, const Var &data, int consumed = 0); 235 236 QString _text; // FIXME: make it pointer (?) 237 Element _root; // FIXME: make it pointer 238 }; 239 240 #endif 241