LibrePCB Developers Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
sexpression.h
Go to the documentation of this file.
1 /*
2  * LibrePCB - Professional EDA for everyone!
3  * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4  * https://librepcb.org/
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef LIBREPCB_SEXPRESSION_H
21 #define LIBREPCB_SEXPRESSION_H
22 
23 /*******************************************************************************
24  * Includes
25  ******************************************************************************/
26 #include "../exceptions.h"
27 #include "filepath.h"
28 
29 #include <QtCore>
30 #include <QtWidgets>
31 
32 /*******************************************************************************
33  * Namespace / Forward Declarations
34  ******************************************************************************/
35 namespace sexpresso {
36 struct Sexp;
37 }
38 
39 namespace librepcb {
40 
41 class SExpression;
42 
43 template <typename T>
44 SExpression serializeToSExpression(const T& obj); // can throw
45 
46 template <typename T>
47 T deserializeFromSExpression(const SExpression& sexpr,
48  bool throwIfEmpty); // can throw
49 
50 /*******************************************************************************
51  * Class SExpression
52  ******************************************************************************/
53 
57 class SExpression final {
58  Q_DECLARE_TR_FUNCTIONS(SExpression)
59 
60 public:
61  // Types
62  enum class Type {
63  List,
64  Token,
65  String,
66  LineBreak,
67  };
68 
69  // Constructors / Destructor
70  SExpression() noexcept;
71  SExpression(const SExpression& other) noexcept;
72  ~SExpression() noexcept;
73 
74  // Getters
75  const FilePath& getFilePath() const noexcept { return mFilePath; }
76  Type getType() const noexcept { return mType; }
77  bool isList() const noexcept { return mType == Type::List; }
78  bool isToken() const noexcept { return mType == Type::Token; }
79  bool isString() const noexcept { return mType == Type::String; }
80  bool isLineBreak() const noexcept { return mType == Type::LineBreak; }
81  bool isMultiLineList() const noexcept;
82  const QString& getName() const;
83  const QString& getStringOrToken(bool throwIfEmpty = false) const;
84  const QList<SExpression>& getChildren() const { return mChildren; }
85  QList<SExpression> getChildren(const QString& name) const noexcept;
86  const SExpression& getChildByIndex(int index) const;
87  const SExpression* tryGetChildByPath(const QString& path) const noexcept;
88  const SExpression& getChildByPath(const QString& path) const;
89 
90  template <typename T>
91  T getValue(bool throwIfEmpty = false) const {
92  try {
93  return deserializeFromSExpression<T>(*this, throwIfEmpty);
94  } catch (const Exception& e) {
95  throw FileParseError(__FILE__, __LINE__, mFilePath, -1, -1, mValue,
96  e.getMsg());
97  }
98  }
99 
100  template <typename T>
101  T getValueByPath(const QString& path, bool throwIfEmpty = false) const {
102  const SExpression& child = getChildByPath(path);
103  return child.getValueOfFirstChild<T>(throwIfEmpty);
104  }
105 
106  template <typename T>
107  T getValueOfFirstChild(bool throwIfEmpty = false) const {
108  if (mChildren.count() < 1) {
109  throw FileParseError(__FILE__, __LINE__, mFilePath, -1, -1, QString(),
110  tr("Node does not have children."));
111  }
112  return mChildren.at(0).getValue<T>(throwIfEmpty);
113  }
114 
115  // General Methods
117  SExpression& appendList(const QString& name, bool linebreak);
118  SExpression& appendChild(const SExpression& child, bool linebreak);
119  template <typename T>
120  SExpression& appendChild(const T& obj) {
121  appendChild(serializeToSExpression(obj), false);
122  return *this;
123  }
124  template <typename T>
125  SExpression& appendChild(const QString& child, const T& obj, bool linebreak) {
126  return appendList(child, linebreak).appendChild(obj);
127  }
128  void removeLineBreaks() noexcept;
129  QByteArray toByteArray() const;
130 
131  // Operator Overloadings
132  SExpression& operator=(const SExpression& rhs) noexcept;
133 
134  // Static Methods
135  static SExpression createList(const QString& name);
136  static SExpression createToken(const QString& token);
137  static SExpression createString(const QString& string);
138  static SExpression createLineBreak();
139  static SExpression parse(const QByteArray& content, const FilePath& filePath);
140 
141 private: // Methods
142  SExpression(Type type, const QString& value);
143  SExpression(sexpresso::Sexp& sexp, const FilePath& filePath);
144 
145  QString escapeString(const QString& string) const noexcept;
146  bool isValidListName(const QString& name) const noexcept;
147  bool isValidToken(const QString& token) const noexcept;
148  QString toString(int indent) const;
149 
150 private: // Data
152  QString mValue;
155 };
156 
157 /*******************************************************************************
158  * Serialization Methods
159  ******************************************************************************/
160 
161 template <>
162 inline SExpression serializeToSExpression(const QString& obj) {
163  return SExpression::createString(obj);
164 }
165 
166 template <>
167 inline SExpression serializeToSExpression(const bool& obj) {
168  return SExpression::createToken(obj ? "true" : "false");
169 }
170 
171 template <>
172 inline SExpression serializeToSExpression(const int& obj) {
173  return SExpression::createToken(QString::number(obj));
174 }
175 
176 template <>
177 inline SExpression serializeToSExpression(const uint& obj) {
178  return SExpression::createToken(QString::number(obj));
179 }
180 
181 template <>
182 inline SExpression serializeToSExpression(const QColor& obj) {
183  return SExpression::createString(obj.isValid() ? obj.name(QColor::HexArgb)
184  : "");
185 }
186 
187 template <>
188 inline SExpression serializeToSExpression(const QUrl& obj) {
190  obj.isValid() ? obj.toString(QUrl::PrettyDecoded) : "");
191 }
192 
193 template <>
194 inline SExpression serializeToSExpression(const QDateTime& obj) {
195  return SExpression::createToken(obj.toUTC().toString(Qt::ISODate));
196 }
197 
198 template <>
200  return obj;
201 }
202 
203 /*******************************************************************************
204  * Deserialization Methods
205  ******************************************************************************/
206 
207 template <>
208 inline QString deserializeFromSExpression(const SExpression& sexpr,
209  bool throwIfEmpty) {
210  return sexpr.getStringOrToken(throwIfEmpty); // can throw
211 }
212 
213 template <>
214 inline bool deserializeFromSExpression(const SExpression& sexpr,
215  bool throwIfEmpty) {
216  if (sexpr.getStringOrToken(throwIfEmpty) == "true") {
217  return true;
218  } else if (sexpr.getStringOrToken(throwIfEmpty) == "false") {
219  return false;
220  } else
221  throw RuntimeError(__FILE__, __LINE__,
222  SExpression::tr("Not a valid boolean."));
223 }
224 
225 template <>
226 inline int deserializeFromSExpression(const SExpression& sexpr,
227  bool throwIfEmpty) {
228  bool ok = false;
229  int value = sexpr.getStringOrToken(throwIfEmpty).toInt(&ok);
230  if (ok) {
231  return value;
232  } else
233  throw RuntimeError(__FILE__, __LINE__,
234  SExpression::tr("Not a valid integer."));
235 }
236 
237 template <>
238 inline uint deserializeFromSExpression(const SExpression& sexpr,
239  bool throwIfEmpty) {
240  bool ok = false;
241  uint value = sexpr.getStringOrToken(throwIfEmpty).toUInt(&ok);
242  if (ok) {
243  return value;
244  } else
245  throw RuntimeError(__FILE__, __LINE__,
246  SExpression::tr("Not a valid unsigned integer."));
247 }
248 
249 template <>
250 inline QDateTime deserializeFromSExpression(const SExpression& sexpr,
251  bool throwIfEmpty) {
252  QDateTime obj =
253  QDateTime::fromString(sexpr.getStringOrToken(throwIfEmpty), Qt::ISODate)
254  .toLocalTime();
255  if (obj.isValid())
256  return obj;
257  else
258  throw RuntimeError(__FILE__, __LINE__,
259  SExpression::tr("Not a valid datetime."));
260 }
261 
262 template <>
263 inline QColor deserializeFromSExpression(const SExpression& sexpr,
264  bool throwIfEmpty) {
265  QColor obj(sexpr.getStringOrToken(throwIfEmpty));
266  if (obj.isValid()) {
267  return obj;
268  } else
269  throw RuntimeError(__FILE__, __LINE__,
270  SExpression::tr("Not a valid color."));
271 }
272 
273 template <>
274 inline QUrl deserializeFromSExpression(const SExpression& sexpr,
275  bool throwIfEmpty) {
276  QUrl obj(sexpr.getStringOrToken(throwIfEmpty), QUrl::StrictMode);
277  if (obj.isValid()) {
278  return obj;
279  } else
280  throw RuntimeError(__FILE__, __LINE__, SExpression::tr("Not a valid URL."));
281 }
282 
283 /*******************************************************************************
284  * End of File
285  ******************************************************************************/
286 
287 } // namespace librepcb
288 
289 #endif // LIBREPCB_SEXPRESSION_H
const QList< SExpression > & getChildren() const
Definition: sexpression.h:84
const SExpression & getChildByIndex(int index) const
Definition: sexpression.cpp:124
bool isString() const noexcept
Definition: sexpression.h:79
const QString & getMsg() const
Get the error message (translated)
Definition: exceptions.h:126
static SExpression createLineBreak()
Definition: sexpression.cpp:288
SExpression & appendLineBreak()
Definition: sexpression.cpp:164
SExpression & appendChild(const SExpression &child, bool linebreak)
Definition: sexpression.cpp:173
Type
Definition: sexpression.h:62
const QString & getName() const
Definition: sexpression.cpp:92
T getValueOfFirstChild(bool throwIfEmpty=false) const
Definition: sexpression.h:107
The Exception class.
Definition: exceptions.h:87
Type getType() const noexcept
Definition: sexpression.h:76
const QString & getStringOrToken(bool throwIfEmpty=false) const
Definition: sexpression.cpp:101
SExpression serializeToSExpression(const HAlign &obj)
Definition: alignment.h:76
static SExpression createList(const QString &name)
Definition: sexpression.cpp:276
bool isToken() const noexcept
Definition: sexpression.h:78
FilePath mFilePath
Definition: sexpression.h:154
bool isList() const noexcept
Definition: sexpression.h:77
Type mType
Definition: sexpression.h:151
values without quotes (e.g. -12.34)
const SExpression * tryGetChildByPath(const QString &path) const noexcept
Definition: sexpression.cpp:132
SExpression & appendChild(const T &obj)
Definition: sexpression.h:120
HAlign deserializeFromSExpression(const SExpression &sexpr, bool throwIfEmpty)
Definition: alignment.h:90
QString toString(int indent) const
Definition: sexpression.cpp:226
static SExpression createToken(const QString &token)
Definition: sexpression.cpp:280
QList< SExpression > mChildren
Definition: sexpression.h:153
QString mValue
either a list name, a token or a string
Definition: sexpression.h:152
static SExpression createString(const QString &string)
Definition: sexpression.cpp:284
SExpression & appendList(const QString &name, bool linebreak)
Definition: sexpression.cpp:169
has a tag name and an arbitrary number of children
void removeLineBreaks() noexcept
Definition: sexpression.cpp:184
T getValue(bool throwIfEmpty=false) const
Definition: sexpression.h:91
manual line break inside a List
bool isMultiLineList() const noexcept
Definition: sexpression.cpp:83
bool isLineBreak() const noexcept
Definition: sexpression.h:80
SExpression & appendChild(const QString &child, const T &obj, bool linebreak)
Definition: sexpression.h:125
The RuntimeError class.
Definition: exceptions.h:219
This class represents absolute, well-formatted paths to files or directories.
Definition: filepath.h:127
The FileParseError class.
Definition: exceptions.h:313
SExpression() noexcept
Definition: sexpression.cpp:38
T getValueByPath(const QString &path, bool throwIfEmpty=false) const
Definition: sexpression.h:101
bool isValidToken(const QString &token) const noexcept
Definition: sexpression.cpp:222
values with double quotes (e.g. "Foo!")
QByteArray toByteArray() const
Definition: sexpression.cpp:192
bool isValidListName(const QString &name) const noexcept
Definition: sexpression.cpp:218
QString escapeString(const QString &string) const noexcept
Definition: sexpression.cpp:214
The SExpression class.
Definition: sexpression.h:57
const SExpression & getChildByPath(const QString &path) const
Definition: sexpression.cpp:150
static SExpression parse(const QByteArray &content, const FilePath &filePath)
Definition: sexpression.cpp:292
const FilePath & getFilePath() const noexcept
Definition: sexpression.h:75