Line data Source code
1 : /*!
2 : * \file esys/repo/manifest/xmlfileimpl_manifest.cpp
3 : * \brief
4 : *
5 : * \cond
6 : * __legal_b__
7 : *
8 : * Copyright (c) 2020 Michel Gillet
9 : * Distributed under the MIT License.
10 : * (See accompanying file LICENSE.txt or
11 : * copy at https://opensource.org/licenses/MIT)
12 : *
13 : * __legal_e__
14 : * \endcond
15 : *
16 : */
17 :
18 : #include "esys/repo/esysrepo_prec.h"
19 : #include "esys/repo/manifest/xmlfileimpl.h"
20 : #include "esys/repo/manifest/xmlfile.h"
21 : #include "esys/repo/manifest/syncs.h"
22 :
23 : #include <esysfile/xml/writer.h>
24 : #include <esysfile/xml/file.h>
25 : #include <esysfile/xml/attr.h>
26 :
27 : #include <string>
28 :
29 : namespace esys::repo::manifest
30 : {
31 :
32 34 : XMLFileImpl::XMLFileImpl(XMLFile *self)
33 34 : : m_self(self)
34 : {
35 34 : }
36 :
37 68 : XMLFileImpl::~XMLFileImpl() = default;
38 :
39 30 : Result XMLFileImpl::read(const std::string &path)
40 : {
41 30 : esysfile::xml::File xml_file;
42 :
43 30 : xml_file.set_root_node_name("esysrepo_manifest");
44 30 : int result = xml_file.read(path);
45 30 : if (result < 0)
46 : {
47 0 : self()->add_error(result, "XML parsing failed.");
48 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_ERROR_PARSING_FILE, path);
49 : }
50 :
51 60 : if (self()->get_data() == nullptr) self()->set_data(std::make_shared<Manifest>());
52 :
53 30 : self()->get_data()->set_type(Type::ESYSREPO_MANIFEST);
54 :
55 60 : return read(xml_file.get_data());
56 30 : }
57 :
58 30 : Result XMLFileImpl::read(std::shared_ptr<esysfile::xml::Data> data)
59 : {
60 30 : if (data->get_attrs().size() != 0)
61 : {
62 14 : Result result = read_root_attributes(data);
63 7 : if (result.error())
64 : {
65 1 : self()->add_error(result.get_result_code_int(), "read_root_attributes failed.");
66 2 : return ESYSREPO_RESULT(result);
67 : }
68 7 : }
69 : else
70 46 : self()->get_data()->set_kind(manifest::Kind::ISOLATED);
71 :
72 29 : Result result;
73 :
74 119 : for (auto el : data->get_elements())
75 : {
76 90 : if (el->get_name() == "location")
77 162 : result = read_location(el);
78 36 : else if ((el->get_name() == "default"))
79 87 : result = read_default(el);
80 7 : else if ((el->get_name() == "group"))
81 6 : result = read_group(el);
82 5 : else if (el->get_name() == "revision-set")
83 12 : result = read_revision_set(el);
84 : else
85 1 : result = unknown_element(el->get_name());
86 90 : if (result.error())
87 : {
88 0 : self()->add_error(result.get_result_code_int(), "Reading XML element failed : " + el->get_name());
89 0 : return result;
90 : }
91 90 : }
92 29 : return ESYSREPO_RESULT(ResultCode::OK);
93 29 : }
94 :
95 2 : Result XMLFileImpl::unknown_element(const std::string &name)
96 : {
97 2 : if (self()->get_parse_mode() == ParseMode::Tolerant) return ESYSREPO_RESULT(ResultCode::OK);
98 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_ELEMENT_UNKNOWN, name);
99 : }
100 :
101 3 : Result XMLFileImpl::unknown_attribute(const std::string &context, const std::string &attr_name)
102 : {
103 3 : if (self()->get_parse_mode() == ParseMode::Tolerant) return ESYSREPO_RESULT(ResultCode::OK);
104 1 : return ESYSREPO_RESULT(ResultCode::MANIFEST_ATTRIBUTE_UNKNOWN, context + ": " + attr_name);
105 : }
106 :
107 7 : Result XMLFileImpl::read_root_attributes(std::shared_ptr<esysfile::xml::Element> root_el)
108 : {
109 16 : for (auto attr : root_el->get_attrs())
110 : {
111 20 : Result result = read_root_attribute(attr);
112 11 : if (result.error()) return ESYSREPO_RESULT(result);
113 20 : }
114 6 : return ESYSREPO_RESULT(ResultCode::OK);
115 : }
116 :
117 10 : Result XMLFileImpl::read_root_attribute(std::shared_ptr<esysfile::xml::Attr> attr)
118 : {
119 10 : if (attr->get_name() == "kind")
120 : {
121 7 : manifest::Kind kind;
122 7 : Result result = convert(attr->get_value(), kind);
123 7 : if (result.error()) return ESYSREPO_RESULT(result, ResultCode::MANIFEST_KIND_UNKNOWN, attr->get_value());
124 :
125 7 : self()->get_data()->set_kind(kind);
126 7 : return ESYSREPO_RESULT(ResultCode::OK);
127 7 : }
128 3 : if (attr->get_name() == "schema-version")
129 : {
130 1 : const std::string &ver = attr->get_value();
131 2 : if (ver != "1.0" && ver != "1.1")
132 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE, ver);
133 1 : return ESYSREPO_RESULT(ResultCode::OK);
134 : }
135 4 : return unknown_attribute("esysrepo_manifest", attr->get_name());
136 : }
137 :
138 54 : Result XMLFileImpl::read_location(std::shared_ptr<esysfile::xml::Element> el)
139 : {
140 54 : if (el->get_name() != "location") return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
141 :
142 54 : std::shared_ptr<Location> location = std::make_shared<Location>();
143 :
144 54 : auto attr = el->get_attr("name");
145 54 : if (attr != nullptr)
146 54 : location->set_name(attr->get_value());
147 : else
148 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_LOCATION_WITHOUT_NAME);
149 :
150 108 : attr = el->get_attr("addr");
151 54 : if (attr != nullptr)
152 54 : location->set_address(attr->get_value());
153 : else
154 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_LOCATION_WITHOUT_ADDRESS);
155 :
156 108 : attr = el->get_attr("alt_addr");
157 54 : if (attr != nullptr) location->set_alt_address(attr->get_value());
158 :
159 54 : Result result;
160 :
161 342 : for (auto child_el : el->get_elements())
162 : {
163 288 : if (child_el->get_name() == "repository")
164 1435 : result = read_repository(child_el, location);
165 : else
166 1 : result = unknown_element(child_el->get_name());
167 288 : if (result.error()) return result;
168 288 : }
169 :
170 162 : self()->get_data()->add_location(location);
171 54 : return ESYSREPO_RESULT(ResultCode::OK);
172 162 : }
173 :
174 287 : Result XMLFileImpl::read_repository(std::shared_ptr<esysfile::xml::Element> el, std::shared_ptr<Location> location)
175 : {
176 287 : if (el->get_name() != "repository") return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
177 :
178 287 : auto repository = std::make_shared<Repository>();
179 287 : repository->set_location(location.get());
180 :
181 287 : auto attr = el->get_attr("name");
182 287 : if (attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_NAME);
183 287 : repository->set_name(attr->get_value());
184 :
185 574 : attr = el->get_attr("path");
186 287 : if (attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_PATH);
187 287 : repository->set_path(attr->get_value());
188 :
189 574 : attr = el->get_attr("revision");
190 287 : if (attr != nullptr) repository->set_revision(attr->get_value());
191 :
192 574 : attr = el->get_attr("sync-s");
193 287 : if (attr != nullptr)
194 : {
195 1 : bool sync_s = false;
196 1 : if (!parse_sync_s_value(attr->get_value(), sync_s))
197 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE, "repository.sync-s");
198 1 : repository->set_sync_s(sync_s);
199 : }
200 :
201 574 : location->add_repo(repository);
202 :
203 287 : return ESYSREPO_RESULT(ResultCode::OK);
204 574 : }
205 :
206 29 : Result XMLFileImpl::read_default(std::shared_ptr<esysfile::xml::Element> el)
207 : {
208 29 : if (el->get_name() != "default") return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
209 :
210 90 : for (auto attr : el->get_attrs())
211 : {
212 122 : Result result = read_default_attr(attr);
213 61 : if (result.error()) return result;
214 122 : }
215 29 : return ESYSREPO_RESULT(ResultCode::OK);
216 : }
217 :
218 61 : Result XMLFileImpl::read_default_attr(std::shared_ptr<esysfile::xml::Attr> attr)
219 : {
220 61 : if (attr->get_name() == "revision")
221 58 : self()->get_data()->set_default_revision(attr->get_value());
222 32 : else if (attr->get_name() == "sync-j")
223 56 : self()->get_data()->set_default_job_count(atoi(attr->get_value().c_str()));
224 4 : else if (attr->get_name() == "revision-set")
225 4 : self()->get_data()->set_default_revision_set(attr->get_value());
226 2 : else if (attr->get_name() == "sync-s")
227 : {
228 1 : bool sync_s = false;
229 1 : if (!parse_sync_s_value(attr->get_value(), sync_s))
230 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE, "default.sync-s");
231 2 : self()->get_data()->set_default_sync_s(sync_s);
232 : }
233 : else
234 2 : return unknown_attribute("default", attr->get_name());
235 60 : return ESYSREPO_RESULT(ResultCode::OK);
236 : }
237 :
238 2 : Result XMLFileImpl::read_group(std::shared_ptr<esysfile::xml::Element> el)
239 : {
240 2 : auto attr = el->get_attr("name");
241 2 : if (attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_GROUP_WITHOUT_NAME);
242 :
243 2 : std::shared_ptr<Group> group = std::make_shared<Group>();
244 2 : group->set_name(attr->get_value());
245 :
246 6 : for (auto child : el->get_elements())
247 : {
248 16 : Result result = read_group_repo(child, group);
249 4 : if (result.error()) return ESYSREPO_RESULT(result);
250 8 : }
251 :
252 6 : self()->get_data()->get_groups().add_group(group);
253 :
254 2 : return ESYSREPO_RESULT(ResultCode::OK);
255 4 : }
256 :
257 4 : Result XMLFileImpl::read_group_repo(std::shared_ptr<esysfile::xml::Element> el, std::shared_ptr<Group> group)
258 : {
259 4 : if (el->get_name() != "repo") return unknown_element(el->get_name());
260 :
261 4 : auto attr = el->get_attr("path");
262 4 : if (attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_GROUP_REPO_WITHOUT_PATH);
263 :
264 4 : auto repo = self()->get_data()->find_repo_by_path(attr->get_value());
265 4 : if (repo == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPO_UNKNOWN, "path = " + attr->get_value());
266 :
267 8 : group->add_repo(repo);
268 4 : return ESYSREPO_RESULT(ResultCode::OK);
269 8 : }
270 :
271 4 : Result XMLFileImpl::read_revision_set(std::shared_ptr<esysfile::xml::Element> el)
272 : {
273 4 : if (el->get_name() != "revision-set") return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
274 :
275 4 : auto attr = el->get_attr("name");
276 4 : if (attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_ATTRIBUTE_UNKNOWN, "revision-set missing name");
277 :
278 4 : auto revision_set = std::make_shared<RevisionSet>(attr->get_value());
279 10 : for (auto child : el->get_elements())
280 : {
281 24 : Result result = read_revision_set_child(child, revision_set);
282 6 : if (result.error()) return ESYSREPO_RESULT(result);
283 12 : }
284 :
285 12 : self()->get_data()->get_revision_sets().add_revision_set(revision_set);
286 4 : return ESYSREPO_RESULT(ResultCode::OK);
287 8 : }
288 :
289 6 : Result XMLFileImpl::read_revision_set_child(std::shared_ptr<esysfile::xml::Element> el,
290 : std::shared_ptr<RevisionSet> revision_set)
291 : {
292 6 : if (el->get_name() == "default")
293 : {
294 4 : auto attr = el->get_attr("revision");
295 4 : if (attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_ATTRIBUTE_UNKNOWN, "default missing revision");
296 4 : revision_set->set_default_revision(attr->get_value());
297 4 : return ESYSREPO_RESULT(ResultCode::OK);
298 4 : }
299 2 : if (el->get_name() == "repository")
300 : {
301 2 : auto path_attr = el->get_attr("path");
302 2 : auto rev_attr = el->get_attr("revision");
303 2 : if (path_attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_PATH);
304 2 : if (rev_attr == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_ATTRIBUTE_UNKNOWN, "repository missing revision");
305 2 : revision_set->set_repo_revision(path_attr->get_value(), rev_attr->get_value());
306 2 : return ESYSREPO_RESULT(ResultCode::OK);
307 4 : }
308 0 : return unknown_element(el->get_name());
309 : }
310 :
311 5 : Result XMLFileImpl::write_xml()
312 : {
313 10 : if (self()->get_data() == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_XML_DATA_NULLPTR);
314 :
315 5 : m_xml_data = std::make_shared<esysfile::xml::Data>();
316 5 : m_xml_data->set_root_node_name("esysrepo_manifest");
317 :
318 10 : if (self()->get_data()->get_kind() != manifest::Kind::ISOLATED)
319 : {
320 0 : std::string kind;
321 0 : Result result = convert(self()->get_data()->get_kind(), kind);
322 0 : if (result.error()) return ESYSREPO_RESULT(result);
323 0 : m_xml_data->add_attr("kind", kind);
324 0 : }
325 :
326 5 : {
327 10 : Result result = write_default(m_xml_data);
328 5 : if (result.error()) return ESYSREPO_RESULT(result);
329 5 : }
330 :
331 19 : for (auto location : self()->get_data()->get_locations())
332 : {
333 36 : Result result = write(m_xml_data, location);
334 9 : if (result.error()) return ESYSREPO_RESULT(result);
335 18 : }
336 :
337 12 : for (auto group : self()->get_data()->get_groups().get_groups())
338 : {
339 8 : Result result = write(m_xml_data, group);
340 2 : if (result.error()) return ESYSREPO_RESULT(result);
341 4 : }
342 :
343 12 : for (auto revision_set : self()->get_data()->get_revision_sets().get_revision_sets())
344 : {
345 8 : Result result = write(m_xml_data, revision_set);
346 2 : if (result.error()) return ESYSREPO_RESULT(result);
347 4 : }
348 5 : return ESYSREPO_RESULT(ResultCode::OK);
349 : }
350 :
351 5 : Result XMLFileImpl::write_default(std::shared_ptr<esysfile::xml::Element> parent_el)
352 : {
353 5 : auto data = self()->get_data();
354 : // Constructor default revision is "master"; omit <default> unless something is non-default
355 : // so existing 1.0 write round-trips stay unchanged.
356 5 : const bool has_jobs = data->get_default_job_count() != 1;
357 5 : const bool has_rev_set = !data->get_default_revision_set().empty();
358 5 : const bool has_nondefault_rev = !data->get_default_revision().empty() && data->get_default_revision() != "master";
359 5 : const bool has_sync_s = data->get_default_sync_s().has_value();
360 5 : if (!has_jobs && !has_rev_set && !has_nondefault_rev && !has_sync_s) return ESYSREPO_RESULT(ResultCode::OK);
361 :
362 4 : auto default_el = std::make_shared<esysfile::xml::Element>();
363 4 : default_el->set_name("default");
364 8 : if (!data->get_default_revision().empty()) default_el->add_attr("revision", data->get_default_revision());
365 7 : if (has_jobs) default_el->add_attr("sync-j", std::to_string(data->get_default_job_count()));
366 5 : if (has_rev_set) default_el->add_attr("revision-set", data->get_default_revision_set());
367 5 : if (has_sync_s) default_el->add_attr("sync-s", *data->get_default_sync_s() ? "true" : "false");
368 8 : parent_el->add_element(default_el);
369 4 : return ESYSREPO_RESULT(ResultCode::OK);
370 10 : }
371 :
372 3 : Result XMLFileImpl::write(const std::string &path)
373 : {
374 3 : Result result = write_xml();
375 3 : if (result.error()) return ESYSREPO_RESULT(result);
376 :
377 3 : esysfile::xml::Writer writer;
378 :
379 3 : writer.set_indent(4);
380 6 : int result_int = writer.write(path, m_xml_data);
381 3 : if (result_int < 0) return ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result_int);
382 3 : return ESYSREPO_RESULT(ResultCode::OK);
383 3 : }
384 :
385 2 : Result XMLFileImpl::write(std::ostream &os)
386 : {
387 2 : Result result = write_xml();
388 2 : if (result.error()) return ESYSREPO_RESULT(result);
389 :
390 2 : esysfile::xml::Writer writer;
391 :
392 2 : writer.set_indent(4);
393 4 : int result_int = writer.write(os, m_xml_data);
394 2 : if (result_int < 0) return ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result_int);
395 2 : return ESYSREPO_RESULT(ResultCode::OK);
396 2 : }
397 :
398 9 : Result XMLFileImpl::write(std::shared_ptr<esysfile::xml::Element> parent_el, std::shared_ptr<Location> location)
399 : {
400 9 : std::shared_ptr<esysfile::xml::Element> location_el = std::make_shared<esysfile::xml::Element>();
401 :
402 9 : location_el->set_name("location");
403 9 : location_el->add_attr("name", location->get_name());
404 9 : location_el->add_attr("addr", location->get_address());
405 :
406 83 : for (auto repo : location->get_repos())
407 : {
408 296 : Result result = write(location_el, repo);
409 74 : if (result.error()) return ESYSREPO_RESULT(result);
410 148 : }
411 :
412 18 : parent_el->add_element(location_el);
413 9 : return ESYSREPO_RESULT(ResultCode::OK);
414 9 : }
415 :
416 74 : Result XMLFileImpl::write(std::shared_ptr<esysfile::xml::Element> parent_el, std::shared_ptr<Repository> repository)
417 : {
418 74 : std::shared_ptr<esysfile::xml::Element> repo_el = std::make_shared<esysfile::xml::Element>();
419 :
420 74 : repo_el->set_name("repository");
421 74 : repo_el->add_attr("path", repository->get_path());
422 74 : repo_el->add_attr("name", repository->get_name());
423 94 : if (!repository->get_revision().empty()) repo_el->add_attr("revision", repository->get_revision());
424 74 : if (repository->get_sync_s().has_value())
425 3 : repo_el->add_attr("sync-s", *repository->get_sync_s() ? "true" : "false");
426 :
427 148 : parent_el->add_element(repo_el);
428 74 : return ESYSREPO_RESULT(ResultCode::OK);
429 74 : }
430 :
431 2 : Result XMLFileImpl::write(std::shared_ptr<esysfile::xml::Element> parent_el, std::shared_ptr<Group> group)
432 : {
433 2 : std::shared_ptr<esysfile::xml::Element> grp_el = std::make_shared<esysfile::xml::Element>();
434 :
435 2 : grp_el->set_name("group");
436 2 : grp_el->add_attr("name", group->get_name());
437 :
438 8 : for (auto repo : group->get_repos())
439 : {
440 6 : std::shared_ptr<esysfile::xml::Element> repo_el = std::make_shared<esysfile::xml::Element>();
441 :
442 6 : repo_el->set_name("repo");
443 6 : repo_el->add_attr("path", repo->get_path());
444 18 : grp_el->add_element(repo_el);
445 12 : }
446 :
447 4 : parent_el->add_element(grp_el);
448 2 : return ESYSREPO_RESULT(ResultCode::OK);
449 2 : }
450 :
451 2 : Result XMLFileImpl::write(std::shared_ptr<esysfile::xml::Element> parent_el, std::shared_ptr<RevisionSet> revision_set)
452 : {
453 2 : auto set_el = std::make_shared<esysfile::xml::Element>();
454 2 : set_el->set_name("revision-set");
455 2 : set_el->add_attr("name", revision_set->get_name());
456 :
457 2 : if (!revision_set->get_default_revision().empty())
458 : {
459 2 : auto default_el = std::make_shared<esysfile::xml::Element>();
460 2 : default_el->set_name("default");
461 2 : default_el->add_attr("revision", revision_set->get_default_revision());
462 6 : set_el->add_element(default_el);
463 2 : }
464 :
465 3 : for (const auto &kv : revision_set->get_repo_revisions())
466 : {
467 1 : auto repo_el = std::make_shared<esysfile::xml::Element>();
468 1 : repo_el->set_name("repository");
469 1 : repo_el->add_attr("path", kv.first);
470 1 : repo_el->add_attr("revision", kv.second);
471 3 : set_el->add_element(repo_el);
472 1 : }
473 :
474 4 : parent_el->add_element(set_el);
475 2 : return ESYSREPO_RESULT(ResultCode::OK);
476 2 : }
477 :
478 256 : XMLFile *XMLFileImpl::self()
479 : {
480 256 : return m_self;
481 : }
482 :
483 0 : const XMLFile *XMLFileImpl::self() const
484 : {
485 0 : return m_self;
486 : }
487 :
488 : } // namespace esys::repo::manifest
|