90 SGPropertyNode addonRoot;
93 if (!metadataFile.exists()) {
95 "unable to find add-on metadata file '" + metadataFile.utf8Str() +
"'");
99 readProperties(metadataFile, &addonRoot);
100 }
catch (
const sg_exception &e) {
102 "unable to load add-on metadata file '" + metadataFile.utf8Str() +
"': " +
103 e.getFormattedMessage());
107 SGPropertyNode *metaNode = addonRoot.getChild(
"meta");
108 if (metaNode ==
nullptr) {
110 "no /meta node found in add-on metadata file '" +
111 metadataFile.utf8Str() +
"'");
115 SGPropertyNode *fileTypeNode = metaNode->getChild(
"file-type");
116 if (fileTypeNode ==
nullptr) {
118 "no /meta/file-type node found in add-on metadata file '" +
119 metadataFile.utf8Str() +
"'");
122 std::string fileType = fileTypeNode->getStringValue();
123 if (fileType !=
"FlightGear add-on metadata") {
125 "Invalid /meta/file-type value for add-on metadata file '" +
126 metadataFile.utf8Str() +
"': '" + fileType +
"' "
127 "(expected 'FlightGear add-on metadata')");
131 SGPropertyNode *fmtVersionNode = metaNode->getChild(
"format-version");
132 if (fmtVersionNode ==
nullptr) {
134 "no /meta/format-version node found in add-on metadata file '" +
135 metadataFile.utf8Str() +
"'");
138 int formatVersion = fmtVersionNode->getIntValue();
139 if (formatVersion != 1) {
141 "unknown format version in add-on metadata file '" +
142 metadataFile.utf8Str() +
"': " + std::to_string(formatVersion));
146 SGPropertyNode *addonNode = addonRoot.getChild(
"addon");
147 if (addonNode ==
nullptr) {
149 "no /addon node found in add-on metadata file '" +
150 metadataFile.utf8Str() +
"'");
154 SGPropertyNode* langStringsNode =
globals->get_locale()->selectLanguageNode(localizedNode);
156 SGPropertyNode *idNode = addonNode->getChild(
"identifier");
157 if (idNode ==
nullptr) {
159 "no /addon/identifier node found in add-on metadata file '" +
160 metadataFile.utf8Str() +
"'");
162 metadata.
id = strutils::strip(idNode->getStringValue());
165 if (metadata.
id.empty()) {
167 "empty or whitespace-only value for the /addon/identifier node in "
168 "add-on metadata file '" + metadataFile.utf8Str() +
"'");
169 }
else if (metadata.
id.find(
'.') == std::string::npos) {
170 SG_LOG(SG_GENERAL, SG_WARN,
171 "Add-on identifier '" << metadata.
id <<
"' does not use reverse DNS "
172 "style (e.g., org.flightgear.addons.MyAddon) in add-on metadata "
173 "file '" << metadataFile.utf8Str() +
"'");
176 SGPropertyNode *nameNode = addonNode->getChild(
"name");
177 if (nameNode ==
nullptr) {
179 "no /addon/name node found in add-on metadata file '" +
180 metadataFile.utf8Str() +
"'");
186 if (metadata.
name.empty()) {
188 "empty or whitespace-only value for the /addon/name node in add-on "
189 "metadata file '" + metadataFile.utf8Str() +
"'");
192 SGPropertyNode *versionNode = addonNode->getChild(
"version");
193 if (versionNode ==
nullptr) {
195 "no /addon/version node found in add-on metadata file '" +
196 metadataFile.utf8Str() +
"'");
199 strutils::strip(versionNode->getStringValue())};
201 metadata.
authors = parseContactsNode<Author>(metadataFile,
202 addonNode->getChild(
"authors"));
203 metadata.
maintainers = parseContactsNode<Maintainer>(
204 metadataFile, addonNode->getChild(
"maintainers"));
210 metadata.
licenseUrl) = parseLicenseNode(addonPath, addonNode);
212 SGPropertyNode *tagsNode = addonNode->getChild(
"tags");
213 if (tagsNode !=
nullptr) {
214 auto tagNodes = tagsNode->getChildren(
"tag");
215 for (
const auto& node: tagNodes) {
216 metadata.
tags.push_back(strutils::strip(node->getStringValue()));
220 SGPropertyNode *minNode = addonNode->getChild(
"min-FG-version");
221 if (minNode !=
nullptr) {
227 SGPropertyNode *maxNode = addonNode->getChild(
"max-FG-version");
228 if (maxNode !=
nullptr) {
236 SGPropertyNode *urlsNode = addonNode->getChild(
"urls");
237 if (urlsNode !=
nullptr) {
238 SGPropertyNode *homePageNode = urlsNode->getChild(
"home-page");
239 if (homePageNode !=
nullptr) {
240 metadata.
homePage = strutils::strip(homePageNode->getStringValue());
243 SGPropertyNode *downloadUrlNode = urlsNode->getChild(
"download");
244 if (downloadUrlNode !=
nullptr) {
245 metadata.
downloadUrl = strutils::strip(downloadUrlNode->getStringValue());
248 SGPropertyNode *supportUrlNode = urlsNode->getChild(
"support");
249 if (supportUrlNode !=
nullptr) {
250 metadata.
supportUrl = strutils::strip(supportUrlNode->getStringValue());
253 SGPropertyNode *codeRepoUrlNode = urlsNode->getChild(
"code-repository");
254 if (codeRepoUrlNode !=
nullptr) {
256 strutils::strip(codeRepoUrlNode->getStringValue());
260 SG_LOG(SG_GENERAL, SG_DEBUG,
261 "Parsed add-on metadata file: '" << metadataFile.utf8Str() +
"'");
295Addon::MetadataParser::parseContactsNode(
const SGPath& metadataFile,
296 SGPropertyNode* mainNode)
299 vector<typename contactTraits::strong_ref> res;
301 if (mainNode !=
nullptr) {
302 auto contactNodes = mainNode->getChildren(contactTraits::xmlNodeName());
303 res.reserve(contactNodes.size());
305 for (
const auto& contactNode: contactNodes) {
306 std::string
name, email, url;
316 res.push_back(ptr_traits::makeStrongRef(
name, email, url));