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