Eclipse SUMO - Simulation of Urban MObility
OptionsCont.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
21 // A storage for options (typed value containers)
22 /****************************************************************************/
23 #include <config.h>
24 
25 #include <map>
26 #include <string>
27 #include <exception>
28 #include <algorithm>
29 #include <vector>
30 #include <iostream>
31 #include <cstdlib>
32 #include <ctime>
33 #include <cstring>
34 #include <cerrno>
35 #include <iterator>
36 #include <sstream>
43 #include "Option.h"
44 #include "OptionsIO.h"
45 #include "OptionsCont.h"
46 
47 
48 // ===========================================================================
49 // static member definitions
50 // ===========================================================================
51 
54 
55 // ===========================================================================
56 // method definitions
57 // ===========================================================================
58 
61  return myOptions;
62 }
63 
64 
66  myCopyrightNotices.push_back(TL("Copyright (C) 2001-2024 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
67 }
68 
69 
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  file = FileHelpers::checkForRelativity(file, configuration);
355  try {
356  file = StringUtils::urlDecode(file);
357  } catch (NumberFormatException& e) {
358  WRITE_WARNING(toString(e.what()) + " when trying to decode filename '" + file + "'.");
359  }
360  }
361  StringVector rawList = StringTokenizer(addresse.second->getValueString(), ",").getVector();
362  for (auto& file : rawList) {
363  file = FileHelpers::checkForRelativity(file, configuration);
364  }
365  const std::string conv = joinToString(fileList, ',');
366  if (conv != joinToString(addresse.second->getStringVector(), ',')) {
367  const bool hadDefault = addresse.second->isDefault();
368  addresse.second->set(conv, joinToString(rawList, ','), false);
369  if (hadDefault) {
370  addresse.second->resetDefault();
371  }
372  }
373  }
374  }
375 }
376 
377 
378 bool
379 OptionsCont::isUsableFileList(const std::string& name) const {
380  Option* const o = getSecure(name);
381  if (!o->isSet()) {
382  return false;
383  }
384  // check whether the list of files is valid
385  bool ok = true;
386  std::vector<std::string> files = getStringVector(name);
387  if (files.size() == 0) {
388  WRITE_ERRORF(TL("The file list for '%' is empty."), name);
389  ok = false;
390  }
391  for (const auto& file : files) {
392  if (!FileHelpers::isReadable(file)) {
393  if (file != "") {
394  WRITE_ERRORF(TL("File '%' is not accessible (%)."), file, std::strerror(errno));
395  ok = false;
396  } else {
397  WRITE_WARNING(TL("Empty file name given; ignoring."));
398  }
399  }
400  }
401  return ok;
402 }
403 
404 
405 bool
406 OptionsCont::checkDependingSuboptions(const std::string& name, const std::string& prefix) const {
407  Option* o = getSecure(name);
408  if (o->isSet()) {
409  return true;
410  }
411  bool ok = true;
412  std::vector<std::string> seenSynonymes;
413  for (const auto& value : myValues) {
414  if (std::find(seenSynonymes.begin(), seenSynonymes.end(), value.first) != seenSynonymes.end()) {
415  continue;
416  }
417  if (value.second->isSet() && !value.second->isDefault() && value.first.find(prefix) == 0) {
418  WRITE_ERRORF(TL("Option '%' needs option '%'."), value.first, name);
419  std::vector<std::string> synonymes = getSynonymes(value.first);
420  std::copy(synonymes.begin(), synonymes.end(), std::back_inserter(seenSynonymes));
421  ok = false;
422  }
423  }
424  return ok;
425 }
426 
427 
428 void
429 OptionsCont::reportDoubleSetting(const std::string& arg) const {
430  std::vector<std::string> synonymes = getSynonymes(arg);
431  std::ostringstream s;
432  s << TLF("A value for the option '%' was already set.\n Possible synonymes: ", arg);
433  auto synonym = synonymes.begin();
434  while (synonym != synonymes.end()) {
435  s << (*synonym);
436  synonym++;
437  if (synonym != synonymes.end()) {
438  s << ", ";
439  }
440  }
441  WRITE_ERROR(s.str());
442 }
443 
444 
445 std::string
446 OptionsCont::convertChar(char abbr) const {
447  char buf[2];
448  buf[0] = abbr;
449  buf[1] = 0;
450  std::string s(buf);
451  return s;
452 }
453 
454 
455 bool
456 OptionsCont::isBool(const std::string& name) const {
457  Option* o = getSecure(name);
458  return o->isBool();
459 }
460 
461 
462 void
464  for (const auto& addresse : myAddresses) {
465  addresse.second->resetWritable();
466  }
467 }
468 
469 
470 void
472  for (const auto& addresse : myAddresses) {
473  addresse.second->resetDefault();
474  }
475 }
476 
477 
478 void
479 OptionsCont::resetDefault(const std::string& name) {
480  getSecure(name)->resetDefault();
481 }
482 
483 
484 bool
485 OptionsCont::isWriteable(const std::string& name) {
486  Option* o = getSecure(name);
487  return o->isWriteable();
488 }
489 
490 
491 void
493  // delete only address (because synonyms placed in values aim to the same Option)
494  for (const auto& addresse : myAddresses) {
495  delete addresse.second;
496  }
497  myAddresses.clear();
498  myValues.clear();
499  mySubTopics.clear();
500  mySubTopicEntries.clear();
501 }
502 
503 
504 void
505 OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
506  const std::string& description) {
507  Option* o = getSecure(name);
508  if (o == nullptr) {
509  throw ProcessError("Option doesn't exist");
510  }
511  if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
512  throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
513  }
514  o->setDescription(description);
515  o->setSubtopic(subtopic);
516  mySubTopicEntries[subtopic].push_back(name);
517 }
518 
519 
520 void
521 OptionsCont::setFurtherAttributes(const std::string& name, const std::string& subtopic, bool required, bool positional, const std::string& listSep) {
522  Option* o = getSecure(name);
523  if (o == nullptr) {
524  throw ProcessError("Option doesn't exist");
525  }
526  if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
527  throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
528  }
529  if (required) {
530  o->setRequired();
531  }
532  if (positional) {
533  o->setPositional();
534  }
535  o->setListSeparator(listSep);
536 }
537 
538 
539 void
540 OptionsCont::setApplicationName(const std::string& appName,
541  const std::string& fullName) {
542  myAppName = appName;
543  myFullName = fullName;
544 }
545 
546 
547 void
548 OptionsCont::setApplicationDescription(const std::string& appDesc) {
549  myAppDescription = appDesc;
550 }
551 
552 
553 void
554 OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
555  myCallExamples.push_back(std::make_pair(example, desc));
556 }
557 
558 
559 void
560 OptionsCont::setAdditionalHelpMessage(const std::string& add) {
561  myAdditionalMessage = add;
562 }
563 
564 
565 void
566 OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
567  myCopyrightNotices.push_back(copyrightLine);
568 }
569 
570 
571 void
573  myCopyrightNotices.clear();
574 }
575 
576 
577 void
578 OptionsCont::addOptionSubTopic(const std::string& topic) {
579  mySubTopics.push_back(topic);
580  mySubTopicEntries[topic] = std::vector<std::string>();
581 }
582 
583 
584 void
585 OptionsCont::splitLines(std::ostream& os, std::string what,
586  int offset, int nextOffset) {
587  while (what.length() > 0) {
588  if ((int)what.length() > 79 - offset) {
589  std::string::size_type splitPos = what.rfind(';', 79 - offset);
590  if (splitPos == std::string::npos) {
591  splitPos = what.rfind(' ', 79 - offset);
592  } else {
593  splitPos++;
594  }
595  if (splitPos != std::string::npos) {
596  os << what.substr(0, splitPos) << std::endl;
597  what = what.substr(splitPos + 1);
598  for (int r = 0; r < (nextOffset + 1); ++r) {
599  os << ' ';
600  }
601  } else {
602  os << what;
603  what = "";
604  }
605  offset = nextOffset;
606  } else {
607  os << what;
608  what = "";
609  }
610  }
611  os << std::endl;
612 }
613 
614 
615 bool
616 OptionsCont::processMetaOptions(bool missingOptions) {
617  MsgHandler::setupI18n(getString("language"));
618  if (missingOptions) {
619  // no options are given
620  std::cout << myFullName << std::endl;
621  std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
622  for (const auto& copyrightNotice : myCopyrightNotices) {
623  std::cout << " " << copyrightNotice.data() << std::endl;
624  }
625  std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
626  std::cout << TL(" Use --help to get the list of options.") << std::endl;
627  return true;
628  }
629 
630  // check whether the help shall be printed
631  if (getBool("help")) {
632  std::cout << myFullName << std::endl;
633  for (const auto& copyrightNotice : myCopyrightNotices) {
634  std::cout << " " << copyrightNotice.data() << std::endl;
635  }
636  printHelp(std::cout);
637  return true;
638  }
639  // check whether the help shall be printed
640  if (getBool("version")) {
641  std::cout << myFullName << std::endl;
642  std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
643  for (const auto& copyrightNotice : myCopyrightNotices) {
644  std::cout << " " << copyrightNotice.data() << std::endl;
645  }
646  std::cout << "\n" << myFullName << " is part of SUMO.\n";
647  std::cout << "This program and the accompanying materials\n";
648  std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
649  std::cout << "which accompanies this distribution, and is available at\n";
650  std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
651  std::cout << "This program may also be made available under the following Secondary\n";
652  std::cout << "Licenses when the conditions for such availability set forth in the Eclipse\n";
653  std::cout << "Public License 2.0 are satisfied: GNU General Public License, version 2\n";
654  std::cout << "or later which is available at\n";
655  std::cout << "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n";
656  std::cout << "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later" << std::endl;
657  return true;
658  }
659  // check whether the settings shall be printed
660  if (getBool("print-options")) {
661  std::cout << (*this);
662  }
663  // check whether something has to be done with options
664  // whether the current options shall be saved
665  if (isSet("save-configuration")) {
666  const std::string& configPath = getString("save-configuration");
667  if (configPath == "-" || configPath == "stdout") {
668  writeConfiguration(std::cout, true, false, getBool("save-commented"));
669  return true;
670  }
671  std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
672  if (!out.good()) {
673  throw ProcessError(TLF("Could not save configuration to '%'", configPath));
674  } else {
675  writeConfiguration(out, true, false, getBool("save-commented"), configPath);
676  if (getBool("verbose")) {
677  WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
678  }
679  return true;
680  }
681  }
682  // whether the template shall be saved
683  if (isSet("save-template")) {
684  if (getString("save-template") == "-" || getString("save-template") == "stdout") {
685  writeConfiguration(std::cout, false, true, getBool("save-commented"));
686  return true;
687  }
688  std::ofstream out(StringUtils::transcodeToLocal(getString("save-template")).c_str());
689  if (!out.good()) {
690  throw ProcessError(TLF("Could not save template to '%'", getString("save-template")));
691  } else {
692  writeConfiguration(out, false, true, getBool("save-commented"));
693  if (getBool("verbose")) {
694  WRITE_MESSAGEF(TL("Written template to '%'"), getString("save-template"));
695  }
696  return true;
697  }
698  }
699  if (isSet("save-schema")) {
700  if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
701  writeSchema(std::cout);
702  return true;
703  }
704  std::ofstream out(StringUtils::transcodeToLocal(getString("save-schema")).c_str());
705  if (!out.good()) {
706  throw ProcessError(TLF("Could not save schema to '%'", getString("save-schema")));
707  } else {
708  writeSchema(out);
709  if (getBool("verbose")) {
710  WRITE_MESSAGEF(TL("Written schema to '%'"), getString("save-schema"));
711  }
712  return true;
713  }
714  }
715  return false;
716 }
717 
718 
719 const std::vector<std::string>&
721  return mySubTopics;
722 }
723 
724 
725 std::vector<std::string>
726 OptionsCont::getSubTopicsEntries(const std::string& subtopic) const {
727  if (mySubTopicEntries.count(subtopic) > 0) {
728  return mySubTopicEntries.find(subtopic)->second;
729  } else {
730  return std::vector<std::string>();
731  }
732 }
733 
734 
735 std::string
736 OptionsCont::getTypeName(const std::string name) {
737  return getSecure(name)->getTypeName();
738 }
739 
740 
741 const std::string&
743  return myFullName;
744 }
745 
746 
747 bool
749  return myAddresses.size() == 0;
750 }
751 
752 
753 std::vector<std::pair<std::string, Option*> >::const_iterator
755  return myAddresses.cbegin();
756 }
757 
758 
759 std::vector<std::pair<std::string, Option*> >::const_iterator
761  return myAddresses.cend();
762 }
763 
764 
765 void
766 OptionsCont::printHelp(std::ostream& os) {
767  // print application description
768  splitLines(os, TL(myAppDescription.c_str()), 0, 0);
769  os << std::endl;
770 
771  // check option sizes first
772  // we want to know how large the largest not-too-large-entry will be
773  int tooLarge = 40;
774  int maxSize = 0;
775  for (const auto& subTopic : mySubTopics) {
776  for (const auto& entry : mySubTopicEntries[subTopic]) {
777  Option* o = getSecure(entry);
778  // name, two leading spaces and "--"
779  int csize = (int)entry.length() + 2 + 4;
780  // abbreviation length ("-X, "->4chars) if any
781  const auto synonymes = getSynonymes(entry);
782  for (const auto& synonym : synonymes) {
783  if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
784  csize += 4;
785  break;
786  }
787  }
788  // the type name
789  if (!o->isBool()) {
790  csize += 1 + (int)o->getTypeName().length();
791  }
792  // divider
793  csize += 2;
794  if (csize < tooLarge && maxSize < csize) {
795  maxSize = csize;
796  }
797  }
798  }
799 
800  const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
801  if (helpTopic != "") {
802  bool foundTopic = false;
803  for (const auto& topic : mySubTopics) {
804  if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
805  foundTopic = true;
806  printHelpOnTopic(topic, tooLarge, maxSize, os);
807  }
808  }
809  if (!foundTopic) {
810  // print topic list
811  os << "Help Topics:" << std::endl;
812  for (std::string t : mySubTopics) {
813  os << " " << t << std::endl;
814  }
815  }
816  return;
817  }
818  // print usage BNF
819  os << "Usage: " << myAppName << " [OPTION]*" << std::endl;
820  // print additional text if any
821  if (myAdditionalMessage.length() > 0) {
822  os << myAdditionalMessage << std::endl << ' ' << std::endl;
823  }
824  // print the options
825  for (const auto& subTopic : mySubTopics) {
826  printHelpOnTopic(subTopic, tooLarge, maxSize, os);
827  }
828  os << std::endl;
829  // print usage examples, calc size first
830  if (myCallExamples.size() != 0) {
831  os << "Examples:" << std::endl;
832  for (const auto& callExample : myCallExamples) {
833  os << " " << myAppName << ' ' << callExample.first << std::endl;
834  os << " " << callExample.second << std::endl;
835  }
836  }
837  os << std::endl;
838  os << "Report bugs at <https://github.com/eclipse/sumo/issues>." << std::endl;
839  os << "Get in contact via <sumo@dlr.de>." << std::endl;
840 }
841 
842 
843 void
844 OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
845  os << topic << " Options:" << std::endl;
846  for (const auto& entry : mySubTopicEntries[topic]) {
847  // start length computation
848  int csize = (int)entry.length() + 2;
849  Option* o = getSecure(entry);
850  os << " ";
851  // write abbreviation if given
852  const auto synonymes = getSynonymes(entry);
853  for (const auto& synonym : synonymes) {
854  if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
855  os << '-' << synonym << ", ";
856  csize += 4;
857  break;
858  }
859  }
860  // write leading '-'/"--"
861  os << "--";
862  csize += 2;
863  // write the name
864  os << entry;
865  // write the type if not a bool option
866  if (!o->isBool()) {
867  os << ' ' << o->getTypeName();
868  csize += 1 + (int)o->getTypeName().length();
869  }
870  csize += 2;
871  // write the description formatting it
872  os << " ";
873  for (int r = maxSize; r > csize; --r) {
874  os << ' ';
875  }
876  int offset = csize > tooLarge ? csize : maxSize;
877  splitLines(os, o->getDescription(), offset, maxSize);
878  }
879  os << std::endl;
880 }
881 
882 
883 void
884 OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
885  const bool complete, const bool addComments, const std::string& relativeTo,
886  const bool forceRelative, const bool inComment) const {
887  if (!inComment) {
888  writeXMLHeader(os, false);
889  }
890  os << "<configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/";
891  if (myAppName == "sumo-gui") {
892  os << "sumo";
893  } else {
894  os << myAppName;
895  }
896  os << "Configuration.xsd\">" << std::endl << std::endl;
897  for (std::string subtopic : mySubTopics) {
898  if (subtopic == "Configuration" && !complete) {
899  continue;
900  }
901  const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
902  std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
903  subtopic = StringUtils::to_lower_case(subtopic);
904  bool hadOne = false;
905  for (const std::string& name : entries) {
906  Option* o = getSecure(name);
907  bool write = complete || (filled && !o->isDefault());
908  if (!write) {
909  continue;
910  }
911  if (name == "registry-viewport" && !complete) {
912  continue;
913  }
914  if (!hadOne) {
915  os << " <" << subtopic << ">" << std::endl;
916  }
917  // add the comment if wished
918  if (addComments) {
919  os << " <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->" << std::endl;
920  }
921  // write the option and the value (if given)
922  os << " <" << name << " value=\"";
923  if (o->isSet() && (filled || o->isDefault())) {
924  if (o->isFileName() && relativeTo != "") {
925  StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
926  for (auto& file : fileList) {
928  StringUtils::urlEncode(file, " ;%"),
929  StringUtils::urlEncode(relativeTo, " ;%"),
930  forceRelative || getBool("save-configuration.relative"));
931  }
932  os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
933  } else {
934  os << StringUtils::escapeXML(o->getValueString(), inComment);
935  }
936  }
937  if (complete) {
938  std::vector<std::string> synonymes = getSynonymes(name);
939  if (!synonymes.empty()) {
940  os << "\" synonymes=\"";
941  for (auto synonym = synonymes.begin(); synonym != synonymes.end(); synonym++) {
942  if (synonym != synonymes.begin()) {
943  os << " ";
944  }
945  os << (*synonym);
946  }
947  }
948  os << "\" type=\"" << o->getTypeName();
949  if (!addComments) {
950  os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
951  }
952  }
953  os << "\"/>" << std::endl;
954  // append an endline if a comment was printed
955  if (addComments) {
956  os << std::endl;
957  }
958  hadOne = true;
959  }
960  if (hadOne) {
961  os << " </" << subtopic << ">" << std::endl << std::endl;
962  }
963  }
964  os << "</configuration>" << std::endl;
965 }
966 
967 
968 void
969 OptionsCont::writeSchema(std::ostream& os) {
970  writeXMLHeader(os, false);
971  os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n\n";
972  os << " <xsd:include schemaLocation=\"baseTypes.xsd\"/>\n";
973  os << " <xsd:element name=\"configuration\" type=\"configurationType\"/>\n\n";
974  os << " <xsd:complexType name=\"configurationType\">\n";
975  os << " <xsd:all>\n";
976  for (std::string subtopic : mySubTopics) {
977  if (subtopic == "Configuration") {
978  continue;
979  }
980  std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
981  subtopic = StringUtils::to_lower_case(subtopic);
982  os << " <xsd:element name=\"" << subtopic << "\" type=\"" << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
983  }
984  os << " </xsd:all>\n";
985  os << " </xsd:complexType>\n\n";
986  for (std::string subtopic : mySubTopics) {
987  if (subtopic == "Configuration") {
988  continue;
989  }
990  const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
991  std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
992  subtopic = StringUtils::to_lower_case(subtopic);
993  os << " <xsd:complexType name=\"" << subtopic << "TopicType\">\n";
994  os << " <xsd:all>\n";
995  for (const auto& entry : entries) {
996  Option* o = getSecure(entry);
997  std::string type = o->getTypeName();
998  type = StringUtils::to_lower_case(type);
999  if (type == "int[]") {
1000  type = "intArray";
1001  }
1002  if (type == "str[]") {
1003  type = "strArray";
1004  }
1005  os << " <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
1006  }
1007  os << " </xsd:all>\n";
1008  os << " </xsd:complexType>\n\n";
1009  }
1010  os << "</xsd:schema>\n";
1011 }
1012 
1013 
1014 void
1015 OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
1016  time_t rawtime;
1017  char buffer [80];
1018 
1019  os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
1020  time(&rawtime);
1021  strftime(buffer, 80, "<!-- generated on %F %T by ", localtime(&rawtime));
1022  os << buffer << myFullName << "\n";
1023  if (getBool("write-license")) {
1024  os << "This data file and the accompanying materials\n"
1025  "are made available under the terms of the Eclipse Public License v2.0\n"
1026  "which accompanies this distribution, and is available at\n"
1027  "http://www.eclipse.org/legal/epl-v20.html\n"
1028  "This file may also be made available under the following Secondary\n"
1029  "Licenses when the conditions for such availability set forth in the Eclipse\n"
1030  "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
1031  "or later which is available at\n"
1032  "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
1033  "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
1034  }
1035  if (includeConfig) {
1036  writeConfiguration(os, true, false, false, "", false, true);
1037  }
1038  os << "-->\n\n";
1039 }
1040 
1041 
1042 bool
1043 OptionsCont::isInStringVector(const std::string& optionName,
1044  const std::string& itemName) const {
1045  if (isSet(optionName)) {
1046  std::vector<std::string> values = getStringVector(optionName);
1047  return std::find(values.begin(), values.end(), itemName) != values.end();
1048  }
1049  return false;
1050 }
1051 
1052 /****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition: MsgHandler.h:298
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:304
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define TL(string)
Definition: MsgHandler.h:315
#define TLF(string,...)
Definition: MsgHandler.h:317
std::vector< std::string > StringVector
Definition of a vector of strings.
Definition: Option.h:42
std::vector< int > IntVector
Definition of a vector of ints.
Definition: Option.h:37
std::ostream & operator<<(std::ostream &os, const OptionsCont &oc)
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static std::string fixRelative(const std::string &filename, const std::string &basePath, const bool force, std::string curDir="")
Fixes the relative path for the given filename in relation to the basePath (usually a config file).
static std::string checkForRelativity(const std::string &filename, const std::string &basePath)
Returns the path from a configuration so that it is accessable from the current working directory.
static bool isReadable(std::string path)
Checks whether the given file is readable.
Definition: FileHelpers.cpp:51
static void setupI18n(const std::string &locale="")
set up gettext stuff
Definition: MsgHandler.cpp:280
A class representing a single program option.
Definition: Option.h:72
bool isWriteable() const
Returns the information whether the option may be set a further time.
Definition: Option.cpp:184
bool isSet() const
returns the information whether this options holds a valid value
Definition: Option.cpp:53
virtual bool isDefault() const
Returns the information whether the option holds the default value.
Definition: Option.cpp:112
void setRequired()
mark option as required
Definition: Option.cpp:220
virtual std::string getString() const
Returns the stored string value.
Definition: Option.cpp:71
virtual const IntVector & getIntVector() const
Returns the stored integer vector.
Definition: Option.cpp:83
const std::string & getDescription() const
Returns the description of what this option does.
Definition: Option.cpp:202
void setListSeparator(const std::string &listSep)
set list separator
Definition: Option.cpp:240
virtual bool isFileName() const
Returns the information whether this option is a file name.
Definition: Option.cpp:136
virtual const StringVector & getStringVector() const
Returns the stored string vector.
Definition: Option.cpp:89
void setDescription(const std::string &desc)
Sets the description of what this option does.
Definition: Option.cpp:208
virtual const std::string & getTypeName() const
Returns the mml-type name of this option.
Definition: Option.cpp:257
virtual int getInt() const
Returns the stored integer value.
Definition: Option.cpp:65
virtual double getFloat() const
Returns the stored double value.
Definition: Option.cpp:59
virtual bool getBool() const
Returns the stored boolean value.
Definition: Option.cpp:77
void setPositional()
mark option as positional
Definition: Option.cpp:230
void resetDefault()
Resets the option to be on its default value.
Definition: Option.cpp:196
const std::string & getSubTopic() const
Returns the subtopic to which this option belongs.
Definition: Option.cpp:245
virtual bool set(const std::string &v, const std::string &orig, const bool append)=0
Stores the given value.
virtual bool isBool() const
Returns the information whether the option is a bool option.
Definition: Option.cpp:130
const std::string & getValueString() const
Returns the string-representation of the value.
Definition: Option.cpp:106
void setSubtopic(const std::string &subtopic)
Sets the subtopic to which this option belongs.
Definition: Option.cpp:251
A storage for options typed value containers)
Definition: OptionsCont.h:89
void setAdditionalHelpMessage(const std::string &add)
Sets an additional message to be printed at the begin of the help screen.
~OptionsCont()
Destructor.
Definition: OptionsCont.cpp:70
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
void resetDefault()
Resets all options to default.
void setFurtherAttributes(const std::string &name, const std::string &subtopic, bool required, bool positional, const std::string &listSep)
mark option as required
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
std::vector< std::pair< std::string, std::string > > myCallExamples
list of call examples
Definition: OptionsCont.h:720
bool isWriteable(const std::string &name)
Returns the information whether the named option may be set.
std::map< std::string, std::vector< std::string > > mySubTopicEntries
A map from subtopic to option.
Definition: OptionsCont.h:726
void writeXMLHeader(std::ostream &os, const bool includeConfig=true) const
Writes a standard XML header, including the configuration.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
void splitLines(std::ostream &os, std::string what, int offset, int nextOffset)
Writes the given string 'formatted'.
void setApplicationName(const std::string &appName, const std::string &fullName)
Sets the application name.
void printHelpOnTopic(const std::string &topic, int tooLarge, int maxSize, std::ostream &os)
Prints help on the given topic.
const std::string & getSubTopic(const std::string &name) const
Returns the option category.
std::map< std::string, Option * > myValues
option maps sorted by name (for adresses AND their synonyms)
Definition: OptionsCont.h:714
bool isEmpty() const
check if options container is empty
std::string myAdditionalMessage
Definition: OptionsCont.h:717
std::vector< std::string > myCopyrightNotices
Definition: OptionsCont.h:723
const std::vector< std::string > & getSubTopics() const
return the list of subtopics
std::vector< std::pair< std::string, Option * > > myAddresses
option-adresses
Definition: OptionsCont.h:711
const IntVector & getIntVector(const std::string &name) const
Returns the list of integer-value of the named option (only for Option_IntVector)
std::vector< std::string > getSynonymes(const std::string &name) const
Returns the synonymes of an option name.
void reportDoubleSetting(const std::string &arg) const
Reports an error that the option has already been set.
std::vector< std::string > mySubTopics
lists of option subtopics and copyright notices
Definition: OptionsCont.h:723
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
static OptionsCont EMPTY_OPTIONS
empty option container
Definition: OptionsCont.h:96
void addSynonyme(const std::string &name1, const std::string &name2, bool isDeprecated=false)
Adds a synonyme for an options name (any order)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool setDefault(const std::string &name, const std::string &value)
Sets the given value for the named option as new default value.
void doRegister(const std::string &name, Option *o)
Adds an option under the given name.
Definition: OptionsCont.cpp:76
bool exists(const std::string &name) const
Returns the information whether the named option is known.
const std::string & getFullName() const
get options full name
bool isBool(const std::string &name) const
Returns the information whether the option is a boolean option.
void addCopyrightNotice(const std::string &copyrightLine)
Adds a copyright notice to the help output.
std::string getTypeName(const std::string name)
return the type name for the given option
void writeSchema(std::ostream &os)
Writes the xml schema for the configuration.
void clear()
Removes all information from the container.
void setApplicationDescription(const std::string &appDesc)
Sets the application description.
std::vector< std::pair< std::string, Option * > >::const_iterator begin() const
get begin adresses iterator
bool set(const std::string &name, const std::string &value, const bool append=false)
Sets the given value for the named option.
void writeConfiguration(std::ostream &os, const bool filled, const bool complete, const bool addComments, const std::string &relativeTo="", const bool forceRelative=false, const bool inComment=false) const
Writes the configuration.
void clearCopyrightNotices()
Removes all copyright information.
std::string convertChar(char abbr) const
Converts an abbreviation into a name.
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
OptionsCont()
Constructor.
Definition: OptionsCont.cpp:65
void printHelp(std::ostream &os)
Prints the help.
std::vector< std::pair< std::string, Option * > >::const_iterator end() const
get begin adresses iterator
std::string getValueString(const std::string &name) const
Returns the string-value of the named option (all options)
std::string myAppDescription
Definition: OptionsCont.h:717
const std::string & getDescription(const std::string &name) const
Returns the option description.
bool setByRootElement(const std::string &name, const std::string &value)
Sets the given value for the option which can handle the given XML root.
std::map< std::string, bool > myDeprecatedSynonymes
A map from deprecated options to a bool indicating whether we warned about deprecation.
Definition: OptionsCont.h:732
static OptionsCont myOptions
The static options container used.
Definition: OptionsCont.h:708
bool checkDependingSuboptions(const std::string &name, const std::string &prefix) const
Checks whether an option is set, which has options with a prefix depending on it.
std::map< std::string, std::string > myXMLDefaults
A map from XML root element to option.
Definition: OptionsCont.h:729
std::string myAppName
some information on the application
Definition: OptionsCont.h:717
void resetWritable()
Resets all options to be writeable.
void addXMLDefault(const std::string &name, const std::string &xmlRoot="")
Adds an XML root element to handle by default. The special root "" denotes the default handler.
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
std::string myFullName
Definition: OptionsCont.h:717
std::vector< std::string > getSubTopicsEntries(const std::string &subtopic) const
return the list of entries for the given subtopic
Option * getSecure(const std::string &name) const
Returns the named option.
void relocateFiles(const std::string &configuration) const
Modifies file name options according to the configuration path.
bool isInStringVector(const std::string &optionName, const std::string &itemName) const
Returns the named option is a list of string values containing the specified item.
bool processMetaOptions(bool missingOptions)
Checks for help and configuration output, returns whether we should exit.
bool isUsableFileList(const std::string &name) const
Checks whether the named option is usable as a file list (with at least a single file)
void addCallExample(const std::string &example, const std::string &desc)
Add a call example.
static const std::chrono::time_point< std::chrono::system_clock > & getLoadTime()
Return the time stamp of the last init.
Definition: OptionsIO.h:101
static const std::string ENCODING
The encoding of parsed strings.
std::vector< std::string > getVector()
return vector of strings
static std::string urlEncode(const std::string &url, const std::string encodeWhich="")
encode url (stem from http://bogomip.net/blog/cpp-url-encoding-and-decoding/)
static std::string urlDecode(const std::string &encoded)
decode url (stem from http://bogomip.net/blog/cpp-url-encoding-and-decoding/)
static std::string to_lower_case(const std::string &str)
Transfers the content to lower case.
Definition: StringUtils.cpp:77
static std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
static std::string substituteEnvironment(const std::string &str, const std::chrono::time_point< std::chrono::system_clock > *const timeRef=nullptr)
Replaces an environment variable with its value (similar to bash); syntax for a variable is ${NAME}.
static std::string transcodeToLocal(const std::string &utf8String)
convert a string from UTF-8 to the local codepage
static std::string strerror()