Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
OptionsCont.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2023 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
21// A storage for options (typed value containers)
22/****************************************************************************/
23#include <config.h>
24
25#include <map>
26#include <string>
27#include <exception>
28#include <algorithm>
29#include <vector>
30#include <iostream>
31#include <cstdlib>
32#include <ctime>
33#include <cstring>
34#include <cerrno>
35#include <iterator>
36#include <sstream>
43#include "Option.h"
44#include "OptionsIO.h"
45#include "OptionsCont.h"
46
47
48// ===========================================================================
49// static member definitions
50// ===========================================================================
51
54
55// ===========================================================================
56// method definitions
57// ===========================================================================
58
63
64
66 myCopyrightNotices.push_back(TL("Copyright (C) 2001-2023 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
67}
68
69
73
74
75void
76OptionsCont::doRegister(const std::string& name, Option* o) {
77 // first check that option isn't null
78 if (o == nullptr) {
79 throw ProcessError("Option cannot be null");
80 }
81 // now check that there isn't another addresse (or synonym) related with the option
82 if (myValues.find(name) != myValues.end()) {
83 throw ProcessError(name + " is an already used option name.");
84 }
85 // check if previously was inserted in addresses (to avoid synonyms in addresses)
86 bool isSynonym = false;
87 for (const auto& addresse : myAddresses) {
88 if (addresse.second == o) {
89 isSynonym = true;
90 }
91 }
92 if (!isSynonym) {
93 myAddresses.push_back(std::make_pair(name, o));
94 }
95 // insert in values
96 myValues[name] = o;
97}
98
99
100void
101OptionsCont::doRegister(const std::string& name1, char abbr, Option* o) {
102 doRegister(name1, o);
103 doRegister(convertChar(abbr), o);
104}
105
106
107void
108OptionsCont::addSynonyme(const std::string& name1, const std::string& name2, bool isDeprecated) {
109 auto i1 = myValues.find(name1);
110 auto i2 = myValues.find(name2);
111 if (i1 == myValues.end() && i2 == myValues.end()) {
112 throw ProcessError("Neither the option '" + name1 + "' nor the option '" + name2 + "' is known yet");
113 }
114 if (i1 != myValues.end() && i2 != myValues.end()) {
115 if ((*i1).second == (*i2).second) {
116 return;
117 }
118 throw ProcessError("Both options '" + name1 + "' and '" + name2 + "' do exist and differ.");
119 }
120 if (i1 == myValues.end() && i2 != myValues.end()) {
121 doRegister(name1, (*i2).second);
122 if (isDeprecated) {
123 myDeprecatedSynonymes[name1] = false;
124 }
125 }
126 if (i1 != myValues.end() && i2 == myValues.end()) {
127 doRegister(name2, (*i1).second);
128 if (isDeprecated) {
129 myDeprecatedSynonymes[name2] = false;
130 }
131 }
132}
133
134
135void
136OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
137 myXMLDefaults[xmlRoot] = name;
138}
139
140
141bool
142OptionsCont::exists(const std::string& name) const {
143 return myValues.count(name) > 0;
144}
145
146
147bool
148OptionsCont::isSet(const std::string& name, bool failOnNonExistant) const {
149 auto i = myValues.find(name);
150 if (i == myValues.end()) {
151 if (failOnNonExistant) {
152 throw ProcessError(TLF("Internal request for unknown option '%'!", name));
153 } else {
154 return false;
155 }
156 }
157 return (*i).second->isSet();
158}
159
160
161bool
162OptionsCont::isDefault(const std::string& name) const {
163 auto i = myValues.find(name);
164 if (i == myValues.end()) {
165 return false;
166 }
167 return (*i).second->isDefault();
168}
169
170
171Option*
172OptionsCont::getSecure(const std::string& name) const {
173 const auto& valuesFinder = myValues.find(name);
174 if (valuesFinder == myValues.end()) {
175 throw ProcessError(TLF("No option with the name '%' exists.", name));
176 }
177 const auto& synonymFinder = myDeprecatedSynonymes.find(name);
178 if ((synonymFinder != myDeprecatedSynonymes.end()) && !synonymFinder->second) {
179 std::string defaultName;
180 for (const auto& subtopicEntry : mySubTopicEntries) {
181 for (const auto& value : subtopicEntry.second) {
182 const auto l = myValues.find(value);
183 if ((l != myValues.end()) && (l->second == valuesFinder->second)) {
184 defaultName = value;
185 break;
186 }
187 }
188 if (defaultName != "") {
189 break;
190 }
191 }
192 WRITE_WARNINGF(TL("Please note that '%' is deprecated.\n Use '%' instead."), name, defaultName);
193 synonymFinder->second = true;
194 }
195 return valuesFinder->second;
196}
197
198
199std::string
200OptionsCont::getValueString(const std::string& name) const {
201 Option* o = getSecure(name);
202 return o->getValueString();
203}
204
205
206std::string
207OptionsCont::getString(const std::string& name) const {
208 Option* o = getSecure(name);
209 return o->getString();
210}
211
212
213double
214OptionsCont::getFloat(const std::string& name) const {
215 Option* o = getSecure(name);
216 return o->getFloat();
217}
218
219
220int
221OptionsCont::getInt(const std::string& name) const {
222 Option* o = getSecure(name);
223 return o->getInt();
224}
225
226
227bool
228OptionsCont::getBool(const std::string& name) const {
229 Option* o = getSecure(name);
230 return o->getBool();
231}
232
233
234const IntVector&
235OptionsCont::getIntVector(const std::string& name) const {
236 Option* o = getSecure(name);
237 return o->getIntVector();
238}
239
240const StringVector&
241OptionsCont::getStringVector(const std::string& name) const {
242 Option* o = getSecure(name);
243 return o->getStringVector();
244}
245
246
247bool
248OptionsCont::set(const std::string& name, const std::string& value, const bool append) {
249 Option* o = getSecure(name);
250 if (!o->isWriteable()) {
252 return false;
253 }
254 try {
255 // Substitute environment variables defined by ${NAME} with their value
256 if (!o->set(StringUtils::substituteEnvironment(value, &OptionsIO::getLoadTime()), value, append)) {
257 return false;
258 }
259 } catch (ProcessError& e) {
260 WRITE_ERROR("While processing option '" + name + "':\n " + e.what());
261 return false;
262 }
263 return true;
264}
265
266
267bool
268OptionsCont::setDefault(const std::string& name, const std::string& value) {
269 Option* const o = getSecure(name);
270 if (o->isWriteable() && set(name, value)) {
271 o->resetDefault();
272 return true;
273 }
274 return false;
275}
276
277
278bool
279OptionsCont::setByRootElement(const std::string& root, const std::string& value) {
280 if (myXMLDefaults.count(root) > 0) {
281 return set(myXMLDefaults[root], value);
282 }
283 if (myXMLDefaults.count("") > 0) {
284 return set(myXMLDefaults[""], value);
285 }
286 return false;
287}
288
289
290std::vector<std::string>
291OptionsCont::getSynonymes(const std::string& name) const {
292 Option* o = getSecure(name);
293 std::vector<std::string> synonymes;
294 for (const auto& value : myValues) {
295 if ((value.second == o) && (name != value.first)) {
296 synonymes.push_back(value.first);
297 }
298 }
299 return synonymes;
300}
301
302
303const std::string&
304OptionsCont::getDescription(const std::string& name) const {
305 return getSecure(name)->getDescription();
306}
307
308
309const std::string&
310OptionsCont::getSubTopic(const std::string& name) const {
311 return getSecure(name)->getSubTopic();
312}
313
314
315std::ostream&
316operator<<(std::ostream& os, const OptionsCont& oc) {
317 std::vector<std::string> done;
318 os << "Options set:" << std::endl;
319 for (const auto& value : oc.myValues) {
320 const auto& finder = std::find(done.begin(), done.end(), value.first);
321 if (finder == done.end()) {
322 std::vector<std::string> synonymes = oc.getSynonymes(value.first);
323 if (synonymes.size() != 0) {
324 os << value.first << " (";
325 for (auto synonym = synonymes.begin(); synonym != synonymes.end(); synonym++) {
326 if (synonym != synonymes.begin()) {
327 os << ", ";
328 }
329 os << (*synonym);
330 }
331 os << ")";
332 } else {
333 os << value.first;
334 }
335 if (value.second->isSet()) {
336 os << ": " << value.second->getValueString() << std::endl;
337 } else {
338 os << ": <INVALID>" << std::endl;
339 }
340 done.push_back(value.first);
341 copy(synonymes.begin(), synonymes.end(), back_inserter(done));
342 }
343 }
344 return os;
345}
346
347
348void
349OptionsCont::relocateFiles(const std::string& configuration) const {
350 for (const auto& addresse : myAddresses) {
351 if (addresse.second->isFileName() && addresse.second->isSet()) {
352 StringVector fileList = StringVector(addresse.second->getStringVector());
353 for (auto& file : fileList) {
354 file = FileHelpers::checkForRelativity(file, configuration);
355 try {
356 file = StringUtils::urlDecode(file);
357 } catch (NumberFormatException& e) {
358 WRITE_WARNING(toString(e.what()) + " when trying to decode filename '" + file + "'.");
359 }
360 }
361 StringVector rawList = StringTokenizer(addresse.second->getValueString(), ",").getVector();
362 for (auto& file : rawList) {
363 file = FileHelpers::checkForRelativity(file, configuration);
364 }
365 const std::string conv = joinToString(fileList, ',');
366 if (conv != joinToString(addresse.second->getStringVector(), ',')) {
367 const bool hadDefault = addresse.second->isDefault();
368 addresse.second->set(conv, joinToString(rawList, ','), false);
369 if (hadDefault) {
370 addresse.second->resetDefault();
371 }
372 }
373 }
374 }
375}
376
377
378bool
379OptionsCont::isUsableFileList(const std::string& name) const {
380 Option* const o = getSecure(name);
381 if (!o->isSet()) {
382 return false;
383 }
384 // check whether the list of files is valid
385 bool ok = true;
386 std::vector<std::string> files = getStringVector(name);
387 if (files.size() == 0) {
388 WRITE_ERRORF(TL("The file list for '%' is empty."), name);
389 ok = false;
390 }
391 for (const auto& file : files) {
392 if (!FileHelpers::isReadable(file)) {
393 if (file != "") {
394 WRITE_ERRORF(TL("File '%' is not accessible (%)."), file, std::strerror(errno));
395 ok = false;
396 } else {
397 WRITE_WARNING(TL("Empty file name given; ignoring."));
398 }
399 }
400 }
401 return ok;
402}
403
404
405bool
406OptionsCont::checkDependingSuboptions(const std::string& name, const std::string& prefix) const {
407 Option* o = getSecure(name);
408 if (o->isSet()) {
409 return true;
410 }
411 bool ok = true;
412 std::vector<std::string> seenSynonymes;
413 for (const auto& value : myValues) {
414 if (std::find(seenSynonymes.begin(), seenSynonymes.end(), value.first) != seenSynonymes.end()) {
415 continue;
416 }
417 if (value.second->isSet() && !value.second->isDefault() && value.first.find(prefix) == 0) {
418 WRITE_ERRORF(TL("Option '%' needs option '%'."), value.first, name);
419 std::vector<std::string> synonymes = getSynonymes(value.first);
420 std::copy(synonymes.begin(), synonymes.end(), std::back_inserter(seenSynonymes));
421 ok = false;
422 }
423 }
424 return ok;
425}
426
427
428void
429OptionsCont::reportDoubleSetting(const std::string& arg) const {
430 std::vector<std::string> synonymes = getSynonymes(arg);
431 std::ostringstream s;
432 s << "A value for the option '" + arg + "' was already set.\n Possible synonymes: ";
433 auto synonym = synonymes.begin();
434 while (synonym != synonymes.end()) {
435 s << (*synonym);
436 synonym++;
437 if (synonym != synonymes.end()) {
438 s << ", ";
439 }
440 }
441 WRITE_ERROR(s.str());
442}
443
444
445std::string
446OptionsCont::convertChar(char abbr) const {
447 char buf[2];
448 buf[0] = abbr;
449 buf[1] = 0;
450 std::string s(buf);
451 return s;
452}
453
454
455bool
456OptionsCont::isBool(const std::string& name) const {
457 Option* o = getSecure(name);
458 return o->isBool();
459}
460
461
462void
464 for (const auto& addresse : myAddresses) {
465 addresse.second->resetWritable();
466 }
467}
468
469
470void
472 for (const auto& addresse : myAddresses) {
473 addresse.second->resetDefault();
474 }
475}
476
477
478void
479OptionsCont::resetDefault(const std::string& name) {
480 getSecure(name)->resetDefault();
481}
482
483
484bool
485OptionsCont::isWriteable(const std::string& name) {
486 Option* o = getSecure(name);
487 return o->isWriteable();
488}
489
490
491void
493 // delete only adresse (because synonyms placed in values aim to the same Option)
494 for (const auto& addresse : myAddresses) {
495 delete addresse.second;
496 }
497 myAddresses.clear();
498 myValues.clear();
499 mySubTopics.clear();
500 mySubTopicEntries.clear();
501}
502
503
504void
505OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
506 const std::string& description) {
507 Option* o = getSecure(name);
508 if (o == nullptr) {
509 throw ProcessError("Option doesn't exist");
510 }
511 if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
512 throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
513 }
514 o->setDescription(description);
515 o->setSubtopic(subtopic);
516 mySubTopicEntries[subtopic].push_back(name);
517}
518
519
520void
521OptionsCont::setFurtherAttributes(const std::string& name, const std::string& subtopic, bool required, bool positional, const std::string& listSep) {
522 Option* o = getSecure(name);
523 if (o == nullptr) {
524 throw ProcessError("Option doesn't exist");
525 }
526 if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
527 throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
528 }
529 if (required) {
530 o->setRequired();
531 }
532 if (positional) {
533 o->setPositional();
534 }
535 o->setListSeparator(listSep);
536}
537
538
539void
540OptionsCont::setApplicationName(const std::string& appName,
541 const std::string& fullName) {
542 myAppName = appName;
543 myFullName = fullName;
544}
545
546
547void
548OptionsCont::setApplicationDescription(const std::string& appDesc) {
549 myAppDescription = appDesc;
550}
551
552
553void
554OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
555 myCallExamples.push_back(std::make_pair(example, desc));
556}
557
558
559void
562}
563
564
565void
566OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
567 myCopyrightNotices.push_back(copyrightLine);
568}
569
570
571void
575
576
577void
578OptionsCont::addOptionSubTopic(const std::string& topic) {
579 mySubTopics.push_back(topic);
580 mySubTopicEntries[topic] = std::vector<std::string>();
581}
582
583
584void
585OptionsCont::splitLines(std::ostream& os, std::string what,
586 int offset, int nextOffset) {
587 while (what.length() > 0) {
588 if ((int)what.length() > 79 - offset) {
589 std::string::size_type splitPos = what.rfind(';', 79 - offset);
590 if (splitPos == std::string::npos) {
591 splitPos = what.rfind(' ', 79 - offset);
592 } else {
593 splitPos++;
594 }
595 if (splitPos != std::string::npos) {
596 os << what.substr(0, splitPos) << std::endl;
597 what = what.substr(splitPos + 1);
598 for (int r = 0; r < (nextOffset + 1); ++r) {
599 os << ' ';
600 }
601 } else {
602 os << what;
603 what = "";
604 }
605 offset = nextOffset;
606 } else {
607 os << what;
608 what = "";
609 }
610 }
611 os << std::endl;
612}
613
614
615bool
617 MsgHandler::setupI18n(getString("language"));
618 if (missingOptions) {
619 // no options are given
620 std::cout << myFullName << std::endl;
621 std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
622 for (const auto& copyrightNotice : myCopyrightNotices) {
623 std::cout << " " << copyrightNotice.data() << std::endl;
624 }
625 std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
626 std::cout << TL(" Use --help to get the list of options.") << std::endl;
627 return true;
628 }
629
630 myWriteLicense = getBool("write-license");
631 // check whether the help shall be printed
632 if (getBool("help")) {
633 std::cout << myFullName << std::endl;
634 for (const auto& copyrightNotice : myCopyrightNotices) {
635 std::cout << " " << copyrightNotice.data() << std::endl;
636 }
637 printHelp(std::cout);
638 return true;
639 }
640 // check whether the help shall be printed
641 if (getBool("version")) {
642 std::cout << myFullName << std::endl;
643 std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
644 for (const auto& copyrightNotice : myCopyrightNotices) {
645 std::cout << " " << copyrightNotice.data() << std::endl;
646 }
647 std::cout << "\n" << myFullName << " is part of SUMO.\n";
648 std::cout << "This program and the accompanying materials\n";
649 std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
650 std::cout << "which accompanies this distribution, and is available at\n";
651 std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
652 std::cout << "This program may also be made available under the following Secondary\n";
653 std::cout << "Licenses when the conditions for such availability set forth in the Eclipse\n";
654 std::cout << "Public License 2.0 are satisfied: GNU General Public License, version 2\n";
655 std::cout << "or later which is available at\n";
656 std::cout << "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n";
657 std::cout << "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later" << std::endl;
658 return true;
659 }
660 // check whether the settings shall be printed
661 if (getBool("print-options")) {
662 std::cout << (*this);
663 }
664 // check whether something has to be done with options
665 // whether the current options shall be saved
666 if (isSet("save-configuration")) {
667 const std::string& configPath = getString("save-configuration");
668 if (configPath == "-" || configPath == "stdout") {
669 writeConfiguration(std::cout, true, false, getBool("save-commented"));
670 return true;
671 }
672 std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
673 if (!out.good()) {
674 throw ProcessError(TLF("Could not save configuration to '%'", configPath));
675 } else {
676 writeConfiguration(out, true, false, getBool("save-commented"), configPath);
677 if (getBool("verbose")) {
678 WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
679 }
680 return true;
681 }
682 }
683 // whether the template shall be saved
684 if (isSet("save-template")) {
685 if (getString("save-template") == "-" || getString("save-template") == "stdout") {
686 writeConfiguration(std::cout, false, true, getBool("save-commented"));
687 return true;
688 }
689 std::ofstream out(StringUtils::transcodeToLocal(getString("save-template")).c_str());
690 if (!out.good()) {
691 throw ProcessError(TLF("Could not save template to '%'", getString("save-template")));
692 } else {
693 writeConfiguration(out, false, true, getBool("save-commented"));
694 if (getBool("verbose")) {
695 WRITE_MESSAGEF(TL("Written template to '%'"), getString("save-template"));
696 }
697 return true;
698 }
699 }
700 if (isSet("save-schema")) {
701 if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
702 writeSchema(std::cout);
703 return true;
704 }
705 std::ofstream out(StringUtils::transcodeToLocal(getString("save-schema")).c_str());
706 if (!out.good()) {
707 throw ProcessError(TLF("Could not save schema to '%'", getString("save-schema")));
708 } else {
709 writeSchema(out);
710 if (getBool("verbose")) {
711 WRITE_MESSAGEF(TL("Written schema to '%'"), getString("save-schema"));
712 }
713 return true;
714 }
715 }
716 return false;
717}
718
719
720const std::vector<std::string>&
722 return mySubTopics;
723}
724
725
726std::vector<std::string>
727OptionsCont::getSubTopicsEntries(const std::string& subtopic) const {
728 if (mySubTopicEntries.count(subtopic) > 0) {
729 return mySubTopicEntries.find(subtopic)->second;
730 } else {
731 return std::vector<std::string>();
732 }
733}
734
735
736std::string
737OptionsCont::getTypeName(const std::string name) {
738 return getSecure(name)->getTypeName();
739}
740
741
742const std::string&
744 return myFullName;
745}
746
747
748bool
750 return myAddresses.size() == 0;
751}
752
753
754std::vector<std::pair<std::string, Option*> >::const_iterator
756 return myAddresses.cbegin();
757}
758
759
760std::vector<std::pair<std::string, Option*> >::const_iterator
762 return myAddresses.cend();
763}
764
765
766void
767OptionsCont::printHelp(std::ostream& os) {
768 // print application description
769 splitLines(os, TL(myAppDescription.c_str()), 0, 0);
770 os << std::endl;
771
772 // check option sizes first
773 // we want to know how large the largest not-too-large-entry will be
774 int tooLarge = 40;
775 int maxSize = 0;
776 for (const auto& subTopic : mySubTopics) {
777 for (const auto& entry : mySubTopicEntries[subTopic]) {
778 Option* o = getSecure(entry);
779 // name, two leading spaces and "--"
780 int csize = (int)entry.length() + 2 + 4;
781 // abbreviation length ("-X, "->4chars) if any
782 const auto synonymes = getSynonymes(entry);
783 for (const auto& synonym : synonymes) {
784 if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
785 csize += 4;
786 break;
787 }
788 }
789 // the type name
790 if (!o->isBool()) {
791 csize += 1 + (int)o->getTypeName().length();
792 }
793 // divider
794 csize += 2;
795 if (csize < tooLarge && maxSize < csize) {
796 maxSize = csize;
797 }
798 }
799 }
800
801 const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
802 if (helpTopic != "") {
803 bool foundTopic = false;
804 for (const auto& topic : mySubTopics) {
805 if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
806 foundTopic = true;
807 printHelpOnTopic(topic, tooLarge, maxSize, os);
808 }
809 }
810 if (!foundTopic) {
811 // print topic list
812 os << "Help Topics:" << std::endl;
813 for (std::string t : mySubTopics) {
814 os << " " << t << std::endl;
815 }
816 }
817 return;
818 }
819 // print usage BNF
820 os << "Usage: " << myAppName << " [OPTION]*" << std::endl;
821 // print additional text if any
822 if (myAdditionalMessage.length() > 0) {
823 os << myAdditionalMessage << std::endl << ' ' << std::endl;
824 }
825 // print the options
826 for (const auto& subTopic : mySubTopics) {
827 printHelpOnTopic(subTopic, tooLarge, maxSize, os);
828 }
829 os << std::endl;
830 // print usage examples, calc size first
831 if (myCallExamples.size() != 0) {
832 os << "Examples:" << std::endl;
833 for (const auto& callExample : myCallExamples) {
834 os << " " << myAppName << ' ' << callExample.first << std::endl;
835 os << " " << callExample.second << std::endl;
836 }
837 }
838 os << std::endl;
839 os << "Report bugs at <https://github.com/eclipse/sumo/issues>." << std::endl;
840 os << "Get in contact via <sumo@dlr.de>." << std::endl;
841}
842
843
844void
845OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
846 os << topic << " Options:" << std::endl;
847 for (const auto& entry : mySubTopicEntries[topic]) {
848 // start length computation
849 int csize = (int)entry.length() + 2;
850 Option* o = getSecure(entry);
851 os << " ";
852 // write abbreviation if given
853 const auto synonymes = getSynonymes(entry);
854 for (const auto& synonym : synonymes) {
855 if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
856 os << '-' << synonym << ", ";
857 csize += 4;
858 break;
859 }
860 }
861 // write leading '-'/"--"
862 os << "--";
863 csize += 2;
864 // write the name
865 os << entry;
866 // write the type if not a bool option
867 if (!o->isBool()) {
868 os << ' ' << o->getTypeName();
869 csize += 1 + (int)o->getTypeName().length();
870 }
871 csize += 2;
872 // write the description formatting it
873 os << " ";
874 for (int r = maxSize; r > csize; --r) {
875 os << ' ';
876 }
877 int offset = csize > tooLarge ? csize : maxSize;
878 splitLines(os, o->getDescription(), offset, maxSize);
879 }
880 os << std::endl;
881}
882
883
884void
885OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
886 const bool complete, const bool addComments, const std::string& relativeTo,
887 const bool forceRelative, const bool inComment) const {
888 if (!inComment) {
889 writeXMLHeader(os, false);
890 }
891 os << "<configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/";
892 if (myAppName == "sumo-gui") {
893 os << "sumo";
894 } else {
895 os << myAppName;
896 }
897 os << "Configuration.xsd\">" << std::endl << std::endl;
898 for (std::string subtopic : mySubTopics) {
899 if (subtopic == "Configuration" && !complete) {
900 continue;
901 }
902 const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
903 std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
904 subtopic = StringUtils::to_lower_case(subtopic);
905 bool hadOne = false;
906 for (const std::string& name : entries) {
907 Option* o = getSecure(name);
908 bool write = complete || (filled && !o->isDefault());
909 if (!write) {
910 continue;
911 }
912 if (name == "registry-viewport" && !complete) {
913 continue;
914 }
915 if (!hadOne) {
916 os << " <" << subtopic << ">" << std::endl;
917 }
918 // add the comment if wished
919 if (addComments) {
920 os << " <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->" << std::endl;
921 }
922 // write the option and the value (if given)
923 os << " <" << name << " value=\"";
924 if (o->isSet() && (filled || o->isDefault())) {
925 if (o->isFileName() && relativeTo != "") {
927 for (auto& file : fileList) {
929 StringUtils::urlEncode(file, " ;%"),
930 StringUtils::urlEncode(relativeTo, " ;%"),
931 forceRelative || getBool("save-configuration.relative"));
932 }
933 os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
934 } else {
935 os << StringUtils::escapeXML(o->getValueString(), inComment);
936 }
937 }
938 if (complete) {
939 std::vector<std::string> synonymes = getSynonymes(name);
940 if (!synonymes.empty()) {
941 os << "\" synonymes=\"";
942 for (auto synonym = synonymes.begin(); synonym != synonymes.end(); synonym++) {
943 if (synonym != synonymes.begin()) {
944 os << " ";
945 }
946 os << (*synonym);
947 }
948 }
949 os << "\" type=\"" << o->getTypeName();
950 if (!addComments) {
951 os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
952 }
953 }
954 os << "\"/>" << std::endl;
955 // append an endline if a comment was printed
956 if (addComments) {
957 os << std::endl;
958 }
959 hadOne = true;
960 }
961 if (hadOne) {
962 os << " </" << subtopic << ">" << std::endl << std::endl;
963 }
964 }
965 os << "</configuration>" << std::endl;
966}
967
968
969void
970OptionsCont::writeSchema(std::ostream& os) {
971 writeXMLHeader(os, false);
972 os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n\n";
973 os << " <xsd:include schemaLocation=\"baseTypes.xsd\"/>\n";
974 os << " <xsd:element name=\"configuration\" type=\"configurationType\"/>\n\n";
975 os << " <xsd:complexType name=\"configurationType\">\n";
976 os << " <xsd:all>\n";
977 for (std::string subtopic : mySubTopics) {
978 if (subtopic == "Configuration") {
979 continue;
980 }
981 std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
982 subtopic = StringUtils::to_lower_case(subtopic);
983 os << " <xsd:element name=\"" << subtopic << "\" type=\"" << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
984 }
985 os << " </xsd:all>\n";
986 os << " </xsd:complexType>\n\n";
987 for (std::string subtopic : mySubTopics) {
988 if (subtopic == "Configuration") {
989 continue;
990 }
991 const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
992 std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
993 subtopic = StringUtils::to_lower_case(subtopic);
994 os << " <xsd:complexType name=\"" << subtopic << "TopicType\">\n";
995 os << " <xsd:all>\n";
996 for (const auto& entry : entries) {
997 Option* o = getSecure(entry);
998 std::string type = o->getTypeName();
999 type = StringUtils::to_lower_case(type);
1000 if (type == "int[]") {
1001 type = "intArray";
1002 }
1003 if (type == "str[]") {
1004 type = "strArray";
1005 }
1006 os << " <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
1007 }
1008 os << " </xsd:all>\n";
1009 os << " </xsd:complexType>\n\n";
1010 }
1011 os << "</xsd:schema>\n";
1012}
1013
1014
1015void
1016OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
1017 time_t rawtime;
1018 char buffer [80];
1019
1020 os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
1021 time(&rawtime);
1022 strftime(buffer, 80, "<!-- generated on %F %T by ", localtime(&rawtime));
1023 os << buffer << myFullName << "\n";
1024 if (myWriteLicense) {
1025 os << "This data file and the accompanying materials\n"
1026 "are made available under the terms of the Eclipse Public License v2.0\n"
1027 "which accompanies this distribution, and is available at\n"
1028 "http://www.eclipse.org/legal/epl-v20.html\n"
1029 "This file may also be made available under the following Secondary\n"
1030 "Licenses when the conditions for such availability set forth in the Eclipse\n"
1031 "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
1032 "or later which is available at\n"
1033 "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
1034 "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
1035 }
1036 if (includeConfig) {
1037 writeConfiguration(os, true, false, false, "", false, true);
1038 }
1039 os << "-->\n\n";
1040}
1041
1042
1043bool
1044OptionsCont::isInStringVector(const std::string& optionName,
1045 const std::string& itemName) const {
1046 if (isSet(optionName)) {
1047 std::vector<std::string> values = getStringVector(optionName);
1048 return std::find(values.begin(), values.end(), itemName) != values.end();
1049 }
1050 return false;
1051}
1052
1053/****************************************************************************/
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:271
#define WRITE_MESSAGEF(...)
Definition MsgHandler.h:273
#define WRITE_ERRORF(...)
Definition MsgHandler.h:280
#define WRITE_ERROR(msg)
Definition MsgHandler.h:279
#define WRITE_WARNING(msg)
Definition MsgHandler.h:270
#define TL(string)
Definition MsgHandler.h:287
#define TLF(string,...)
Definition MsgHandler.h:288
std::vector< std::string > StringVector
Definition of a vector of strings.
Definition Option.h:42
std::vector< int > IntVector
Definition of a vector of ints.
Definition Option.h:37
std::ostream & operator<<(std::ostream &os, const OptionsCont &oc)
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
static std::string fixRelative(const std::string &filename, const std::string &basePath, const bool force, std::string curDir="")
Fixes the relative path for the given filename in relation to the basePath (usually a config file).
static std::string checkForRelativity(const std::string &filename, const std::string &basePath)
Returns the path from a configuration so that it is accessable from the current working directory.
static bool isReadable(std::string path)
Checks whether the given file is readable.
static void setupI18n(const std::string &locale="")
set up gettext stuff
A class representing a single program option.
Definition Option.h:72
bool isWriteable() const
Returns the information whether the option may be set a further time.
Definition Option.cpp:184
bool isSet() const
returns the information whether this options holds a valid value
Definition Option.cpp:53
virtual bool isDefault() const
Returns the information whether the option holds the default value.
Definition Option.cpp:112
void setRequired()
mark option as required
Definition Option.cpp:220
virtual std::string getString() const
Returns the stored string value.
Definition Option.cpp:71
virtual const IntVector & getIntVector() const
Returns the stored integer vector.
Definition Option.cpp:83
const std::string & getDescription() const
Returns the description of what this option does.
Definition Option.cpp:202
void setListSeparator(const std::string &listSep)
set list separator
Definition Option.cpp:240
virtual bool isFileName() const
Returns the information whether this option is a file name.
Definition Option.cpp:136
virtual const StringVector & getStringVector() const
Returns the stored string vector.
Definition Option.cpp:89
void setDescription(const std::string &desc)
Sets the description of what this option does.
Definition Option.cpp:208
virtual const std::string & getTypeName() const
Returns the mml-type name of this option.
Definition Option.cpp:257
virtual int getInt() const
Returns the stored integer value.
Definition Option.cpp:65
virtual double getFloat() const
Returns the stored double value.
Definition Option.cpp:59
virtual bool getBool() const
Returns the stored boolean value.
Definition Option.cpp:77
void setPositional()
mark option as positional
Definition Option.cpp:230
void resetDefault()
Resets the option to be on its default value.
Definition Option.cpp:196
const std::string & getSubTopic() const
Returns the subtopic to which this option belongs.
Definition Option.cpp:245
virtual bool set(const std::string &v, const std::string &orig, const bool append)=0
Stores the given value.
virtual bool isBool() const
Returns the information whether the option is a bool option.
Definition Option.cpp:130
const std::string & getValueString() const
Returns the string-representation of the value.
Definition Option.cpp:106
void setSubtopic(const std::string &subtopic)
Sets the subtopic to which this option belongs.
Definition Option.cpp:251
A storage for options typed value containers)
Definition OptionsCont.h:89
void setAdditionalHelpMessage(const std::string &add)
Sets an additional message to be printed at the begin of the help screen.
~OptionsCont()
Destructor.
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
void resetDefault()
Resets all options to default.
void setFurtherAttributes(const std::string &name, const std::string &subtopic, bool required, bool positional, const std::string &listSep)
mark option as required
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
std::vector< std::pair< std::string, std::string > > myCallExamples
list of call examples
bool isWriteable(const std::string &name)
Returns the information whether the named option may be set.
std::map< std::string, std::vector< std::string > > mySubTopicEntries
A map from subtopic to option.
void writeXMLHeader(std::ostream &os, const bool includeConfig=true) const
Writes a standard XML header, including the configuration.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
void splitLines(std::ostream &os, std::string what, int offset, int nextOffset)
Writes the given string 'formatted'.
void setApplicationName(const std::string &appName, const std::string &fullName)
Sets the application name.
void printHelpOnTopic(const std::string &topic, int tooLarge, int maxSize, std::ostream &os)
Prints help on the given topic.
const std::string & getSubTopic(const std::string &name) const
Returns the option category.
std::map< std::string, Option * > myValues
option maps sorted by name (for adresses AND their synonyms)
bool isEmpty() const
check if options container is empty
std::string myAdditionalMessage
std::vector< std::string > myCopyrightNotices
const std::vector< std::string > & getSubTopics() const
return the list of subtopics
std::vector< std::pair< std::string, Option * > > myAddresses
option-adresses
const IntVector & getIntVector(const std::string &name) const
Returns the list of integer-value of the named option (only for Option_IntVector)
std::vector< std::string > getSynonymes(const std::string &name) const
Returns the synonymes of an option name.
void reportDoubleSetting(const std::string &arg) const
Reports an error that the option has already been set.
std::vector< std::string > mySubTopics
lists of option subtopics and copyright notices
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
static OptionsCont EMPTY_OPTIONS
empty option container
Definition OptionsCont.h:96
bool myWriteLicense
Information whether we should always include license information in file headers.
void addSynonyme(const std::string &name1, const std::string &name2, bool isDeprecated=false)
Adds a synonyme for an options name (any order)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool setDefault(const std::string &name, const std::string &value)
Sets the given value for the named option as new default value.
void doRegister(const std::string &name, Option *o)
Adds an option under the given name.
bool exists(const std::string &name) const
Returns the information whether the named option is known.
const std::string & getFullName() const
get options full name
bool isBool(const std::string &name) const
Returns the information whether the option is a boolean option.
void addCopyrightNotice(const std::string &copyrightLine)
Adds a copyright notice to the help output.
std::string getTypeName(const std::string name)
return the type name for the given option
void writeSchema(std::ostream &os)
Writes the xml schema for the configuration.
void clear()
Removes all information from the container.
void setApplicationDescription(const std::string &appDesc)
Sets the application description.
std::vector< std::pair< std::string, Option * > >::const_iterator begin() const
get begin adresses iterator
bool set(const std::string &name, const std::string &value, const bool append=false)
Sets the given value for the named option.
void writeConfiguration(std::ostream &os, const bool filled, const bool complete, const bool addComments, const std::string &relativeTo="", const bool forceRelative=false, const bool inComment=false) const
Writes the configuration.
void clearCopyrightNotices()
Removes all copyright information.
std::string convertChar(char abbr) const
Converts an abbreviation into a name.
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
OptionsCont()
Constructor.
void printHelp(std::ostream &os)
Prints the help.
std::vector< std::pair< std::string, Option * > >::const_iterator end() const
get begin adresses iterator
std::string getValueString(const std::string &name) const
Returns the string-value of the named option (all options)
std::string myAppDescription
const std::string & getDescription(const std::string &name) const
Returns the option description.
bool setByRootElement(const std::string &name, const std::string &value)
Sets the given value for the option which can handle the given XML root.
std::map< std::string, bool > myDeprecatedSynonymes
A map from deprecated options to a bool indicating whether we warned about deprecation.
static OptionsCont myOptions
The static options container used.
bool checkDependingSuboptions(const std::string &name, const std::string &prefix) const
Checks whether an option is set, which has options with a prefix depending on it.
std::map< std::string, std::string > myXMLDefaults
A map from XML root element to option.
std::string myAppName
some information on the application
void resetWritable()
Resets all options to be writeable.
void addXMLDefault(const std::string &name, const std::string &xmlRoot="")
Adds an XML root element to handle by default. The special root "" denotes the default handler.
static OptionsCont & getOptions()
Retrieves the options.
std::string myFullName
std::vector< std::string > getSubTopicsEntries(const std::string &subtopic) const
return the list of entries for the given subtopic
Option * getSecure(const std::string &name) const
Returns the named option.
void relocateFiles(const std::string &configuration) const
Modifies file name options according to the configuration path.
bool isInStringVector(const std::string &optionName, const std::string &itemName) const
Returns the named option is a list of string values containing the specified item.
bool processMetaOptions(bool missingOptions)
Checks for help and configuration output, returns whether we should exit.
bool isUsableFileList(const std::string &name) const
Checks whether the named option is usable as a file list (with at least a single file)
void addCallExample(const std::string &example, const std::string &desc)
Add a call example.
static const std::chrono::time_point< std::chrono::system_clock > & getLoadTime()
Return the time stamp of the last init.
Definition OptionsIO.h:101
static const std::string ENCODING
The encoding of parsed strings.
std::vector< std::string > getVector()
return vector of strings
static std::string urlEncode(const std::string &url, const std::string encodeWhich="")
encode url (stem from http://bogomip.net/blog/cpp-url-encoding-and-decoding/)
static std::string urlDecode(const std::string &encoded)
decode url (stem from http://bogomip.net/blog/cpp-url-encoding-and-decoding/)
static std::string to_lower_case(const std::string &str)
Transfers the content to lower case.
static std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
static std::string substituteEnvironment(const std::string &str, const std::chrono::time_point< std::chrono::system_clock > *const timeRef=nullptr)
Replaces an environment variable with its value (similar to bash); syntax for a variable is ${NAME}.
static std::string transcodeToLocal(const std::string &utf8String)
convert a string from UTF-8 to the local codepage