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 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "librarybaseelement.h"
24
25 #include "librarybaseelementcheck.h"
26
27 #include <librepcb/common/application.h>
28 #include <librepcb/common/fileio/sexpression.h>
29 #include <librepcb/common/fileio/versionfile.h>
30
31 #include <QtCore>
32
33 /*******************************************************************************
34 * Namespace
35 ******************************************************************************/
36 namespace librepcb {
37 namespace library {
38
39 /*******************************************************************************
40 * Constructors / Destructor
41 ******************************************************************************/
42
LibraryBaseElement(bool dirnameMustBeUuid,const QString & shortElementName,const QString & longElementName,const Uuid & uuid,const Version & version,const QString & author,const ElementName & name_en_US,const QString & description_en_US,const QString & keywords_en_US)43 LibraryBaseElement::LibraryBaseElement(
44 bool dirnameMustBeUuid, const QString& shortElementName,
45 const QString& longElementName, const Uuid& uuid, const Version& version,
46 const QString& author, const ElementName& name_en_US,
47 const QString& description_en_US, const QString& keywords_en_US)
48 : QObject(nullptr),
49 mDirectory(new TransactionalDirectory()),
50 mDirectoryNameMustBeUuid(dirnameMustBeUuid),
51 mShortElementName(shortElementName),
52 mLongElementName(longElementName),
53 mLoadingFileDocument(),
54 mLoadingFileFormat(qApp->getFileFormatVersion()),
55 mUuid(uuid),
56 mVersion(version),
57 mAuthor(author),
58 mCreated(QDateTime::currentDateTime()),
59 mIsDeprecated(false),
60 mNames(name_en_US),
61 mDescriptions(description_en_US),
62 mKeywords(keywords_en_US) {
63 }
64
LibraryBaseElement(std::unique_ptr<TransactionalDirectory> directory,bool dirnameMustBeUuid,const QString & shortElementName,const QString & longElementName)65 LibraryBaseElement::LibraryBaseElement(
66 std::unique_ptr<TransactionalDirectory> directory, bool dirnameMustBeUuid,
67 const QString& shortElementName, const QString& longElementName)
68 : QObject(nullptr),
69 mDirectory(std::move(directory)),
70 mDirectoryNameMustBeUuid(dirnameMustBeUuid),
71 mShortElementName(shortElementName),
72 mLongElementName(longElementName),
73 mLoadingFileDocument(),
74 mLoadingFileFormat(qApp->getFileFormatVersion()),
75 mUuid(Uuid::createRandom()), // just for initialization, will be
76 // overwritten
77 mVersion(Version::fromString(
78 "0.1")), // just for initialization, will be overwritten
79 mNames(ElementName(
80 "unknown")), // just for initialization, will be overwritten
81 mDescriptions(""),
82 mKeywords("") {
83 // determine the filename of the version file
84 QString versionFileName = ".librepcb-" % mShortElementName;
85
86 // check if the directory is a library element
87 if (!mDirectory->fileExists(versionFileName)) {
88 throw RuntimeError(
89 __FILE__, __LINE__,
90 tr("Directory is not a library element of type %1: \"%2\"")
91 .arg(mLongElementName, mDirectory->getAbsPath().toNative()));
92 }
93
94 // check directory name
95 QString dirUuidStr = mDirectory->getAbsPath().getFilename();
96 if (mDirectoryNameMustBeUuid && (!Uuid::isValid(dirUuidStr))) {
97 throw RuntimeError(__FILE__, __LINE__,
98 tr("Directory name is not a valid UUID: \"%1\"")
99 .arg(mDirectory->getAbsPath().toNative()));
100 }
101
102 // read version number from version file
103 VersionFile versionFile =
104 VersionFile::fromByteArray(mDirectory->read(versionFileName));
105 mLoadingFileFormat = versionFile.getVersion();
106 if (mLoadingFileFormat > qApp->getAppVersion()) {
107 throw RuntimeError(
108 __FILE__, __LINE__,
109 QString(
110 tr("The library element %1 was created with a newer application "
111 "version. You need at least LibrePCB version %2 to open it."))
112 .arg(mDirectory->getAbsPath().toNative())
113 .arg(mLoadingFileFormat.toPrettyStr(3)));
114 }
115
116 // open main file
117 QString sexprFileName = mLongElementName % ".lp";
118 FilePath sexprFilePath = mDirectory->getAbsPath(sexprFileName);
119 mLoadingFileDocument =
120 SExpression::parse(mDirectory->read(sexprFileName), sexprFilePath);
121
122 // read attributes
123 mUuid = deserialize<Uuid>(mLoadingFileDocument.getChild("@0"),
124 mLoadingFileFormat);
125 mVersion = deserialize<Version>(mLoadingFileDocument.getChild("version/@0"),
126 mLoadingFileFormat);
127 mAuthor = mLoadingFileDocument.getChild("author/@0").getValue();
128 mCreated = deserialize<QDateTime>(mLoadingFileDocument.getChild("created/@0"),
129 mLoadingFileFormat);
130 mIsDeprecated = deserialize<bool>(
131 mLoadingFileDocument.getChild("deprecated/@0"), mLoadingFileFormat);
132
133 // read names, descriptions and keywords in all available languages
134 mNames = LocalizedNameMap(mLoadingFileDocument, mLoadingFileFormat);
135 mDescriptions =
136 LocalizedDescriptionMap(mLoadingFileDocument, mLoadingFileFormat);
137 mKeywords = LocalizedKeywordsMap(mLoadingFileDocument, mLoadingFileFormat);
138
139 // check if the UUID equals to the directory basename
140 if (mDirectoryNameMustBeUuid && (mUuid.toStr() != dirUuidStr)) {
141 qDebug() << mUuid.toStr() << "!=" << dirUuidStr;
142 throw RuntimeError(
143 __FILE__, __LINE__,
144 QString(
145 tr("UUID mismatch between element directory and main file: \"%1\""))
146 .arg(sexprFilePath.toNative()));
147 }
148 }
149
~LibraryBaseElement()150 LibraryBaseElement::~LibraryBaseElement() noexcept {
151 }
152
153 /*******************************************************************************
154 * Getters
155 ******************************************************************************/
156
getAllAvailableLocales() const157 QStringList LibraryBaseElement::getAllAvailableLocales() const noexcept {
158 QStringList list;
159 list.append(mNames.keys());
160 list.append(mDescriptions.keys());
161 list.append(mKeywords.keys());
162 list.removeDuplicates();
163 list.sort(Qt::CaseSensitive);
164 return list;
165 }
166
167 /*******************************************************************************
168 * General Methods
169 ******************************************************************************/
170
runChecks() const171 LibraryElementCheckMessageList LibraryBaseElement::runChecks() const {
172 LibraryBaseElementCheck check(*this);
173 return check.runChecks(); // can throw
174 }
175
save()176 void LibraryBaseElement::save() {
177 // save S-Expressions file
178 mDirectory->write(
179 mLongElementName % ".lp",
180 serializeToDomElement("librepcb_" % mLongElementName).toByteArray());
181
182 // save version number file
183 mDirectory->write(".librepcb-" % mShortElementName,
184 VersionFile(qApp->getFileFormatVersion()).toByteArray());
185 }
186
saveTo(TransactionalDirectory & dest)187 void LibraryBaseElement::saveTo(TransactionalDirectory& dest) {
188 mDirectory->saveTo(dest); // can throw
189 save(); // can throw
190 }
191
moveTo(TransactionalDirectory & dest)192 void LibraryBaseElement::moveTo(TransactionalDirectory& dest) {
193 mDirectory->moveTo(dest); // can throw
194 save(); // can throw
195 }
196
saveIntoParentDirectory(TransactionalDirectory & dest)197 void LibraryBaseElement::saveIntoParentDirectory(TransactionalDirectory& dest) {
198 TransactionalDirectory dir(dest, mUuid.toStr());
199 saveTo(dir); // can throw
200 }
201
moveIntoParentDirectory(TransactionalDirectory & dest)202 void LibraryBaseElement::moveIntoParentDirectory(TransactionalDirectory& dest) {
203 TransactionalDirectory dir(dest, mUuid.toStr());
204 moveTo(dir); // can throw
205 }
206
207 /*******************************************************************************
208 * Protected Methods
209 ******************************************************************************/
210
cleanupAfterLoadingElementFromFile()211 void LibraryBaseElement::cleanupAfterLoadingElementFromFile() noexcept {
212 mLoadingFileDocument = SExpression(); // destroy the whole DOM tree
213 }
214
serialize(SExpression & root) const215 void LibraryBaseElement::serialize(SExpression& root) const {
216 root.appendChild(mUuid);
217 mNames.serialize(root);
218 mDescriptions.serialize(root);
219 mKeywords.serialize(root);
220 root.appendChild("author", mAuthor, true);
221 root.appendChild("version", mVersion, true);
222 root.appendChild("created", mCreated, true);
223 root.appendChild("deprecated", mIsDeprecated, true);
224 }
225
226 /*******************************************************************************
227 * End of File
228 ******************************************************************************/
229
230 } // namespace library
231 } // namespace librepcb
232