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  const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
891  os << "<" << app << "Configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
892  << "xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/" << app << "Configuration.xsd\">\n\n";
893  for (std::string subtopic : mySubTopics) {
894  if (subtopic == "Configuration" && !complete) {
895  continue;
896  }
897  const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
898  std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
899  subtopic = StringUtils::to_lower_case(subtopic);
900  bool hadOne = false;
901  for (const std::string& name : entries) {
902  Option* o = getSecure(name);
903  bool write = complete || (filled && !o->isDefault());
904  if (!write) {
905  continue;
906  }
907  if (name == "registry-viewport" && !complete) {
908  continue;
909  }
910  if (!hadOne) {
911  os << " <" << subtopic << ">\n";
912  }
913  // add the comment if wished
914  if (addComments) {
915  os << " <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->\n";
916  }
917  // write the option and the value (if given)
918  os << " <" << name << " value=\"";
919  if (o->isSet() && (filled || o->isDefault())) {
920  if (o->isFileName() && relativeTo != "") {
921  StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
922  for (auto& file : fileList) {
923  if (StringUtils::startsWith(file, "${")) {
924  // there is an environment variable up front, assume it points to an absolute path
925  // not even forcing relativity makes sense here
926  file = StringUtils::urlEncode(file, " ;%");
927  } else {
929  StringUtils::urlEncode(file, " ;%"),
930  StringUtils::urlEncode(relativeTo, " ;%"),
931  forceRelative || getBool("save-configuration.relative"));
932  }
933  }
934  os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
935  } else {
936  os << StringUtils::escapeXML(o->getValueString(), inComment);
937  }
938  }
939  if (complete) {
940  std::vector<std::string> synonymes = getSynonymes(name);
941  if (!synonymes.empty()) {
942  os << "\" synonymes=\"";
943  for (auto synonym = synonymes.begin(); synonym != synonymes.end(); synonym++) {
944  if (synonym != synonymes.begin()) {
945  os << " ";
946  }
947  os << (*synonym);
948  }
949  }
950  os << "\" type=\"" << o->getTypeName();
951  if (!addComments) {
952  os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
953  }
954  }
955  os << "\"/>\n";
956  // append an endline if a comment was printed
957  if (addComments) {
958  os << "\n";
959  }
960  hadOne = true;
961  }
962  if (hadOne) {
963  os << " </" << subtopic << ">\n\n";
964  }
965  }
966  os << "</" << app << "Configuration>" << std::endl; // flushing seems like a good idea here
967 }
968 
969 
970 void
971 OptionsCont::writeSchema(std::ostream& os) {
972  const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
973  writeXMLHeader(os, false);
974  os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n\n";
975  os << " <xsd:include schemaLocation=\"baseTypes.xsd\"/>\n";
976  os << " <xsd:element name=\"" << app << "Configuration\" type=\"configurationType\"/>\n\n";
977  os << " <xsd:complexType name=\"configurationType\">\n";
978  os << " <xsd:all>\n";
979  for (std::string subtopic : mySubTopics) {
980  if (subtopic == "Configuration") {
981  continue;
982  }
983  std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
984  subtopic = StringUtils::to_lower_case(subtopic);
985  os << " <xsd:element name=\"" << subtopic << "\" type=\"" << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
986  }
987  os << " </xsd:all>\n";
988  os << " </xsd:complexType>\n\n";
989  for (std::string subtopic : mySubTopics) {
990  if (subtopic == "Configuration") {
991  continue;
992  }
993  const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
994  std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
995  subtopic = StringUtils::to_lower_case(subtopic);
996  os << " <xsd:complexType name=\"" << subtopic << "TopicType\">\n";
997  os << " <xsd:all>\n";
998  for (const auto& entry : entries) {
999  Option* o = getSecure(entry);
1000  std::string type = o->getTypeName();
1001  type = StringUtils::to_lower_case(type);
1002  if (type == "int[]") {
1003  type = "intArray";
1004  }
1005  if (type == "str[]") {
1006  type = "strArray";
1007  }
1008  os << " <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
1009  }
1010  os << " </xsd:all>\n";
1011  os << " </xsd:complexType>\n\n";
1012  }
1013  os << "</xsd:schema>\n";
1014 }
1015 
1016 
1017 void
1018 OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
1019  time_t rawtime;
1020  char buffer [80];
1021 
1022  os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
1023  time(&rawtime);
1024  strftime(buffer, 80, "<!-- generated on %F %T by ", localtime(&rawtime));
1025  os << buffer << myFullName << "\n";
1026  if (getBool("write-license")) {
1027  os << "This data file and the accompanying materials\n"
1028  "are made available under the terms of the Eclipse Public License v2.0\n"
1029  "which accompanies this distribution, and is available at\n"
1030  "http://www.eclipse.org/legal/epl-v20.html\n"
1031  "This file may also be made available under the following Secondary\n"
1032  "Licenses when the conditions for such availability set forth in the Eclipse\n"
1033  "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
1034  "or later which is available at\n"
1035  "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
1036  "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
1037  }
1038  if (includeConfig) {
1039  writeConfiguration(os, true, false, false, "", false, true);
1040  }
1041  os << "-->\n\n";
1042 }
1043 
1044 
1045 bool
1046 OptionsCont::isInStringVector(const std::string& optionName,
1047  const std::string& itemName) const {
1048  if (isSet(optionName)) {
1049  std::vector<std::string> values = getStringVector(optionName);
1050  return std::find(values.begin(), values.end(), itemName) != values.end();
1051  }
1052  return false;
1053 }
1054 
1055 /****************************************************************************/
#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:79
static std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static std::string substituteEnvironment(const std::string &str, const std::chrono::time_point< std::chrono::system_clock > *const timeRef=nullptr)
Replaces an environment variable with its value (similar to bash); syntax for a variable is ${NAME}.
static std::string transcodeToLocal(const std::string &utf8String)
convert a string from UTF-8 to the local codepage
static std::string strerror()