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