1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file OptionsCont.cpp
11 /// @author Daniel Krajzewicz
12 /// @author Jakob Erdmann
13 /// @author Michael Behrisch
14 /// @author Walter Bamberger
15 /// @date Mon, 17 Dec 2001
16 /// @version $Id$
17 ///
18 // A storage for options (typed value containers)
19 /****************************************************************************/
20 // ===========================================================================
21 // included modules
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 <cassert>
33 #include <ctime>
34 #include <cstring>
35 #include <cerrno>
36 #include <iterator>
37 #include "Option.h"
38 #include "OptionsCont.h"
39 #include <utils/common/UtilExceptions.h>
40 #include <utils/common/FileHelpers.h>
41 #include <utils/common/MsgHandler.h>
42 #include <utils/common/StringTokenizer.h>
43 #include <utils/common/StringUtils.h>
44 #include <utils/xml/SUMOSAXAttributes.h>
45 #include <sstream>
46
47
48 // ===========================================================================
49 // static member definitions
50 // ===========================================================================
51 OptionsCont OptionsCont::myOptions;
52
53
54 // ===========================================================================
55 // method definitions
56 // ===========================================================================
57 OptionsCont&
getOptions()58 OptionsCont::getOptions() {
59 return myOptions;
60 }
61
62
OptionsCont()63 OptionsCont::OptionsCont()
64 : myAddresses(), myValues(), myDeprecatedSynonymes(), myHaveInformedAboutDeprecatedDivider(false) {
65 myCopyrightNotices.push_back("Copyright (C) 2001-2019 German Aerospace Center (DLR) and others; https://sumo.dlr.de");
66 }
67
68
~OptionsCont()69 OptionsCont::~OptionsCont() {
70 clear();
71 }
72
73
74 void
doRegister(const std::string & name,Option * v)75 OptionsCont::doRegister(const std::string& name, Option* v) {
76 assert(v != 0);
77 ItemAddressContType::iterator i = std::find(myAddresses.begin(), myAddresses.end(), v);
78 if (i == myAddresses.end()) {
79 myAddresses.push_back(v);
80 }
81 if (myValues.find(name) != myValues.end()) {
82 throw ProcessError(name + " is an already used option name.");
83 }
84 myValues[name] = v;
85 }
86
87
88 void
doRegister(const std::string & name1,char abbr,Option * v)89 OptionsCont::doRegister(const std::string& name1, char abbr, Option* v) {
90 doRegister(name1, v);
91 doRegister(convertChar(abbr), v);
92 }
93
94
95 void
addSynonyme(const std::string & name1,const std::string & name2,bool isDeprecated)96 OptionsCont::addSynonyme(const std::string& name1, const std::string& name2, bool isDeprecated) {
97 KnownContType::iterator i1 = myValues.find(name1);
98 KnownContType::iterator i2 = myValues.find(name2);
99 if (i1 == myValues.end() && i2 == myValues.end()) {
100 throw ProcessError("Neither the option '" + name1 + "' nor the option '" + name2 + "' is known yet");
101 }
102 if (i1 != myValues.end() && i2 != myValues.end()) {
103 if ((*i1).second == (*i2).second) {
104 return;
105 }
106 throw ProcessError("Both options '" + name1 + "' and '" + name2 + "' do exist and differ.");
107 }
108 if (i1 == myValues.end() && i2 != myValues.end()) {
109 doRegister(name1, (*i2).second);
110 if (isDeprecated) {
111 myDeprecatedSynonymes[name1] = false;
112 }
113 }
114 if (i1 != myValues.end() && i2 == myValues.end()) {
115 doRegister(name2, (*i1).second);
116 if (isDeprecated) {
117 myDeprecatedSynonymes[name2] = false;
118 }
119 }
120 }
121
122
123 void
addXMLDefault(const std::string & name,const std::string & xmlRoot)124 OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
125 myXMLDefaults[xmlRoot] = name;
126 }
127
128
129 bool
exists(const std::string & name) const130 OptionsCont::exists(const std::string& name) const {
131 return myValues.count(name) > 0;
132 }
133
134
135 bool
isSet(const std::string & name,bool failOnNonExistant) const136 OptionsCont::isSet(const std::string& name, bool failOnNonExistant) const {
137 KnownContType::const_iterator i = myValues.find(name);
138 if (i == myValues.end()) {
139 if (failOnNonExistant) {
140 throw ProcessError("Internal request for unknown option '" + name + "'!");
141 } else {
142 return false;
143 }
144 }
145 return (*i).second->isSet();
146 }
147
148
149 void
unSet(const std::string & name,bool failOnNonExistant) const150 OptionsCont::unSet(const std::string& name, bool failOnNonExistant) const {
151 KnownContType::const_iterator i = myValues.find(name);
152 if (i == myValues.end()) {
153 if (failOnNonExistant) {
154 throw ProcessError("Internal request for unknown option '" + name + "'!");
155 } else {
156 return;
157 }
158 }
159 (*i).second->unSet();
160 }
161
162
163 bool
isDefault(const std::string & name) const164 OptionsCont::isDefault(const std::string& name) const {
165 KnownContType::const_iterator i = myValues.find(name);
166 if (i == myValues.end()) {
167 return false;
168 }
169 return (*i).second->isDefault();
170 }
171
172
173 Option*
getSecure(const std::string & name) const174 OptionsCont::getSecure(const std::string& name) const {
175 KnownContType::const_iterator k = myValues.find(name);
176 if (k == myValues.end()) {
177 throw ProcessError("No option with the name '" + name + "' exists.");
178 }
179 std::map<std::string, bool>::iterator s = myDeprecatedSynonymes.find(name);
180 if (s != myDeprecatedSynonymes.end() && !s->second) {
181 std::string defaultName;
182 for (std::map<std::string, std::vector<std::string> >::const_iterator i = mySubTopicEntries.begin(); i != mySubTopicEntries.end(); ++i) {
183 for (std::vector<std::string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
184 KnownContType::const_iterator l = myValues.find(*j);
185 if (l != myValues.end() && l->second == k->second) {
186 defaultName = *j;
187 break;
188 }
189 }
190 if (defaultName != "") {
191 break;
192 }
193 }
194 WRITE_WARNING("Please note that '" + name + "' is deprecated.\n Use '" + defaultName + "' instead.");
195 s->second = true;
196 }
197 return k->second;
198 }
199
200
201 std::string
getString(const std::string & name) const202 OptionsCont::getString(const std::string& name) const {
203 Option* o = getSecure(name);
204 return o->getString();
205 }
206
207
208 double
getFloat(const std::string & name) const209 OptionsCont::getFloat(const std::string& name) const {
210 Option* o = getSecure(name);
211 return o->getFloat();
212 }
213
214
215 int
getInt(const std::string & name) const216 OptionsCont::getInt(const std::string& name) const {
217 Option* o = getSecure(name);
218 return o->getInt();
219 }
220
221
222 bool
getBool(const std::string & name) const223 OptionsCont::getBool(const std::string& name) const {
224 Option* o = getSecure(name);
225 return o->getBool();
226 }
227
228
229 const IntVector&
getIntVector(const std::string & name) const230 OptionsCont::getIntVector(const std::string& name) const {
231 Option* o = getSecure(name);
232 return o->getIntVector();
233 }
234
235
236 const FloatVector&
getFloatVector(const std::string & name) const237 OptionsCont::getFloatVector(const std::string& name) const {
238 Option* o = getSecure(name);
239 return o->getFloatVector();
240 }
241
242
243 bool
set(const std::string & name,const std::string & value)244 OptionsCont::set(const std::string& name, const std::string& value) {
245 Option* o = getSecure(name);
246 if (!o->isWriteable()) {
247 reportDoubleSetting(name);
248 return false;
249 }
250 try {
251 if (!o->set(value)) {
252 return false;
253 }
254 } catch (ProcessError& e) {
255 WRITE_ERROR("While processing option '" + name + "':\n " + e.what());
256 return false;
257 }
258 return true;
259 }
260
261
262 bool
setDefault(const std::string & name,const std::string & value)263 OptionsCont::setDefault(const std::string& name, const std::string& value) {
264 if (set(name, value)) {
265 getSecure(name)->resetDefault();
266 return true;
267 }
268 return false;
269 }
270
271
272 bool
setByRootElement(const std::string & root,const std::string & value)273 OptionsCont::setByRootElement(const std::string& root, const std::string& value) {
274 if (myXMLDefaults.count(root) > 0) {
275 return set(myXMLDefaults[root], value);
276 }
277 if (myXMLDefaults.count("") > 0) {
278 return set(myXMLDefaults[""], value);
279 }
280 return false;
281 }
282
283
284 std::vector<std::string>
getSynonymes(const std::string & name) const285 OptionsCont::getSynonymes(const std::string& name) const {
286 Option* o = getSecure(name);
287 std::vector<std::string> v(0);
288 for (KnownContType::const_iterator i = myValues.begin(); i != myValues.end(); i++) {
289 if ((*i).second == o && name != (*i).first) {
290 v.push_back((*i).first);
291 }
292 }
293 return v;
294 }
295
296
297 const std::string&
getDescription(const std::string & name) const298 OptionsCont::getDescription(const std::string& name) const {
299 return getSecure(name)->getDescription();
300 }
301
302
303 std::ostream&
operator <<(std::ostream & os,const OptionsCont & oc)304 operator<<(std::ostream& os, const OptionsCont& oc) {
305 std::vector<std::string> done;
306 os << "Options set:" << std::endl;
307 for (OptionsCont::KnownContType::const_iterator i = oc.myValues.begin();
308 i != oc.myValues.end(); i++) {
309 std::vector<std::string>::iterator j = std::find(done.begin(), done.end(), (*i).first);
310 if (j == done.end()) {
311 std::vector<std::string> synonymes = oc.getSynonymes((*i).first);
312 if (synonymes.size() != 0) {
313 os << (*i).first << " (";
314 for (j = synonymes.begin(); j != synonymes.end(); j++) {
315 if (j != synonymes.begin()) {
316 os << ", ";
317 }
318 os << (*j);
319 }
320 os << ")";
321 } else {
322 os << (*i).first;
323 }
324 if ((*i).second->isSet()) {
325 os << ": " << (*i).second->getValueString() << std::endl;
326 } else {
327 os << ": <INVALID>" << std::endl;
328 }
329 done.push_back((*i).first);
330 copy(synonymes.begin(), synonymes.end(), back_inserter(done));
331 }
332 }
333 return os;
334 }
335
336
337 void
relocateFiles(const std::string & configuration) const338 OptionsCont::relocateFiles(const std::string& configuration) const {
339 for (Option* const option : myAddresses) {
340 if (option->isFileName() && option->isSet()) {
341 std::vector<std::string> fileList = StringTokenizer(option->getString(), ",").getVector();
342 for (std::string& f : fileList) {
343 // Pruning is necessary because filenames may be separated by ', ' in the configuration file
344 f = StringUtils::urlDecode(FileHelpers::checkForRelativity(StringUtils::prune(f), configuration));
345 }
346 const std::string conv = joinToString(fileList, ',');
347 if (conv != option->getString()) {
348 const bool hadDefault = option->isDefault();
349 option->set(conv);
350 if (hadDefault) {
351 option->resetDefault();
352 }
353 }
354 }
355 }
356 }
357
358
359 bool
isUsableFileList(const std::string & name) const360 OptionsCont::isUsableFileList(const std::string& name) const {
361 Option* o = getSecure(name);
362 // check whether the option is set
363 // return false i not
364 if (!o->isSet()) {
365 return false;
366 }
367 // check whether the list of files is valid
368 bool ok = true;
369 std::vector<std::string> files = getStringVector(name);
370 if (files.size() == 0) {
371 WRITE_ERROR("The file list for '" + name + "' is empty.");
372 ok = false;
373 }
374 for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
375 if (!FileHelpers::isReadable(*fileIt)) {
376 if (*fileIt != "") {
377 WRITE_ERROR("File '" + *fileIt + "' is not accessible (" + std::strerror(errno) + ").");
378 ok = false;
379 } else {
380 WRITE_WARNING("Empty file name given; ignoring.");
381 }
382 }
383 }
384 return ok;
385 }
386
387
388 bool
checkDependingSuboptions(const std::string & name,const std::string & prefix) const389 OptionsCont::checkDependingSuboptions(const std::string& name, const std::string& prefix) const {
390 Option* o = getSecure(name);
391 if (o->isSet()) {
392 return true;
393 }
394 bool ok = true;
395 std::vector<std::string> seenSynonymes;
396 for (KnownContType::const_iterator i = myValues.begin(); i != myValues.end(); i++) {
397 if (std::find(seenSynonymes.begin(), seenSynonymes.end(), (*i).first) != seenSynonymes.end()) {
398 continue;
399 }
400 if ((*i).second->isSet() && !(*i).second->isDefault() && (*i).first.find(prefix) == 0) {
401 WRITE_ERROR("Option '" + (*i).first + "' needs option '" + name + "'.");
402 std::vector<std::string> synonymes = getSynonymes((*i).first);
403 std::copy(synonymes.begin(), synonymes.end(), std::back_inserter(seenSynonymes));
404 ok = false;
405 }
406 }
407 return ok;
408 }
409
410
411 void
reportDoubleSetting(const std::string & arg) const412 OptionsCont::reportDoubleSetting(const std::string& arg) const {
413 std::vector<std::string> synonymes = getSynonymes(arg);
414 std::ostringstream s;
415 s << "A value for the option '" + arg + "' was already set.\n Possible synonymes: ";
416 for (std::vector<std::string>::iterator i = synonymes.begin(); i != synonymes.end();) {
417 s << (*i);
418 i++;
419 if (i != synonymes.end()) {
420 s << ", ";
421 }
422 }
423 WRITE_ERROR(s.str());
424 }
425
426
427 std::string
convertChar(char abbr) const428 OptionsCont::convertChar(char abbr) const {
429 char buf[2];
430 buf[0] = abbr;
431 buf[1] = 0;
432 std::string s(buf);
433 return s;
434 }
435
436
437 bool
isBool(const std::string & name) const438 OptionsCont::isBool(const std::string& name) const {
439 Option* o = getSecure(name);
440 return o->isBool();
441 }
442
443
444 void
resetWritable()445 OptionsCont::resetWritable() {
446 for (ItemAddressContType::iterator i = myAddresses.begin(); i != myAddresses.end(); i++) {
447 (*i)->resetWritable();
448 }
449 }
450
451
452 bool
isWriteable(const std::string & name)453 OptionsCont::isWriteable(const std::string& name) {
454 Option* o = getSecure(name);
455 return o->isWriteable();
456 }
457
458
459 void
clear()460 OptionsCont::clear() {
461 ItemAddressContType::iterator i;
462 for (i = myAddresses.begin(); i != myAddresses.end(); i++) {
463 delete (*i);
464 }
465 myAddresses.clear();
466 myValues.clear();
467 mySubTopics.clear();
468 mySubTopicEntries.clear();
469 }
470
471
472 void
addDescription(const std::string & name,const std::string & subtopic,const std::string & description)473 OptionsCont::addDescription(const std::string& name,
474 const std::string& subtopic,
475 const std::string& description) {
476 Option* o = getSecure(name);
477 assert(o != 0);
478 assert(find(mySubTopics.begin(), mySubTopics.end(), subtopic) != mySubTopics.end());
479 o->setDescription(description);
480 mySubTopicEntries[subtopic].push_back(name);
481 }
482
483
484 void
setApplicationName(const std::string & appName,const std::string & fullName)485 OptionsCont::setApplicationName(const std::string& appName,
486 const std::string& fullName) {
487 myAppName = appName;
488 myFullName = fullName;
489 }
490
491
492 void
setApplicationDescription(const std::string & appDesc)493 OptionsCont::setApplicationDescription(const std::string& appDesc) {
494 myAppDescription = appDesc;
495 }
496
497
498 void
addCallExample(const std::string & example,const std::string & desc)499 OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
500 myCallExamples.push_back(std::make_pair(example, desc));
501 }
502
503
504 void
setAdditionalHelpMessage(const std::string & add)505 OptionsCont::setAdditionalHelpMessage(const std::string& add) {
506 myAdditionalMessage = add;
507 }
508
509
510 void
addCopyrightNotice(const std::string & copyrightLine)511 OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
512 myCopyrightNotices.push_back(copyrightLine);
513 }
514
515
516 void
clearCopyrightNotices()517 OptionsCont::clearCopyrightNotices() {
518 myCopyrightNotices.clear();
519 }
520
521
522 void
addOptionSubTopic(const std::string & topic)523 OptionsCont::addOptionSubTopic(const std::string& topic) {
524 mySubTopics.push_back(topic);
525 mySubTopicEntries[topic] = std::vector<std::string>();
526 }
527
528
529 void
splitLines(std::ostream & os,std::string what,int offset,int nextOffset)530 OptionsCont::splitLines(std::ostream& os, std::string what,
531 int offset, int nextOffset) {
532 while (what.length() > 0) {
533 if ((int)what.length() > 79 - offset) {
534 std::string::size_type splitPos = what.rfind(';', 79 - offset);
535 if (splitPos == std::string::npos) {
536 splitPos = what.rfind(' ', 79 - offset);
537 } else {
538 splitPos++;
539 }
540 if (splitPos != std::string::npos) {
541 os << what.substr(0, splitPos) << std::endl;
542 what = what.substr(splitPos);
543 for (int r = 0; r < nextOffset + 1; ++r) {
544 os << ' ';
545 }
546 } else {
547 os << what;
548 what = "";
549 }
550 offset = nextOffset;
551 } else {
552 os << what;
553 what = "";
554 }
555 }
556 os << std::endl;
557 }
558
559
560 bool
processMetaOptions(bool missingOptions)561 OptionsCont::processMetaOptions(bool missingOptions) {
562 if (missingOptions) {
563 // no options are given
564 std::cout << myFullName << std::endl;
565 std::cout << " Build features: " << HAVE_ENABLED << std::endl;
566 for (std::vector<std::string>::const_iterator it =
567 myCopyrightNotices.begin(); it != myCopyrightNotices.end(); ++it) {
568 std::cout << " " << *it << std::endl;
569 }
570 std::cout << " License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>\n";
571 std::cout << " Use --help to get the list of options." << std::endl;
572 return true;
573 }
574
575 myWriteLicense = getBool("write-license");
576 // check whether the help shall be printed
577 if (getBool("help")) {
578 std::cout << myFullName << std::endl;
579 for (std::vector<std::string>::const_iterator it =
580 myCopyrightNotices.begin(); it != myCopyrightNotices.end(); ++it) {
581 std::cout << " " << *it << std::endl;
582 }
583 printHelp(std::cout);
584 return true;
585 }
586 // check whether the help shall be printed
587 if (getBool("version")) {
588 std::cout << myFullName << std::endl;
589 std::cout << " Build features: " << HAVE_ENABLED << std::endl;
590 for (std::vector<std::string>::const_iterator it =
591 myCopyrightNotices.begin(); it != myCopyrightNotices.end(); ++it) {
592 std::cout << " " << *it << std::endl;
593 }
594 std::cout << "\n" << myFullName << " is part of SUMO.\n";
595 std::cout << "This program and the accompanying materials\n";
596 std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
597 std::cout << "which accompanies this distribution, and is available at\n";
598 std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
599 std::cout << "SPDX-License-Identifier: EPL-2.0" << std::endl;
600 return true;
601 }
602 // check whether the settings shall be printed
603 if (exists("print-options") && getBool("print-options")) {
604 std::cout << (*this);
605 }
606 // check whether something has to be done with options
607 // whether the current options shall be saved
608 if (isSet("save-configuration", false)) { // sumo-gui does not register these
609 if (getString("save-configuration") == "-" || getString("save-configuration") == "stdout") {
610 writeConfiguration(std::cout, true, false, getBool("save-commented"));
611 return true;
612 }
613 std::ofstream out(getString("save-configuration").c_str());
614 if (!out.good()) {
615 throw ProcessError("Could not save configuration to '" + getString("save-configuration") + "'");
616 } else {
617 writeConfiguration(out, true, false, getBool("save-commented"));
618 if (getBool("verbose")) {
619 WRITE_MESSAGE("Written configuration to '" + getString("save-configuration") + "'");
620 }
621 return true;
622 }
623 }
624 // whether the template shall be saved
625 if (isSet("save-template", false)) { // sumo-gui does not register these
626 if (getString("save-template") == "-" || getString("save-template") == "stdout") {
627 writeConfiguration(std::cout, false, true, getBool("save-commented"));
628 return true;
629 }
630 std::ofstream out(getString("save-template").c_str());
631 if (!out.good()) {
632 throw ProcessError("Could not save template to '" + getString("save-template") + "'");
633 } else {
634 writeConfiguration(out, false, true, getBool("save-commented"));
635 if (getBool("verbose")) {
636 WRITE_MESSAGE("Written template to '" + getString("save-template") + "'");
637 }
638 return true;
639 }
640 }
641 if (isSet("save-schema", false)) { // sumo-gui does not register these
642 if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
643 writeSchema(std::cout);
644 return true;
645 }
646 std::ofstream out(getString("save-schema").c_str());
647 if (!out.good()) {
648 throw ProcessError("Could not save schema to '" + getString("save-schema") + "'");
649 } else {
650 writeSchema(out);
651 if (getBool("verbose")) {
652 WRITE_MESSAGE("Written schema to '" + getString("save-schema") + "'");
653 }
654 return true;
655 }
656 }
657 return false;
658 }
659
660 void
printHelp(std::ostream & os)661 OptionsCont::printHelp(std::ostream& os) {
662 std::vector<std::string>::const_iterator i, j;
663 // print application description
664 splitLines(os, myAppDescription, 0, 0);
665 os << std::endl;
666
667 // check option sizes first
668 // we want to know how large the largest not-too-large-entry will be
669 int tooLarge = 40;
670 int maxSize = 0;
671 for (i = mySubTopics.begin(); i != mySubTopics.end(); ++i) {
672 const std::vector<std::string>& entries = mySubTopicEntries[*i];
673 for (j = entries.begin(); j != entries.end(); ++j) {
674 Option* o = getSecure(*j);
675 // name, two leading spaces and "--"
676 int csize = (int)j->length() + 2 + 4;
677 // abbreviation length ("-X, "->4chars) if any
678 const std::vector<std::string> synonymes = getSynonymes(*j);
679 for (std::vector<std::string>::const_iterator s = synonymes.begin(); s != synonymes.end(); ++s) {
680 if (s->length() == 1 && myDeprecatedSynonymes.count(*s) == 0) {
681 csize += 4;
682 break;
683 }
684 }
685 // the type name
686 if (!o->isBool()) {
687 csize += 1 + (int)o->getTypeName().length();
688 }
689 // divider
690 csize += 2;
691 if (csize < tooLarge && maxSize < csize) {
692 maxSize = csize;
693 }
694 }
695 }
696
697 const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
698 if (helpTopic != "") {
699 bool foundTopic = false;
700 for (const std::string& topic : mySubTopics) {
701 if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
702 foundTopic = true;
703 printHelpOnTopic(topic, tooLarge, maxSize, os);
704 }
705 }
706 if (!foundTopic) {
707 // print topic list
708 os << "Help Topics:" << std::endl;
709 for (std::string t : mySubTopics) {
710 os << " " << t << std::endl;
711 }
712 }
713 return;
714 }
715 // print usage BNF
716 os << "Usage: " << myAppName << " [OPTION]*" << std::endl;
717 // print additional text if any
718 if (myAdditionalMessage.length() > 0) {
719 os << myAdditionalMessage << std::endl << ' ' << std::endl;
720 }
721 // print the options
722 for (i = mySubTopics.begin(); i != mySubTopics.end(); ++i) {
723 printHelpOnTopic(*i, tooLarge, maxSize, os);
724 }
725 os << std::endl;
726 // print usage examples, calc size first
727 if (myCallExamples.size() != 0) {
728 os << "Examples:" << std::endl;
729 for (std::vector<std::pair<std::string, std::string> >::const_iterator e = myCallExamples.begin(); e != myCallExamples.end(); ++e) {
730 os << " " << myAppName << ' ' << e->first << std::endl;
731 os << " " << e->second << std::endl;
732 }
733 }
734 os << std::endl;
735 os << "Report bugs at <https://github.com/eclipse/sumo/issues>." << std::endl;
736 os << "Get in contact via <sumo@dlr.de>." << std::endl;
737 }
738
739 void
printHelpOnTopic(const std::string & topic,int tooLarge,int maxSize,std::ostream & os)740 OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
741 os << topic << " Options:" << std::endl;
742 for (std::string entry : mySubTopicEntries[topic]) {
743 // start length computation
744 int csize = (int)entry.length() + 2;
745 Option* o = getSecure(entry);
746 os << " ";
747 // write abbreviation if given
748 std::vector<std::string> synonymes = getSynonymes(entry);
749 for (std::vector<std::string>::const_iterator s = synonymes.begin(); s != synonymes.end(); ++s) {
750 if (s->length() == 1 && myDeprecatedSynonymes.count(*s) == 0) {
751 os << '-' << *s << ", ";
752 csize += 4;
753 break;
754 }
755 }
756 // write leading '-'/"--"
757 os << "--";
758 csize += 2;
759 // write the name
760 os << entry;
761 // write the type if not a bool option
762 if (!o->isBool()) {
763 os << ' ' << o->getTypeName();
764 csize += 1 + (int)o->getTypeName().length();
765 }
766 csize += 2;
767 // write the description formatting it
768 os << " ";
769 for (int r = maxSize; r > csize; --r) {
770 os << ' ';
771 }
772 int offset = csize > tooLarge ? csize : maxSize;
773 splitLines(os, o->getDescription(), offset, maxSize);
774 }
775 os << std::endl;
776 }
777
778 void
writeConfiguration(std::ostream & os,const bool filled,const bool complete,const bool addComments,const bool inComment) const779 OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
780 const bool complete, const bool addComments,
781 const bool inComment) const {
782 if (!inComment) {
783 writeXMLHeader(os, false);
784 }
785 os << "<configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/";
786 if (myAppName == "sumo-gui") {
787 os << "sumo";
788 } else if (myAppName == "netedit") {
789 os << "netconvert";
790 } else {
791 os << myAppName;
792 }
793 os << "Configuration.xsd\">" << std::endl << std::endl;
794 for (std::vector<std::string>::const_iterator i = mySubTopics.begin(); i != mySubTopics.end(); ++i) {
795 std::string subtopic = *i;
796 if (subtopic == "Configuration" && !complete) {
797 continue;
798 }
799 std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
800 std::transform(subtopic.begin(), subtopic.end(), subtopic.begin(), tolower);
801 const std::vector<std::string>& entries = mySubTopicEntries.find(*i)->second;
802 bool hadOne = false;
803 for (std::vector<std::string>::const_iterator j = entries.begin(); j != entries.end(); ++j) {
804 Option* o = getSecure(*j);
805 bool write = complete || (filled && !o->isDefault());
806 if (!write) {
807 continue;
808 }
809 if (!hadOne) {
810 os << " <" << subtopic << ">" << std::endl;
811 }
812 // add the comment if wished
813 if (addComments) {
814 os << " <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->" << std::endl;
815 }
816 // write the option and the value (if given)
817 os << " <" << *j << " value=\"";
818 if (o->isSet() && (filled || o->isDefault())) {
819 os << StringUtils::escapeXML(o->getValueString(), inComment);
820 }
821 if (complete) {
822 std::vector<std::string> synonymes = getSynonymes(*j);
823 if (!synonymes.empty()) {
824 os << "\" synonymes=\"";
825 for (std::vector<std::string>::const_iterator s = synonymes.begin(); s != synonymes.end(); ++s) {
826 if (s != synonymes.begin()) {
827 os << " ";
828 }
829 os << (*s);
830 }
831 }
832 os << "\" type=\"" << o->getTypeName();
833 if (!addComments) {
834 os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
835 }
836 }
837 os << "\"/>" << std::endl;
838 // append an endline if a comment was printed
839 if (addComments) {
840 os << std::endl;
841 }
842 hadOne = true;
843 }
844 if (hadOne) {
845 os << " </" << subtopic << ">" << std::endl << std::endl;
846 }
847 }
848 os << "</configuration>" << std::endl;
849 }
850
851
852 void
writeSchema(std::ostream & os)853 OptionsCont::writeSchema(std::ostream& os) {
854 writeXMLHeader(os, false);
855 os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n\n";
856 os << " <xsd:include schemaLocation=\"baseTypes.xsd\"/>\n";
857 os << " <xsd:element name=\"configuration\" type=\"configurationType\"/>\n\n";
858 os << " <xsd:complexType name=\"configurationType\">\n";
859 os << " <xsd:all>\n";
860 for (std::vector<std::string>::const_iterator i = mySubTopics.begin(); i != mySubTopics.end(); ++i) {
861 std::string subtopic = *i;
862 if (subtopic == "Configuration") {
863 continue;
864 }
865 std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
866 std::transform(subtopic.begin(), subtopic.end(), subtopic.begin(), tolower);
867 os << " <xsd:element name=\"" << subtopic << "\" type=\"" << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
868 }
869 os << " </xsd:all>\n";
870 os << " </xsd:complexType>\n\n";
871 for (std::vector<std::string>::const_iterator i = mySubTopics.begin(); i != mySubTopics.end(); ++i) {
872 std::string subtopic = *i;
873 if (subtopic == "Configuration") {
874 continue;
875 }
876 std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
877 std::transform(subtopic.begin(), subtopic.end(), subtopic.begin(), tolower);
878 os << " <xsd:complexType name=\"" << subtopic << "TopicType\">\n";
879 os << " <xsd:all>\n";
880 const std::vector<std::string>& entries = mySubTopicEntries[*i];
881 for (std::vector<std::string>::const_iterator j = entries.begin(); j != entries.end(); ++j) {
882 Option* o = getSecure(*j);
883 std::string type = o->getTypeName();
884 std::transform(type.begin(), type.end(), type.begin(), tolower);
885 if (type == "int[]") {
886 type = "intArray";
887 }
888 os << " <xsd:element name=\"" << *j << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
889 }
890 os << " </xsd:all>\n";
891 os << " </xsd:complexType>\n\n";
892 }
893 os << "</xsd:schema>\n";
894 }
895
896
897 void
writeXMLHeader(std::ostream & os,const bool includeConfig) const898 OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
899 time_t rawtime;
900 char buffer [80];
901
902 os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
903 time(&rawtime);
904 strftime(buffer, 80, "<!-- generated on %c by ", localtime(&rawtime));
905 os << buffer << myFullName << "\n";
906 if (myWriteLicense) {
907 os << "This data file and the accompanying materials\n";
908 os << "are made available under the terms of the Eclipse Public License v2.0\n";
909 os << "which accompanies this distribution, and is available at\n";
910 os << "http://www.eclipse.org/legal/epl-v20.html\n";
911 os << "SPDX-License-Identifier: EPL-2.0\n";
912 }
913 if (includeConfig) {
914 writeConfiguration(os, true, false, false, true);
915 }
916 os << "-->\n\n";
917 }
918
919
920 std::vector<std::string>
getStringVector(const std::string & name) const921 OptionsCont::getStringVector(const std::string& name) const {
922 Option* o = getSecure(name);
923 std::string def = o->getString();
924 if (def.find(';') != std::string::npos && !myHaveInformedAboutDeprecatedDivider) {
925 WRITE_WARNING("Please note that using ';' as list separator is deprecated.\n From 1.0 onwards, only ',' will be accepted.");
926 myHaveInformedAboutDeprecatedDivider = true;
927 }
928 StringTokenizer st(def, ";,", true);
929 std::vector<std::string> ret = st.getVector();
930 for (std::vector<std::string>::iterator i = ret.begin(); i != ret.end(); ++i) {
931 (*i) = StringUtils::prune(*i);
932 }
933 return ret;
934 }
935
936
937 bool
isInStringVector(const std::string & optionName,const std::string & itemName)938 OptionsCont::isInStringVector(const std::string& optionName,
939 const std::string& itemName) {
940 if (isSet(optionName)) {
941 std::vector<std::string> values = getStringVector(optionName);
942 return std::find(values.begin(), values.end(), itemName) != values.end();
943 }
944 return false;
945 }
946
947
948 /****************************************************************************/
949