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