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