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  QString toString(int indent) 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 QString& str, 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 
152 private: // Data
154  QString mValue;
157 };
158 
159 /*******************************************************************************
160  * Serialization Methods
161  ******************************************************************************/
162 
163 template <>
164 inline SExpression serializeToSExpression(const QString& obj) {
165  return SExpression::createString(obj);
166 }
167 
168 template <>
169 inline SExpression serializeToSExpression(const bool& obj) {
170  return SExpression::createToken(obj ? "true" : "false");
171 }
172 
173 template <>
174 inline SExpression serializeToSExpression(const int& obj) {
175  return SExpression::createToken(QString::number(obj));
176 }
177 
178 template <>
179 inline SExpression serializeToSExpression(const uint& obj) {
180  return SExpression::createToken(QString::number(obj));
181 }
182 
183 template <>
184 inline SExpression serializeToSExpression(const QColor& obj) {
185  return SExpression::createString(obj.isValid() ? obj.name(QColor::HexArgb)
186  : "");
187 }
188 
189 template <>
190 inline SExpression serializeToSExpression(const QUrl& obj) {
192  obj.isValid() ? obj.toString(QUrl::PrettyDecoded) : "");
193 }
194 
195 template <>
196 inline SExpression serializeToSExpression(const QDateTime& obj) {
197  return SExpression::createToken(obj.toUTC().toString(Qt::ISODate));
198 }
199 
200 template <>
202  return obj;
203 }
204 
205 /*******************************************************************************
206  * Deserialization Methods
207  ******************************************************************************/
208 
209 template <>
210 inline QString deserializeFromSExpression(const SExpression& sexpr,
211  bool throwIfEmpty) {
212  return sexpr.getStringOrToken(throwIfEmpty); // can throw
213 }
214 
215 template <>
216 inline bool deserializeFromSExpression(const SExpression& sexpr,
217  bool throwIfEmpty) {
218  if (sexpr.getStringOrToken(throwIfEmpty) == "true") {
219  return true;
220  } else if (sexpr.getStringOrToken(throwIfEmpty) == "false") {
221  return false;
222  } else
223  throw RuntimeError(__FILE__, __LINE__,
224  SExpression::tr("Not a valid boolean."));
225 }
226 
227 template <>
228 inline int deserializeFromSExpression(const SExpression& sexpr,
229  bool throwIfEmpty) {
230  bool ok = false;
231  int value = sexpr.getStringOrToken(throwIfEmpty).toInt(&ok);
232  if (ok) {
233  return value;
234  } else
235  throw RuntimeError(__FILE__, __LINE__,
236  SExpression::tr("Not a valid integer."));
237 }
238 
239 template <>
240 inline uint deserializeFromSExpression(const SExpression& sexpr,
241  bool throwIfEmpty) {
242  bool ok = false;
243  uint value = sexpr.getStringOrToken(throwIfEmpty).toUInt(&ok);
244  if (ok) {
245  return value;
246  } else
247  throw RuntimeError(__FILE__, __LINE__,
248  SExpression::tr("Not a valid unsigned integer."));
249 }
250 
251 template <>
252 inline QDateTime deserializeFromSExpression(const SExpression& sexpr,
253  bool throwIfEmpty) {
254  QDateTime obj =
255  QDateTime::fromString(sexpr.getStringOrToken(throwIfEmpty), Qt::ISODate)
256  .toLocalTime();
257  if (obj.isValid())
258  return obj;
259  else
260  throw RuntimeError(__FILE__, __LINE__,
261  SExpression::tr("Not a valid datetime."));
262 }
263 
264 template <>
265 inline QColor deserializeFromSExpression(const SExpression& sexpr,
266  bool throwIfEmpty) {
267  QColor obj(sexpr.getStringOrToken(throwIfEmpty));
268  if (obj.isValid()) {
269  return obj;
270  } else
271  throw RuntimeError(__FILE__, __LINE__,
272  SExpression::tr("Not a valid color."));
273 }
274 
275 template <>
276 inline QUrl deserializeFromSExpression(const SExpression& sexpr,
277  bool throwIfEmpty) {
278  QUrl obj(sexpr.getStringOrToken(throwIfEmpty), QUrl::StrictMode);
279  if (obj.isValid()) {
280  return obj;
281  } else
282  throw RuntimeError(__FILE__, __LINE__, SExpression::tr("Not a valid URL."));
283 }
284 
285 /*******************************************************************************
286  * End of File
287  ******************************************************************************/
288 
289 } // namespace librepcb
290 
291 #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:282
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:270
bool isToken() const noexcept
Definition: sexpression.h:81
FilePath mFilePath
Definition: sexpression.h:156
bool isList() const noexcept
Definition: sexpression.h:80
Type mType
Definition: sexpression.h:153
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:192
static SExpression createToken(const QString &token)
Definition: sexpression.cpp:274
QList< SExpression > mChildren
Definition: sexpression.h:155
QString mValue
either a list name, a token or a string
Definition: sexpression.h:154
static SExpression createString(const QString &string)
Definition: sexpression.cpp:278
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:262
values with double quotes (e.g. "Foo!")
bool isValidListName(const QString &name) const noexcept
Definition: sexpression.cpp:258
static SExpression parse(const QString &str, const FilePath &filePath)
Definition: sexpression.cpp:286
QString escapeString(const QString &string) const noexcept
Definition: sexpression.cpp:254
The SExpression class.
Definition: sexpression.h:60
const SExpression & getChildByPath(const QString &path) const
Definition: sexpression.cpp:150
const FilePath & getFilePath() const noexcept
Definition: sexpression.h:78