Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/options/OptionsCont.cpp
193671 views
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
/****************************************************************************/
14
/// @file OptionsCont.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @author Walter Bamberger
19
/// @date Mon, 17 Dec 2001
20
///
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>
37
#include <utils/common/UtilExceptions.h>
38
#include <utils/common/FileHelpers.h>
39
#include <utils/common/MsgHandler.h>
40
#include <utils/common/StdDefs.h>
41
#include <utils/common/StringTokenizer.h>
42
#include <utils/common/StringUtils.h>
43
#include <utils/xml/SUMOSAXAttributes.h>
44
#include "Option.h"
45
#include "OptionsIO.h"
46
#include "OptionsCont.h"
47
48
49
// ===========================================================================
50
// static member definitions
51
// ===========================================================================
52
53
OptionsCont OptionsCont::myOptions;
54
OptionsCont OptionsCont::EMPTY_OPTIONS;
55
56
// ===========================================================================
57
// method definitions
58
// ===========================================================================
59
60
OptionsCont&
61
OptionsCont::getOptions() {
62
return myOptions;
63
}
64
65
66
OptionsCont::OptionsCont() {
67
myCopyrightNotices.push_back(TL("Copyright (C) 2001-2026 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
68
}
69
70
71
OptionsCont::~OptionsCont() {
72
clear();
73
}
74
75
76
void
77
OptionsCont::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
101
void
102
OptionsCont::doRegister(const std::string& name1, char abbr, Option* o) {
103
doRegister(name1, o);
104
doRegister(convertChar(abbr), o);
105
}
106
107
108
void
109
OptionsCont::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
136
void
137
OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
138
myXMLDefaults[xmlRoot] = name;
139
}
140
141
142
bool
143
OptionsCont::exists(const std::string& name) const {
144
return myValues.count(name) > 0;
145
}
146
147
148
bool
149
OptionsCont::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
162
bool
163
OptionsCont::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
172
Option*
173
OptionsCont::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
200
std::string
201
OptionsCont::getValueString(const std::string& name) const {
202
Option* o = getSecure(name);
203
return o->getValueString();
204
}
205
206
207
std::string
208
OptionsCont::getString(const std::string& name) const {
209
Option* o = getSecure(name);
210
return o->getString();
211
}
212
213
214
double
215
OptionsCont::getFloat(const std::string& name) const {
216
Option* o = getSecure(name);
217
return o->getFloat();
218
}
219
220
221
int
222
OptionsCont::getInt(const std::string& name) const {
223
Option* o = getSecure(name);
224
return o->getInt();
225
}
226
227
228
bool
229
OptionsCont::getBool(const std::string& name) const {
230
Option* o = getSecure(name);
231
return o->getBool();
232
}
233
234
235
const IntVector&
236
OptionsCont::getIntVector(const std::string& name) const {
237
Option* o = getSecure(name);
238
return o->getIntVector();
239
}
240
241
const StringVector&
242
OptionsCont::getStringVector(const std::string& name) const {
243
Option* o = getSecure(name);
244
return o->getStringVector();
245
}
246
247
248
bool
249
OptionsCont::set(const std::string& name, const std::string& value, const bool append) {
250
Option* o = getSecure(name);
251
if (!o->isWriteable()) {
252
reportDoubleSetting(name);
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
268
bool
269
OptionsCont::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
279
bool
280
OptionsCont::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
291
std::vector<std::string>
292
OptionsCont::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
304
const std::string&
305
OptionsCont::getDescription(const std::string& name) const {
306
return getSecure(name)->getDescription();
307
}
308
309
310
const std::string&
311
OptionsCont::getSubTopic(const std::string& name) const {
312
return getSecure(name)->getSubTopic();
313
}
314
315
316
std::ostream&
317
operator<<(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
349
void
350
OptionsCont::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
381
bool
382
OptionsCont::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
408
bool
409
OptionsCont::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
431
void
432
OptionsCont::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
448
std::string
449
OptionsCont::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
458
bool
459
OptionsCont::isBool(const std::string& name) const {
460
Option* o = getSecure(name);
461
return o->isBool();
462
}
463
464
465
void
466
OptionsCont::resetWritable() {
467
for (const auto& addresse : myAddresses) {
468
addresse.second->resetWritable();
469
}
470
}
471
472
473
void
474
OptionsCont::resetDefault() {
475
for (const auto& addresse : myAddresses) {
476
addresse.second->resetDefault();
477
}
478
}
479
480
481
void
482
OptionsCont::resetDefault(const std::string& name) {
483
getSecure(name)->resetDefault();
484
}
485
486
487
bool
488
OptionsCont::isWriteable(const std::string& name) {
489
return getSecure(name)->isWriteable();
490
}
491
492
493
bool
494
OptionsCont::isEditable(const std::string& name) {
495
return getSecure(name)->isEditable();
496
497
}
498
499
500
void
501
OptionsCont::clear() {
502
// delete only address (because synonyms placed in values aim to the same Option)
503
for (const auto& addresse : myAddresses) {
504
delete addresse.second;
505
}
506
myAddresses.clear();
507
myValues.clear();
508
mySubTopics.clear();
509
mySubTopicEntries.clear();
510
}
511
512
513
void
514
OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
515
const std::string& description) {
516
Option* o = getSecure(name);
517
if (o == nullptr) {
518
throw ProcessError("Option doesn't exist");
519
}
520
if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
521
throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
522
}
523
o->setDescription(description);
524
o->setSubtopic(subtopic);
525
mySubTopicEntries[subtopic].push_back(name);
526
}
527
528
529
void
530
OptionsCont::setFurtherAttributes(const std::string& name, const std::string& subtopic, bool required, bool positional, const std::string& listSep) {
531
Option* o = getSecure(name);
532
if (o == nullptr) {
533
throw ProcessError("Option doesn't exist");
534
}
535
if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
536
throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
537
}
538
if (required) {
539
o->setRequired();
540
}
541
if (positional) {
542
o->setPositional();
543
}
544
o->setListSeparator(listSep);
545
}
546
547
548
void
549
OptionsCont::setOptionEditable(const std::string& name, const bool value) {
550
getSecure(name)->setEditable(value);
551
}
552
553
554
void
555
OptionsCont::setApplicationName(const std::string& appName, const std::string& fullName) {
556
myAppName = appName;
557
myFullName = fullName;
558
}
559
560
561
void
562
OptionsCont::setApplicationDescription(const std::string& appDesc) {
563
myAppDescription = appDesc;
564
}
565
566
567
void
568
OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
569
myCallExamples.push_back(std::make_pair(example, desc));
570
}
571
572
573
void
574
OptionsCont::setAdditionalHelpMessage(const std::string& add) {
575
myAdditionalMessage = add;
576
}
577
578
579
void
580
OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
581
myCopyrightNotices.push_back(copyrightLine);
582
}
583
584
585
void
586
OptionsCont::clearCopyrightNotices() {
587
myCopyrightNotices.clear();
588
}
589
590
591
void
592
OptionsCont::addOptionSubTopic(const std::string& topic) {
593
mySubTopics.push_back(topic);
594
mySubTopicEntries[topic] = std::vector<std::string>();
595
}
596
597
598
void
599
OptionsCont::splitLines(std::ostream& os, std::string what,
600
int offset, int nextOffset) {
601
while (what.length() > 0) {
602
if ((int)what.length() > 79 - offset) {
603
std::string::size_type splitPos = what.rfind(';', 79 - offset);
604
if (splitPos == std::string::npos) {
605
splitPos = what.rfind(' ', 79 - offset);
606
} else {
607
splitPos++;
608
}
609
if (splitPos != std::string::npos) {
610
os << what.substr(0, splitPos) << std::endl;
611
what = what.substr(splitPos + 1);
612
for (int r = 0; r < (nextOffset + 1); ++r) {
613
os << ' ';
614
}
615
} else {
616
os << what;
617
what = "";
618
}
619
offset = nextOffset;
620
} else {
621
os << what;
622
what = "";
623
}
624
}
625
os << std::endl;
626
}
627
628
629
bool
630
OptionsCont::processMetaOptions(bool missingOptions) {
631
MsgHandler::setupI18n(getString("language"));
632
localizeDescriptions();
633
if (missingOptions) {
634
// no options are given
635
std::cout << myFullName << std::endl;
636
std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
637
for (const auto& copyrightNotice : myCopyrightNotices) {
638
std::cout << " " << copyrightNotice.data() << std::endl;
639
}
640
std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
641
std::cout << TL(" Use --help to get the list of options.") << std::endl;
642
return true;
643
}
644
645
// check whether the help shall be printed
646
if (getBool("help")) {
647
std::cout << myFullName << std::endl;
648
for (const auto& copyrightNotice : myCopyrightNotices) {
649
std::cout << " " << copyrightNotice.data() << std::endl;
650
}
651
printHelp(std::cout);
652
return true;
653
}
654
// check whether the help shall be printed
655
if (getBool("version")) {
656
std::cout << myFullName << std::endl;
657
std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
658
for (const auto& copyrightNotice : myCopyrightNotices) {
659
std::cout << " " << copyrightNotice.data() << std::endl;
660
}
661
std::cout << "\n" << myFullName << " is part of SUMO.\n";
662
std::cout << "This program and the accompanying materials\n";
663
std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
664
std::cout << "which accompanies this distribution, and is available at\n";
665
std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
666
std::cout << "This program may also be made available under the following Secondary\n";
667
std::cout << "Licenses when the conditions for such availability set forth in the Eclipse\n";
668
std::cout << "Public License 2.0 are satisfied: GNU General Public License, version 2\n";
669
std::cout << "or later which is available at\n";
670
std::cout << "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n";
671
std::cout << "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later" << std::endl;
672
return true;
673
}
674
// check whether the settings shall be printed
675
if (getBool("print-options")) {
676
std::cout << (*this);
677
}
678
// check whether something has to be done with options
679
// whether the current options shall be saved
680
if (isSet("save-configuration")) {
681
const std::string& configPath = getString("save-configuration");
682
if (configPath == "-" || configPath == "stdout") {
683
writeConfiguration(std::cout, true, false, getBool("save-commented"));
684
return true;
685
}
686
std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
687
if (!out.good()) {
688
throw ProcessError(TLF("Could not save configuration to '%'", configPath));
689
} else {
690
writeConfiguration(out, true, false, getBool("save-commented"), configPath);
691
if (getBool("verbose")) {
692
WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
693
}
694
return true;
695
}
696
}
697
// whether the template shall be saved
698
if (isSet("save-template")) {
699
if (getString("save-template") == "-" || getString("save-template") == "stdout") {
700
writeConfiguration(std::cout, false, true, getBool("save-commented"));
701
return true;
702
}
703
std::ofstream out(StringUtils::transcodeToLocal(getString("save-template")).c_str());
704
if (!out.good()) {
705
throw ProcessError(TLF("Could not save template to '%'", getString("save-template")));
706
} else {
707
writeConfiguration(out, false, true, getBool("save-commented"));
708
if (getBool("verbose")) {
709
WRITE_MESSAGEF(TL("Written template to '%'"), getString("save-template"));
710
}
711
return true;
712
}
713
}
714
if (isSet("save-schema")) {
715
if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
716
writeSchema(std::cout);
717
return true;
718
}
719
std::ofstream out(StringUtils::transcodeToLocal(getString("save-schema")).c_str());
720
if (!out.good()) {
721
throw ProcessError(TLF("Could not save schema to '%'", getString("save-schema")));
722
} else {
723
writeSchema(out);
724
if (getBool("verbose")) {
725
WRITE_MESSAGEF(TL("Written schema to '%'"), getString("save-schema"));
726
}
727
return true;
728
}
729
}
730
return false;
731
}
732
733
734
void
735
OptionsCont::localizeDescriptions() {
736
if (!myAmLocalized && gLocaleInitialized) {
737
// options
738
for (auto option : myAddresses) {
739
option.second->setDescription(TL(option.second->getDescription().c_str()));
740
}
741
// examples
742
for (auto example : myCallExamples) {
743
example.second = TL(example.second.c_str());
744
}
745
// other text
746
setApplicationDescription(TL(myAppDescription.c_str()));
747
myAmLocalized = true;
748
}
749
}
750
751
752
const std::vector<std::string>&
753
OptionsCont::getSubTopics() const {
754
return mySubTopics;
755
}
756
757
758
std::vector<std::string>
759
OptionsCont::getSubTopicsEntries(const std::string& subtopic) const {
760
if (mySubTopicEntries.count(subtopic) > 0) {
761
return mySubTopicEntries.find(subtopic)->second;
762
} else {
763
return std::vector<std::string>();
764
}
765
}
766
767
768
std::string
769
OptionsCont::getTypeName(const std::string name) {
770
return getSecure(name)->getTypeName();
771
}
772
773
774
const std::string&
775
OptionsCont::getFullName() const {
776
return myFullName;
777
}
778
779
780
bool
781
OptionsCont::isEmpty() const {
782
return myAddresses.size() == 0;
783
}
784
785
786
std::vector<std::pair<std::string, Option*> >::const_iterator
787
OptionsCont::begin() const {
788
return myAddresses.cbegin();
789
}
790
791
792
std::vector<std::pair<std::string, Option*> >::const_iterator
793
OptionsCont::end() const {
794
return myAddresses.cend();
795
}
796
797
798
void
799
OptionsCont::printHelp(std::ostream& os) {
800
// print application description
801
splitLines(os, TL(myAppDescription.c_str()), 0, 0);
802
os << std::endl;
803
804
// check option sizes first
805
// we want to know how large the largest not-too-large-entry will be
806
int tooLarge = 40;
807
int maxSize = 0;
808
for (const auto& subTopic : mySubTopics) {
809
for (const auto& entry : mySubTopicEntries[subTopic]) {
810
Option* o = getSecure(entry);
811
// name, two leading spaces and "--"
812
int csize = (int)entry.length() + 2 + 4;
813
// abbreviation length ("-X, "->4chars) if any
814
const auto synonymes = getSynonymes(entry);
815
for (const auto& synonym : synonymes) {
816
if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
817
csize += 4;
818
break;
819
}
820
}
821
// the type name
822
if (!o->isBool()) {
823
csize += 1 + (int)o->getTypeName().length();
824
}
825
// divider
826
csize += 2;
827
if (csize < tooLarge && maxSize < csize) {
828
maxSize = csize;
829
}
830
}
831
}
832
833
const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
834
if (helpTopic != "") {
835
bool foundTopic = false;
836
for (const auto& topic : mySubTopics) {
837
if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
838
foundTopic = true;
839
printHelpOnTopic(topic, tooLarge, maxSize, os);
840
}
841
}
842
if (!foundTopic) {
843
// print topic list
844
os << TL("Help Topics:") << std::endl;
845
for (const std::string& t : mySubTopics) {
846
os << " " << t << std::endl;
847
}
848
}
849
return;
850
}
851
// print usage BNF
852
os << TL("Usage: ") << myAppName << TL(" [OPTION]*") << std::endl;
853
// print additional text if any
854
if (myAdditionalMessage.length() > 0) {
855
os << myAdditionalMessage << std::endl << std::endl;
856
}
857
// print the options
858
for (const auto& subTopic : mySubTopics) {
859
printHelpOnTopic(subTopic, tooLarge, maxSize, os);
860
}
861
os << std::endl;
862
// print usage examples, calc size first
863
if (myCallExamples.size() != 0) {
864
os << TL("Examples:") << std::endl;
865
for (const auto& callExample : myCallExamples) {
866
os << " " << myAppName << ' ' << callExample.first << std::endl;
867
os << " " << callExample.second << std::endl;
868
}
869
}
870
os << std::endl;
871
os << TLF("Report bugs at %.", "<https://github.com/eclipse-sumo/sumo/issues>") << std::endl;
872
os << TLF("Get in contact via %.", "<[email protected]>") << std::endl;
873
}
874
875
876
void
877
OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
878
os << TLF("% Options:", topic) << std::endl;
879
for (const auto& entry : mySubTopicEntries[topic]) {
880
// start length computation
881
int csize = (int)entry.length() + 2;
882
Option* o = getSecure(entry);
883
os << " ";
884
// write abbreviation if given
885
const auto synonymes = getSynonymes(entry);
886
for (const auto& synonym : synonymes) {
887
if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
888
os << '-' << synonym << ", ";
889
csize += 4;
890
break;
891
}
892
}
893
// write leading '-'/"--"
894
os << "--";
895
csize += 2;
896
// write the name
897
os << entry;
898
// write the type if not a bool option
899
if (!o->isBool()) {
900
os << ' ' << o->getTypeName();
901
csize += 1 + (int)o->getTypeName().length();
902
}
903
csize += 2;
904
// write the description formatting it
905
os << " ";
906
for (int r = maxSize; r > csize; --r) {
907
os << ' ';
908
}
909
int offset = csize > tooLarge ? csize : maxSize;
910
splitLines(os, o->getDescription(), offset, maxSize);
911
}
912
os << std::endl;
913
}
914
915
916
void
917
OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
918
const bool complete, const bool addComments, const std::string& relativeTo,
919
const bool forceRelative, const bool inComment, const std::string& indent) const {
920
if (!inComment) {
921
writeXMLHeader(os, false);
922
}
923
const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
924
os << indent << "<" << app << "Configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
925
<< "xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/" << app << "Configuration.xsd\">\n\n";
926
for (std::string subtopic : mySubTopics) {
927
if (subtopic == "Configuration" && !complete) {
928
continue;
929
}
930
const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
931
std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
932
subtopic = StringUtils::to_lower_case(subtopic);
933
bool hadOne = false;
934
for (const std::string& name : entries) {
935
Option* o = getSecure(name);
936
bool write = complete || (filled && !o->isDefault());
937
if (!write) {
938
continue;
939
}
940
if (name == "registry-viewport" && !complete) {
941
continue;
942
}
943
if (!hadOne) {
944
os << indent << " <" << subtopic << ">\n";
945
}
946
// add the comment if wished
947
if (addComments) {
948
os << indent << " <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->\n";
949
}
950
// write the option and the value (if given)
951
os << indent << " <" << name << " value=\"";
952
if (o->isSet() && (filled || o->isDefault())) {
953
if (o->isFileName() && relativeTo != "") {
954
StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
955
for (auto& file : fileList) {
956
if (StringUtils::startsWith(file, "${")) {
957
// there is an environment variable up front, assume it points to an absolute path
958
// not even forcing relativity makes sense here
959
file = StringUtils::urlEncode(file, " ;%");
960
} else {
961
file = FileHelpers::fixRelative(
962
StringUtils::urlEncode(file, " ;%"),
963
StringUtils::urlEncode(relativeTo, " ;%"),
964
forceRelative || getBool("save-configuration.relative"));
965
}
966
}
967
os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
968
} else {
969
os << StringUtils::escapeXML(o->getValueString(), inComment);
970
}
971
}
972
if (complete) {
973
const std::vector<std::string> synonymes = getSynonymes(name);
974
if (!synonymes.empty()) {
975
os << "\" synonymes=\"" << toString(synonymes);
976
}
977
std::string deprecated;
978
for (const auto& synonym : synonymes) {
979
if (myDeprecatedSynonymes.count(synonym) > 0) {
980
deprecated += " " + synonym;
981
}
982
}
983
if (deprecated != "") {
984
os << "\" deprecated=\"" << deprecated.substr(1);
985
}
986
os << "\" type=\"" << o->getTypeName();
987
if (!addComments) {
988
os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
989
}
990
}
991
os << "\"/>\n";
992
// append an endline if a comment was printed
993
if (addComments) {
994
os << "\n";
995
}
996
hadOne = true;
997
}
998
if (hadOne) {
999
os << indent << " </" << subtopic << ">\n\n";
1000
}
1001
}
1002
os << indent << "</" << app << "Configuration>" << std::endl; // flushing seems like a good idea here
1003
}
1004
1005
1006
void
1007
OptionsCont::writeSchema(std::ostream& os) {
1008
const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
1009
writeXMLHeader(os, false);
1010
os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n";
1011
os << " <xsd:complexType name=\"" << app << "ConfigurationType\">\n";
1012
os << " <xsd:all>\n";
1013
for (std::string subtopic : mySubTopics) {
1014
if (subtopic == "Configuration") {
1015
continue;
1016
}
1017
std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
1018
subtopic = StringUtils::to_lower_case(subtopic);
1019
os << " <xsd:element name=\"" << subtopic << "\" type=\"" << app << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
1020
}
1021
os << " </xsd:all>\n";
1022
os << " </xsd:complexType>\n\n";
1023
for (std::string subtopic : mySubTopics) {
1024
if (subtopic == "Configuration") {
1025
continue;
1026
}
1027
const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
1028
std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
1029
subtopic = StringUtils::to_lower_case(subtopic);
1030
os << " <xsd:complexType name=\"" << app << subtopic << "TopicType\">\n";
1031
os << " <xsd:all>\n";
1032
for (const auto& entry : entries) {
1033
Option* o = getSecure(entry);
1034
std::string type = o->getTypeName();
1035
type = StringUtils::to_lower_case(type);
1036
if (type == "int[]") {
1037
type = "intArray";
1038
}
1039
if (type == "str[]") {
1040
type = "strArray";
1041
}
1042
os << " <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
1043
}
1044
os << " </xsd:all>\n";
1045
os << " </xsd:complexType>\n\n";
1046
}
1047
os << "</xsd:schema>\n";
1048
}
1049
1050
1051
void
1052
OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
1053
os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
1054
os << "<!-- ";
1055
if (!getBool("write-metadata")) {
1056
os << "generated on " << StringUtils::isoTimeString() << " by " << myFullName << "\n";
1057
}
1058
if (getBool("write-license")) {
1059
os << "This data file and the accompanying materials\n"
1060
"are made available under the terms of the Eclipse Public License v2.0\n"
1061
"which accompanies this distribution, and is available at\n"
1062
"http://www.eclipse.org/legal/epl-v20.html\n"
1063
"This file may also be made available under the following Secondary\n"
1064
"Licenses when the conditions for such availability set forth in the Eclipse\n"
1065
"Public License 2.0 are satisfied: GNU General Public License, version 2\n"
1066
"or later which is available at\n"
1067
"https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
1068
"SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
1069
}
1070
if (includeConfig && !getBool("write-metadata")) {
1071
writeConfiguration(os, true, false, false, "", false, true);
1072
}
1073
os << "-->\n\n";
1074
}
1075
1076
1077
bool
1078
OptionsCont::isInStringVector(const std::string& optionName,
1079
const std::string& itemName) const {
1080
if (isSet(optionName)) {
1081
std::vector<std::string> values = getStringVector(optionName);
1082
return std::find(values.begin(), values.end(), itemName) != values.end();
1083
}
1084
return false;
1085
}
1086
1087
1088
OptionsCont*
1089
OptionsCont::clone() const {
1090
// build a clone to call writeConfiguration on
1091
// (with the possibility of changing a few settings and not affecting the original)
1092
OptionsCont* oc = new OptionsCont(*this);
1093
oc->resetWritable();
1094
for (auto& addr : oc->myAddresses) {
1095
addr.second = addr.second->clone();
1096
}
1097
return oc;
1098
}
1099
1100
1101
/****************************************************************************/
1102
1103