LibrePCB Developers Documentation
serializablekeyvaluemap.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_SERIALIZABLEKEYVALUEMAP_H
21 #define LIBREPCB_SERIALIZABLEKEYVALUEMAP_H
22 
23 /*******************************************************************************
24  * Includes
25  ******************************************************************************/
26 #include "../elementname.h"
27 #include "../signalslot.h"
28 #include "serializableobject.h"
29 
30 #include <QtCore>
31 
32 #include <optional.hpp>
33 
34 /*******************************************************************************
35  * Namespace / Forward Declarations
36  ******************************************************************************/
37 namespace librepcb {
38 
39 /*******************************************************************************
40  * Class SerializableKeyValueMap
41  ******************************************************************************/
42 
54 template <typename T>
56  Q_DECLARE_TR_FUNCTIONS(SerializableKeyValueMap)
57 
58 public:
59  // Signals
60  enum class Event {
64  };
67 
68  // Constructors / Destructor
69  SerializableKeyValueMap() = delete;
71  : onEdited(*this), mValues(other.mValues) {}
73  const typename T::ValueType& defaultValue) noexcept
74  : onEdited(*this) {
75  mValues.insert("", defaultValue);
76  }
77  SerializableKeyValueMap(const SExpression& node, const Version& fileFormat)
78  : onEdited(*this) {
79  foreach (const SExpression& child, node.getChildren(T::tagname)) {
80  QString key;
82  if (child.getChildren().count() > 1) {
83  key = child.getChild(QString(T::keyname) % "/@0").getValue();
84  value = child.getChild("@1");
85  } else {
86  key = QString("");
87  value = child.getChild("@0");
88  }
89  if (mValues.contains(key)) {
90  throw RuntimeError(__FILE__, __LINE__,
91  tr("Key \"%1\" defined multiple times.").arg(key));
92  }
93  mValues.insert(
94  key,
95  deserialize<typename T::ValueType>(value, fileFormat)); // can throw
96  }
97  if (!mValues.contains(QString(""))) {
98  throw RuntimeError(__FILE__, __LINE__,
99  tr("No default %1 defined.").arg(T::tagname));
100  }
101  }
103 
104  // Getters
105  QStringList keys() const noexcept { return mValues.keys(); }
106  const typename T::ValueType& getDefaultValue() const noexcept {
107  auto i = mValues.find(QString(""));
108  // there must always be a default value!!!
109  Q_ASSERT((i != mValues.end()) && (i.key() == QString("")));
110  return i.value();
111  }
112  bool contains(const QString& key) const noexcept {
113  return mValues.contains(key);
114  }
115  tl::optional<typename T::ValueType> tryGet(const QString& key) const
116  noexcept {
117  auto i = mValues.find(key);
118  if ((i != mValues.end()) && (i.key() == key)) {
119  return i.value();
120  } else {
121  return tl::nullopt;
122  }
123  }
124  const typename T::ValueType& value(const QStringList& keyOrder,
125  QString* usedKey = nullptr) const
126  noexcept {
127  // search in the specified key order
128  foreach (const QString& key, keyOrder) {
129  auto i = mValues.find(key);
130  if ((i != mValues.end()) && (i.key() == key)) {
131  if (usedKey) *usedKey = key;
132  return i.value();
133  }
134  }
135  // use default value (empty key) as fallback
136  if (usedKey) *usedKey = QString("");
137  return getDefaultValue();
138  }
139 
140  // General Methods
141 
142  void setDefaultValue(const typename T::ValueType& value) noexcept {
143  insert(QString(""), value);
144  }
145 
146  void insert(const QString& key, const typename T::ValueType& value) noexcept {
147  auto it = mValues.find(key);
148  if (it == mValues.end()) {
149  mValues.insert(key, value);
150  onEdited.notify(key, Event::ElementAdded);
151  } else if (it.value() != value) {
152  mValues.insert(key, value);
153  onEdited.notify(key, Event::ElementValueChanged);
154  }
155  }
156 
158  void serialize(SExpression& root) const override {
159  for (auto i = mValues.constBegin(); i != mValues.constEnd(); ++i) {
160  SExpression& child = root.appendList(T::tagname, true);
161  if (!i.key().isEmpty()) {
162  child.appendChild(T::keyname, i.key(), false);
163  }
164  child.appendChild(i.value());
165  }
166  }
167 
168  // Operator Overloadings
170  const SerializableKeyValueMap<T>& rhs) noexcept {
171  foreach (const QString& key, mValues.keys()) {
172  mValues.remove(key);
173  onEdited.notify(key, Event::ElementRemoved);
174  }
175  mValues = rhs.mValues;
176  QMapIterator<QString, typename T::ValueType> i(rhs.mValues);
177  while (i.hasNext()) {
178  i.next();
179  insert(i.key(), i.value());
180  }
181  return *this;
182  }
183  bool operator==(const SerializableKeyValueMap<T>& rhs) const noexcept {
184  return mValues == rhs.mValues;
185  }
186  bool operator!=(const SerializableKeyValueMap<T>& rhs) const noexcept {
187  return mValues != rhs.mValues;
188  }
189 
190 private: // Data
191  QMap<QString, typename T::ValueType> mValues;
192 };
193 
194 /*******************************************************************************
195  * Class LocalizedNameMap
196  ******************************************************************************/
197 
200  static constexpr const char* tagname = "name";
201  static constexpr const char* keyname = "locale";
202 };
204 
205 /*******************************************************************************
206  * Class LocalizedDescriptionMap
207  ******************************************************************************/
208 
210  typedef QString ValueType;
211  static constexpr const char* tagname = "description";
212  static constexpr const char* keyname = "locale";
213 };
216 
217 /*******************************************************************************
218  * Class LocalizedKeywordsMap
219  ******************************************************************************/
220 
222  typedef QString ValueType;
223  static constexpr const char* tagname = "keywords";
224  static constexpr const char* keyname = "locale";
225 };
226 using LocalizedKeywordsMap =
228 
229 /*******************************************************************************
230  * End of File
231  ******************************************************************************/
232 
233 } // namespace librepcb
234 
235 #endif // LIBREPCB_SERIALIZABLEKEYVALUEMAP_H
Slot< SerializableKeyValueMap< T >, const QString &, Event > OnEditedSlot
Definition: serializablekeyvaluemap.h:66
bool contains(const QString &key) const noexcept
Definition: serializablekeyvaluemap.h:112
The Version class represents a version number in the format "1.42.7".
Definition: version.h:60
const QList< SExpression > & getChildren() const noexcept
Definition: sexpression.h:99
SerializableKeyValueMap(const SExpression &node, const Version &fileFormat)
Definition: serializablekeyvaluemap.h:77
SExpression & appendChild(const SExpression &child, bool linebreak)
Definition: sexpression.cpp:148
Definition: airwiresbuilder.cpp:32
QString ValueType
Definition: serializablekeyvaluemap.h:222
QStringList keys() const noexcept
Definition: serializablekeyvaluemap.h:105
void serialize(SExpression &root) const override
Serialize the object into an existing S-Expression node.
Definition: serializablekeyvaluemap.h:158
QMap< QString, typename T::ValueType > mValues
Definition: serializablekeyvaluemap.h:191
QString ValueType
Definition: serializablekeyvaluemap.h:210
SerializableKeyValueMap< T > & operator=(const SerializableKeyValueMap< T > &rhs) noexcept
Definition: serializablekeyvaluemap.h:169
The SerializableObject class is the base class for all classes which need to be serializable/deserial...
Definition: serializableobject.h:43
const T::ValueType & getDefaultValue() const noexcept
Definition: serializablekeyvaluemap.h:106
SExpression & appendList(const QString &name, bool linebreak)
Definition: sexpression.cpp:144
Definition: serializablekeyvaluemap.h:221
bool operator!=(const SerializableKeyValueMap< T > &rhs) const noexcept
Definition: serializablekeyvaluemap.h:186
~SerializableKeyValueMap() noexcept
Definition: serializablekeyvaluemap.h:102
The RuntimeError class.
Definition: exceptions.h:216
void notify(Args... args) noexcept
Notify all attached slots.
Definition: signalslot.h:123
ElementName ValueType
Definition: serializablekeyvaluemap.h:199
void insert(const QString &key, const typename T::ValueType &value) noexcept
Definition: serializablekeyvaluemap.h:146
Definition: serializablekeyvaluemap.h:198
tl::optional< typename T::ValueType > tryGet(const QString &key) const noexcept
Definition: serializablekeyvaluemap.h:115
const SExpression & getChild(const QString &path) const
Get a child by path.
Definition: sexpression.cpp:96
The Signal class is used to emit signals on non-QObject derived classes.
Definition: signalslot.h:65
bool operator==(const SerializableKeyValueMap< T > &rhs) const noexcept
Definition: serializablekeyvaluemap.h:183
Signal< SerializableKeyValueMap< T >, const QString &, Event > onEdited
Definition: serializablekeyvaluemap.h:65
const T::ValueType & value(const QStringList &keyOrder, QString *usedKey=nullptr) const noexcept
Definition: serializablekeyvaluemap.h:124
Definition: serializablekeyvaluemap.h:209
void setDefaultValue(const typename T::ValueType &value) noexcept
Definition: serializablekeyvaluemap.h:142
SerializableKeyValueMap(const SerializableKeyValueMap< T > &other) noexcept
Definition: serializablekeyvaluemap.h:70
const QString & getValue() const
Definition: sexpression.cpp:77
The Slot class is used to receive signals from non-QObject derived classes.
Definition: signalslot.h:36
The SExpression class.
Definition: sexpression.h:72
SerializableKeyValueMap(const typename T::ValueType &defaultValue) noexcept
Definition: serializablekeyvaluemap.h:72
The SerializableKeyValueMap class provides an easy way to serialize and deserialize ordered key value...
Definition: serializablekeyvaluemap.h:55
type_safe::constrained_type< QString, ElementNameConstraint, ElementNameVerifier > ElementName
Definition: elementname.h:92