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 
60 class SExpression final {
61  Q_DECLARE_TR_FUNCTIONS(SExpression)
62 
63 public:
64  // Types
65  enum class Type {
66  List,
67  Token,
68  String,
69  LineBreak,
70  };
71 
72  // Constructors / Destructor
73  SExpression() noexcept;
74  SExpression(const SExpression& other) noexcept;
75  ~SExpression() noexcept;
76 
77  // Getters
78  const FilePath& getFilePath() const noexcept { return mFilePath; }
79  Type getType() const noexcept { return mType; }
80  bool isList() const noexcept { return mType == Type::List; }
81  bool isToken() const noexcept { return mType == Type::Token; }
82  bool isString() const noexcept { return mType == Type::String; }
83  bool isLineBreak() const noexcept { return mType == Type::LineBreak; }
84  bool isMultiLineList() const noexcept;
85  const QString& getName() const;
86  const QString& getStringOrToken(bool throwIfEmpty = false) const;
87  const QList<SExpression>& getChildren() const { return mChildren; }
88  QList<SExpression> getChildren(const QString& name) const noexcept;
89  const SExpression& getChildByIndex(int index) const;
90  const SExpression* tryGetChildByPath(const QString& path) const noexcept;
91  const SExpression& getChildByPath(const QString& path) const;
92 
93  template <typename T>
94  T getValue(bool throwIfEmpty = false) const {
95  try {
96  return deserializeFromSExpression<T>(*this, throwIfEmpty);
97  } catch (const Exception& e) {
98  throw FileParseError(__FILE__, __LINE__, mFilePath, -1, -1, mValue,
99  e.getMsg());
100  }
101  }
102 
103  template <typename T>
104  T getValueByPath(const QString& path, bool throwIfEmpty = false) const {
105  const SExpression& child = getChildByPath(path);
106  return child.getValueOfFirstChild<T>(throwIfEmpty);
107  }
108 
109  template <typename T>
110  T getValueOfFirstChild(bool throwIfEmpty = false) const {
111  if (mChildren.count() < 1) {
112  throw FileParseError(__FILE__, __LINE__, mFilePath, -1, -1, QString(),
113  tr("Node does not have children."));
114  }
115  return mChildren.at(0).getValue<T>(throwIfEmpty);
116  }
117 
118  // General Methods
120  SExpression& appendList(const QString& name, bool linebreak);
121  SExpression& appendChild(const SExpression& child, bool linebreak);
122  template <typename T>
123  SExpression& appendChild(const T& obj) {
124  appendChild(serializeToSExpression(obj), false);
125  return *this;
126  }
127  template <typename T>
128  SExpression& appendChild(const QString& child, const T& obj, bool linebreak) {
129  return appendList(child, linebreak).appendChild(obj);
130  }
131  void removeLineBreaks() noexcept;
132  QByteArray toByteArray() const;
133 
134  // Operator Overloadings
135  SExpression& operator=(const SExpression& rhs) noexcept;
136 
137  // Static Methods
138  static SExpression createList(const QString& name);
139  static SExpression createToken(const QString& token);
140  static SExpression createString(const QString& string);
141  static SExpression createLineBreak();
142  static SExpression parse(const QByteArray& content, const FilePath& filePath);
143 
144 private: // Methods
145  SExpression(Type type, const QString& value);
146  SExpression(sexpresso::Sexp& sexp, const FilePath& filePath);
147 
148  QString escapeString(const QString& string) const noexcept;
149  bool isValidListName(const QString& name) const noexcept;
150  bool isValidToken(const QString& token) const noexcept;
151  QString toString(int indent) const;
152 
153 private: // Data
155  QString mValue;
158 };
159 
160 /*******************************************************************************
161  * Serialization Methods
162  ******************************************************************************/
163 
164 template <>
165 inline SExpression serializeToSExpression(const QString& obj) {
166  return SExpression::createString(obj);
167 }
168 
169 template <>
170 inline SExpression serializeToSExpression(const bool& obj) {
171  return SExpression::createToken(obj ? "true" : "false");
172 }
173 
174 template <>
175 inline SExpression serializeToSExpression(const int& obj) {
176  return SExpression::createToken(QString::number(obj));
177 }
178 
179 template <>
180 inline SExpression serializeToSExpression(const uint& obj) {
181  return SExpression::createToken(QString::number(obj));
182 }
183 
184 template <>
185 inline SExpression serializeToSExpression(const QColor& obj) {
186  return SExpression::createString(obj.isValid() ? obj.name(QColor::HexArgb)
187  : "");
188 }
189 
190 template <>
191 inline SExpression serializeToSExpression(const QUrl& obj) {
193  obj.isValid() ? obj.toString(QUrl::PrettyDecoded) : "");
194 }
195 
196 template <>
197 inline SExpression serializeToSExpression(const QDateTime& obj) {
198  return SExpression::createToken(obj.toUTC().toString(Qt::ISODate));
199 }
200 
201 template <>
203  return obj;
204 }
205 
206 /*******************************************************************************
207  * Deserialization Methods
208  ******************************************************************************/
209 
210 template <>
211 inline QString deserializeFromSExpression(const SExpression& sexpr,
212  bool throwIfEmpty) {
213  return sexpr.getStringOrToken(throwIfEmpty); // can throw
214 }
215 
216 template <>
217 inline bool deserializeFromSExpression(const SExpression& sexpr,
218  bool throwIfEmpty) {
219  if (sexpr.getStringOrToken(throwIfEmpty) == "true") {
220  return true;
221  } else if (sexpr.getStringOrToken(throwIfEmpty) == "false") {
222  return false;
223  } else
224  throw RuntimeError(__FILE__, __LINE__,
225  SExpression::tr("Not a valid boolean."));
226 }
227 
228 template <>
229 inline int deserializeFromSExpression(const SExpression& sexpr,
230  bool throwIfEmpty) {
231  bool ok = false;
232  int value = sexpr.getStringOrToken(throwIfEmpty).toInt(&ok);
233  if (ok) {
234  return value;
235  } else
236  throw RuntimeError(__FILE__, __LINE__,
237  SExpression::tr("Not a valid integer."));
238 }
239 
240 template <>
241 inline uint deserializeFromSExpression(const SExpression& sexpr,
242  bool throwIfEmpty) {
243  bool ok = false;
244  uint value = sexpr.getStringOrToken(throwIfEmpty).toUInt(&ok);
245  if (ok) {
246  return value;
247  } else
248  throw RuntimeError(__FILE__, __LINE__,
249  SExpression::tr("Not a valid unsigned integer."));
250 }
251 
252 template <>
253 inline QDateTime deserializeFromSExpression(const SExpression& sexpr,
254  bool throwIfEmpty) {
255  QDateTime obj =
256  QDateTime::fromString(sexpr.getStringOrToken(throwIfEmpty), Qt::ISODate)
257  .toLocalTime();
258  if (obj.isValid())
259  return obj;
260  else
261  throw RuntimeError(__FILE__, __LINE__,
262  SExpression::tr("Not a valid datetime."));
263 }
264 
265 template <>
266 inline QColor deserializeFromSExpression(const SExpression& sexpr,
267  bool throwIfEmpty) {
268  QColor obj(sexpr.getStringOrToken(throwIfEmpty));
269  if (obj.isValid()) {
270  return obj;
271  } else
272  throw RuntimeError(__FILE__, __LINE__,
273  SExpression::tr("Not a valid color."));
274 }
275 
276 template <>
277 inline QUrl deserializeFromSExpression(const SExpression& sexpr,
278  bool throwIfEmpty) {
279  QUrl obj(sexpr.getStringOrToken(throwIfEmpty), QUrl::StrictMode);
280  if (obj.isValid()) {
281  return obj;
282  } else
283  throw RuntimeError(__FILE__, __LINE__, SExpression::tr("Not a valid URL."));
284 }
285 
286 /*******************************************************************************
287  * End of File
288  ******************************************************************************/
289 
290 } // namespace librepcb
291 
292 #endif // LIBREPCB_SEXPRESSION_H
const QList< SExpression > & getChildren() const
Definition: sexpression.h:87
const SExpression & getChildByIndex(int index) const
Definition: sexpression.cpp:124
bool isString() const noexcept
Definition: sexpression.h:82
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:65
const QString & getName() const
Definition: sexpression.cpp:92
T getValueOfFirstChild(bool throwIfEmpty=false) const
Definition: sexpression.h:110
The Exception class.
Definition: exceptions.h:87
Type getType() const noexcept
Definition: sexpression.h:79
const QString & getStringOrToken(bool throwIfEmpty=false) const
Definition: sexpression.cpp:101
SExpression serializeToSExpression(const HAlign &obj)
Definition: alignment.h:79
static SExpression createList(const QString &name)
Definition: sexpression.cpp:276
bool isToken() const noexcept
Definition: sexpression.h:81
FilePath mFilePath
Definition: sexpression.h:157
bool isList() const noexcept
Definition: sexpression.h:80
Type mType
Definition: sexpression.h:154
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:123
HAlign deserializeFromSExpression(const SExpression &sexpr, bool throwIfEmpty)
Definition: alignment.h:93
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:156
QString mValue
either a list name, a token or a string
Definition: sexpression.h:155
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:94
manual line break inside a List
bool isMultiLineList() const noexcept
Definition: sexpression.cpp:83
bool isLineBreak() const noexcept
Definition: sexpression.h:83
SExpression & appendChild(const QString &child, const T &obj, bool linebreak)
Definition: sexpression.h:128
The RuntimeError class.
Definition: exceptions.h:219
This class represents absolute, well-formatted paths to files or directories.
Definition: filepath.h:130
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:104
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:60
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:78