LCOV - code coverage report
Current view: top level - src/utils/options - OptionsCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 80.2 % 519 416
Test Date: 2026-03-27 16:39:44 Functions: 73.4 % 64 47

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    OptionsCont.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @author  Walter Bamberger
      19              : /// @date    Mon, 17 Dec 2001
      20              : ///
      21              : // A storage for options (typed value containers)
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <map>
      26              : #include <string>
      27              : #include <exception>
      28              : #include <algorithm>
      29              : #include <vector>
      30              : #include <iostream>
      31              : #include <cstdlib>
      32              : #include <ctime>
      33              : #include <cstring>
      34              : #include <cerrno>
      35              : #include <iterator>
      36              : #include <sstream>
      37              : #include <utils/common/UtilExceptions.h>
      38              : #include <utils/common/FileHelpers.h>
      39              : #include <utils/common/MsgHandler.h>
      40              : #include <utils/common/StdDefs.h>
      41              : #include <utils/common/StringTokenizer.h>
      42              : #include <utils/common/StringUtils.h>
      43              : #include <utils/xml/SUMOSAXAttributes.h>
      44              : #include "Option.h"
      45              : #include "OptionsIO.h"
      46              : #include "OptionsCont.h"
      47              : 
      48              : 
      49              : // ===========================================================================
      50              : // static member definitions
      51              : // ===========================================================================
      52              : 
      53              : OptionsCont OptionsCont::myOptions;
      54              : OptionsCont OptionsCont::EMPTY_OPTIONS;
      55              : 
      56              : // ===========================================================================
      57              : // method definitions
      58              : // ===========================================================================
      59              : 
      60              : OptionsCont&
      61    978356552 : OptionsCont::getOptions() {
      62    978356552 :     return myOptions;
      63              : }
      64              : 
      65              : 
      66       107322 : OptionsCont::OptionsCont() {
      67       107322 :     myCopyrightNotices.push_back(TL("Copyright (C) 2001-2026 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
      68       107322 : }
      69              : 
      70              : 
      71       107496 : OptionsCont::~OptionsCont() {
      72       107496 :     clear();
      73       214992 : }
      74              : 
      75              : 
      76              : void
      77     26999090 : OptionsCont::doRegister(const std::string& name, Option* o) {
      78              :     // first check that option isn't null
      79     26999090 :     if (o == nullptr) {
      80            0 :         throw ProcessError("Option cannot be null");
      81              :     }
      82              :     // now check that there isn't another addresse (or synonym) related with the option
      83     26999090 :     if (myValues.find(name) != myValues.end()) {
      84            0 :         throw ProcessError(name + " is an already used option name.");
      85              :     }
      86              :     // check if previously was inserted in addresses (to avoid synonyms in addresses)
      87              :     bool isSynonym = false;
      88   5835317266 :     for (const auto& addresse : myAddresses) {
      89   5808318176 :         if (addresse.second == o) {
      90              :             isSynonym = true;
      91              :         }
      92              :     }
      93     26999090 :     if (!isSynonym) {
      94     43902078 :         myAddresses.push_back(std::make_pair(name, o));
      95              :     }
      96              :     // insert in values
      97     26999090 :     myValues[name] = o;
      98     26999090 : }
      99              : 
     100              : 
     101              : void
     102      1414655 : OptionsCont::doRegister(const std::string& name1, char abbr, Option* o) {
     103      1414655 :     doRegister(name1, o);
     104      1414655 :     doRegister(convertChar(abbr), o);
     105      1414655 : }
     106              : 
     107              : 
     108              : void
     109      3633396 : OptionsCont::addSynonyme(const std::string& name1, const std::string& name2, bool isDeprecated) {
     110              :     auto i1 = myValues.find(name1);
     111              :     auto i2 = myValues.find(name2);
     112      3633396 :     if (i1 == myValues.end() && i2 == myValues.end()) {
     113            0 :         throw ProcessError("Neither the option '" + name1 + "' nor the option '" + name2 + "' is known yet");
     114              :     }
     115      3633396 :     if (i1 != myValues.end() && i2 != myValues.end()) {
     116            0 :         if ((*i1).second == (*i2).second) {
     117              :             return;
     118              :         }
     119            0 :         throw ProcessError("Both options '" + name1 + "' and '" + name2 + "' do exist and differ.");
     120              :     }
     121      3633396 :     if (i1 == myValues.end() && i2 != myValues.end()) {
     122       115329 :         doRegister(name1, (*i2).second);
     123       115329 :         if (isDeprecated) {
     124            0 :             myDeprecatedSynonymes[name1] = false;
     125              :         }
     126              :     }
     127      3633396 :     if (i1 != myValues.end() && i2 == myValues.end()) {
     128      3518067 :         doRegister(name2, (*i1).second);
     129      3518067 :         if (isDeprecated) {
     130      1863714 :             myDeprecatedSynonymes[name2] = false;
     131              :         }
     132              :     }
     133              : }
     134              : 
     135              : 
     136              : void
     137       100429 : OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
     138       100429 :     myXMLDefaults[xmlRoot] = name;
     139       100429 : }
     140              : 
     141              : 
     142              : bool
     143    376901284 : OptionsCont::exists(const std::string& name) const {
     144    376901284 :     return myValues.count(name) > 0;
     145              : }
     146              : 
     147              : 
     148              : bool
     149   1031830683 : OptionsCont::isSet(const std::string& name, bool failOnNonExistant) const {
     150              :     auto i = myValues.find(name);
     151   1031830683 :     if (i == myValues.end()) {
     152          202 :         if (failOnNonExistant) {
     153            3 :             throw ProcessError(TLF("Internal request for unknown option '%'!", name));
     154              :         } else {
     155              :             return false;
     156              :         }
     157              :     }
     158   1031830481 :     return (*i).second->isSet();
     159              : }
     160              : 
     161              : 
     162              : bool
     163      2295136 : OptionsCont::isDefault(const std::string& name) const {
     164              :     auto i = myValues.find(name);
     165      2295136 :     if (i == myValues.end()) {
     166              :         return false;
     167              :     }
     168      2294687 :     return (*i).second->isDefault();
     169              : }
     170              : 
     171              : 
     172              : Option*
     173    355280271 : OptionsCont::getSecure(const std::string& name) const {
     174              :     const auto& valuesFinder = myValues.find(name);
     175    355280271 :     if (valuesFinder == myValues.end()) {
     176          165 :         throw ProcessError(TLF("No option with the name '%' exists.", name));
     177              :     }
     178              :     const auto& synonymFinder = myDeprecatedSynonymes.find(name);
     179    355280216 :     if ((synonymFinder != myDeprecatedSynonymes.end()) && !synonymFinder->second) {
     180              :         std::string defaultName;
     181         7018 :         for (const auto& subtopicEntry : mySubTopicEntries) {
     182       118755 :             for (const auto& value : subtopicEntry.second) {
     183              :                 const auto l = myValues.find(value);
     184       112938 :                 if ((l != myValues.end()) && (l->second == valuesFinder->second)) {
     185              :                     defaultName = value;
     186              :                     break;
     187              :                 }
     188              :             }
     189         7018 :             if (defaultName != "") {
     190              :                 break;
     191              :             }
     192              :         }
     193         3603 :         WRITE_WARNINGF(TL("Please note that '%' is deprecated.\n Use '%' instead."), name, defaultName);
     194         1201 :         synonymFinder->second = true;
     195              :     }
     196    355280216 :     return valuesFinder->second;
     197              : }
     198              : 
     199              : 
     200              : std::string
     201     15377917 : OptionsCont::getValueString(const std::string& name) const {
     202     15377917 :     Option* o = getSecure(name);
     203     15377917 :     return o->getValueString();
     204              : }
     205              : 
     206              : 
     207              : std::string
     208     28647313 : OptionsCont::getString(const std::string& name) const {
     209     28647313 :     Option* o = getSecure(name);
     210     28647313 :     return o->getString();
     211              : }
     212              : 
     213              : 
     214              : double
     215    116867580 : OptionsCont::getFloat(const std::string& name) const {
     216    116867580 :     Option* o = getSecure(name);
     217    116867579 :     return o->getFloat();
     218              : }
     219              : 
     220              : 
     221              : int
     222      2055198 : OptionsCont::getInt(const std::string& name) const {
     223      2055198 :     Option* o = getSecure(name);
     224      2055198 :     return o->getInt();
     225              : }
     226              : 
     227              : 
     228              : bool
     229    130701310 : OptionsCont::getBool(const std::string& name) const {
     230    130701310 :     Option* o = getSecure(name);
     231    130701310 :     return o->getBool();
     232              : }
     233              : 
     234              : 
     235              : const IntVector&
     236            0 : OptionsCont::getIntVector(const std::string& name) const {
     237            0 :     Option* o = getSecure(name);
     238            0 :     return o->getIntVector();
     239              : }
     240              : 
     241              : const StringVector&
     242       599950 : OptionsCont::getStringVector(const std::string& name) const {
     243       599950 :     Option* o = getSecure(name);
     244       599950 :     return o->getStringVector();
     245              : }
     246              : 
     247              : 
     248              : bool
     249      1087975 : OptionsCont::set(const std::string& name, const std::string& value, const bool append) {
     250      1087975 :     Option* o = getSecure(name);
     251      1087957 :     if (!o->isWriteable()) {
     252            0 :         reportDoubleSetting(name);
     253            0 :         return false;
     254              :     }
     255              :     try {
     256              :         // Substitute environment variables defined by ${NAME} with their value
     257      2175896 :         if (!o->set(StringUtils::substituteEnvironment(value, &OptionsIO::getLoadTime()), value, append)) {
     258              :             return false;
     259              :         }
     260           18 :     } catch (ProcessError& e) {
     261           36 :         WRITE_ERROR("While processing option '" + name + "':\n " + e.what());
     262              :         return false;
     263           18 :     }
     264              :     return true;
     265              : }
     266              : 
     267              : 
     268              : bool
     269       164845 : OptionsCont::setDefault(const std::string& name, const std::string& value) {
     270       164845 :     Option* const o = getSecure(name);
     271       164845 :     if (o->isWriteable() && set(name, value)) {
     272       145451 :         o->resetDefault();
     273       145451 :         return true;
     274              :     }
     275              :     return false;
     276              : }
     277              : 
     278              : 
     279              : bool
     280           31 : OptionsCont::setByRootElement(const std::string& root, const std::string& value) {
     281              :     if (myXMLDefaults.count(root) > 0) {
     282            1 :         return set(myXMLDefaults[root], value);
     283              :     }
     284           60 :     if (myXMLDefaults.count("") > 0) {
     285           60 :         return set(myXMLDefaults[""], value);
     286              :     }
     287              :     return false;
     288              : }
     289              : 
     290              : 
     291              : std::vector<std::string>
     292        46249 : OptionsCont::getSynonymes(const std::string& name) const {
     293        46249 :     Option* o = getSecure(name);
     294              :     std::vector<std::string> synonymes;
     295     21256475 :     for (const auto& value : myValues) {
     296     21210226 :         if ((value.second == o) && (name != value.first)) {
     297        13295 :             synonymes.push_back(value.first);
     298              :         }
     299              :     }
     300        46249 :     return synonymes;
     301            0 : }
     302              : 
     303              : 
     304              : const std::string&
     305            0 : OptionsCont::getDescription(const std::string& name) const {
     306            0 :     return getSecure(name)->getDescription();
     307              : }
     308              : 
     309              : 
     310              : const std::string&
     311            0 : OptionsCont::getSubTopic(const std::string& name) const {
     312            0 :     return getSecure(name)->getSubTopic();
     313              : }
     314              : 
     315              : 
     316              : std::ostream&
     317            0 : operator<<(std::ostream& os, const OptionsCont& oc) {
     318              :     std::vector<std::string> done;
     319              :     os << "Options set:" << std::endl;
     320            0 :     for (const auto& value : oc.myValues) {
     321            0 :         const auto& finder = std::find(done.begin(), done.end(), value.first);
     322            0 :         if (finder == done.end()) {
     323            0 :             std::vector<std::string> synonymes = oc.getSynonymes(value.first);
     324            0 :             if (synonymes.size() != 0) {
     325            0 :                 os << value.first << " (";
     326            0 :                 for (auto synonym = synonymes.begin(); synonym != synonymes.end(); synonym++) {
     327            0 :                     if (synonym != synonymes.begin()) {
     328            0 :                         os << ", ";
     329              :                     }
     330              :                     os << (*synonym);
     331              :                 }
     332            0 :                 os << ")";
     333              :             } else {
     334              :                 os << value.first;
     335              :             }
     336            0 :             if (value.second->isSet()) {
     337            0 :                 os << ": " << value.second->getValueString() << std::endl;
     338              :             } else {
     339              :                 os << ": <INVALID>" << std::endl;
     340              :             }
     341            0 :             done.push_back(value.first);
     342              :             copy(synonymes.begin(), synonymes.end(), back_inserter(done));
     343            0 :         }
     344              :     }
     345            0 :     return os;
     346            0 : }
     347              : 
     348              : 
     349              : void
     350        13599 : OptionsCont::relocateFiles(const std::string& configuration) const {
     351      6124004 :     for (const auto& addresse : myAddresses) {
     352      6110405 :         if (addresse.second->isFileName() && addresse.second->isSet()) {
     353        80793 :             StringVector fileList = StringVector(addresse.second->getStringVector());
     354       172742 :             for (auto& file : fileList) {
     355        91949 :                 if (addresse.first != "configuration-file") {
     356       156700 :                     file = FileHelpers::checkForRelativity(file, configuration);
     357              :                 }
     358              :                 try {
     359       183896 :                     file = StringUtils::urlDecode(file);
     360            2 :                 } catch (NumberFormatException& e) {
     361            4 :                     WRITE_WARNING(toString(e.what()) + " when trying to decode filename '" + file + "'.");
     362            2 :                 }
     363              :             }
     364       242379 :             StringVector rawList = StringTokenizer(addresse.second->getValueString(), ",").getVector();
     365       172742 :             for (auto& file : rawList) {
     366       183898 :                 file = FileHelpers::checkForRelativity(file, configuration);
     367              :             }
     368        80793 :             const std::string conv = joinToString(fileList, ',');
     369       161586 :             if (conv != joinToString(addresse.second->getStringVector(), ',')) {
     370         3662 :                 const bool hadDefault = addresse.second->isDefault();
     371         3662 :                 addresse.second->set(conv, joinToString(rawList, ','), false);
     372         3662 :                 if (hadDefault) {
     373          939 :                     addresse.second->resetDefault();
     374              :                 }
     375              :             }
     376        80793 :         }
     377              :     }
     378        13599 : }
     379              : 
     380              : 
     381              : bool
     382        85618 : OptionsCont::isUsableFileList(const std::string& name) const {
     383        85618 :     Option* const o = getSecure(name);
     384        85618 :     if (!o->isSet()) {
     385              :         return false;
     386              :     }
     387              :     // check whether the list of files is valid
     388              :     bool ok = true;
     389        76461 :     std::vector<std::string> files = getStringVector(name);
     390        76461 :     if (files.size() == 0) {
     391            0 :         WRITE_ERRORF(TL("The file list for '%' is empty."), name);
     392              :         ok = false;
     393              :     }
     394       172182 :     for (const auto& file : files) {
     395       191442 :         if (!FileHelpers::isReadable(file)) {
     396           30 :             if (file != "") {
     397           90 :                 WRITE_ERRORF(TL("File '%' is not accessible (%)."), file, std::strerror(errno));
     398              :                 ok = false;
     399              :             } else {
     400            0 :                 WRITE_WARNING(TL("Empty file name given; ignoring."));
     401              :             }
     402              :         }
     403              :     }
     404              :     return ok;
     405        76461 : }
     406              : 
     407              : 
     408              : bool
     409         7182 : OptionsCont::checkDependingSuboptions(const std::string& name, const std::string& prefix) const {
     410         7182 :     Option* o = getSecure(name);
     411         7182 :     if (o->isSet()) {
     412              :         return true;
     413              :     }
     414              :     bool ok = true;
     415              :     std::vector<std::string> seenSynonymes;
     416      3516542 :     for (const auto& value : myValues) {
     417      3509380 :         if (std::find(seenSynonymes.begin(), seenSynonymes.end(), value.first) != seenSynonymes.end()) {
     418            1 :             continue;
     419              :         }
     420      3616905 :         if (value.second->isSet() && !value.second->isDefault() && value.first.find(prefix) == 0) {
     421            3 :             WRITE_ERRORF(TL("Option '%' needs option '%'."), value.first, name);
     422            1 :             std::vector<std::string> synonymes = getSynonymes(value.first);
     423              :             std::copy(synonymes.begin(), synonymes.end(), std::back_inserter(seenSynonymes));
     424              :             ok = false;
     425            1 :         }
     426              :     }
     427              :     return ok;
     428         7162 : }
     429              : 
     430              : 
     431              : void
     432            0 : OptionsCont::reportDoubleSetting(const std::string& arg) const {
     433            0 :     std::vector<std::string> synonymes = getSynonymes(arg);
     434            0 :     std::ostringstream s;
     435            0 :     s << TLF("A value for the option '%' was already set.\n Possible synonymes: ", arg);
     436              :     auto synonym = synonymes.begin();
     437            0 :     while (synonym != synonymes.end()) {
     438              :         s << (*synonym);
     439              :         synonym++;
     440            0 :         if (synonym != synonymes.end()) {
     441            0 :             s << ", ";
     442              :         }
     443              :     }
     444            0 :     WRITE_ERROR(s.str());
     445            0 : }
     446              : 
     447              : 
     448              : std::string
     449      1414655 : OptionsCont::convertChar(char abbr) const {
     450              :     char buf[2];
     451      1414655 :     buf[0] = abbr;
     452      1414655 :     buf[1] = 0;
     453      1414655 :     std::string s(buf);
     454      1414655 :     return s;
     455              : }
     456              : 
     457              : 
     458              : bool
     459       738944 : OptionsCont::isBool(const std::string& name) const {
     460       738944 :     Option* o = getSecure(name);
     461       738914 :     return o->isBool();
     462              : }
     463              : 
     464              : 
     465              : void
     466        67061 : OptionsCont::resetWritable() {
     467     27974461 :     for (const auto& addresse : myAddresses) {
     468     27907400 :         addresse.second->resetWritable();
     469              :     }
     470        67061 : }
     471              : 
     472              : 
     473              : void
     474            0 : OptionsCont::resetDefault() {
     475            0 :     for (const auto& addresse : myAddresses) {
     476            0 :         addresse.second->resetDefault();
     477              :     }
     478            0 : }
     479              : 
     480              : 
     481              : void
     482            0 : OptionsCont::resetDefault(const std::string& name) {
     483            0 :     getSecure(name)->resetDefault();
     484            0 : }
     485              : 
     486              : 
     487              : bool
     488        78126 : OptionsCont::isWriteable(const std::string& name) {
     489        78126 :     return getSecure(name)->isWriteable();
     490              : }
     491              : 
     492              : 
     493              : bool
     494            0 : OptionsCont::isEditable(const std::string& name) {
     495            0 :     return getSecure(name)->isEditable();
     496              : 
     497              : }
     498              : 
     499              : 
     500              : void
     501       197326 : OptionsCont::clear() {
     502              :     // delete only address (because synonyms placed in values aim to the same Option)
     503     22211701 :     for (const auto& addresse : myAddresses) {
     504     22014375 :         delete addresse.second;
     505              :     }
     506              :     myAddresses.clear();
     507              :     myValues.clear();
     508              :     mySubTopics.clear();
     509              :     mySubTopicEntries.clear();
     510       197326 : }
     511              : 
     512              : 
     513              : void
     514     21907426 : OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
     515              :                             const std::string& description) {
     516     21907426 :     Option* o = getSecure(name);
     517     21907426 :     if (o == nullptr) {
     518            0 :         throw ProcessError("Option doesn't exist");
     519              :     }
     520     21907426 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     521            0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     522              :     }
     523     21907426 :     o->setDescription(description);
     524     21907426 :     o->setSubtopic(subtopic);
     525     21907426 :     mySubTopicEntries[subtopic].push_back(name);
     526     21907426 : }
     527              : 
     528              : 
     529              : void
     530            0 : OptionsCont::setFurtherAttributes(const std::string& name, const std::string& subtopic, bool required, bool positional, const std::string& listSep) {
     531            0 :     Option* o = getSecure(name);
     532            0 :     if (o == nullptr) {
     533            0 :         throw ProcessError("Option doesn't exist");
     534              :     }
     535            0 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     536            0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     537              :     }
     538            0 :     if (required) {
     539            0 :         o->setRequired();
     540              :     }
     541            0 :     if (positional) {
     542            0 :         o->setPositional();
     543              :     }
     544            0 :     o->setListSeparator(listSep);
     545            0 : }
     546              : 
     547              : 
     548              : void
     549            0 : OptionsCont::setOptionEditable(const std::string& name, const bool value) {
     550            0 :     getSecure(name)->setEditable(value);
     551            0 : }
     552              : 
     553              : 
     554              : void
     555        53144 : OptionsCont::setApplicationName(const std::string& appName, const std::string& fullName) {
     556        53144 :     myAppName = appName;
     557        53144 :     myFullName = fullName;
     558        53144 : }
     559              : 
     560              : 
     561              : void
     562       105863 : OptionsCont::setApplicationDescription(const std::string& appDesc) {
     563       105863 :     myAppDescription = appDesc;
     564       105863 : }
     565              : 
     566              : 
     567              : void
     568       143176 : OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
     569       143176 :     myCallExamples.push_back(std::make_pair(example, desc));
     570       143176 : }
     571              : 
     572              : 
     573              : void
     574          120 : OptionsCont::setAdditionalHelpMessage(const std::string& add) {
     575          120 :     myAdditionalMessage = add;
     576          120 : }
     577              : 
     578              : 
     579              : void
     580           14 : OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
     581           14 :     myCopyrightNotices.push_back(copyrightLine);
     582           14 : }
     583              : 
     584              : 
     585              : void
     586            0 : OptionsCont::clearCopyrightNotices() {
     587              :     myCopyrightNotices.clear();
     588            0 : }
     589              : 
     590              : 
     591              : void
     592      1290433 : OptionsCont::addOptionSubTopic(const std::string& topic) {
     593      1290433 :     mySubTopics.push_back(topic);
     594      1290433 :     mySubTopicEntries[topic] = std::vector<std::string>();
     595      1290433 : }
     596              : 
     597              : 
     598              : void
     599        20719 : OptionsCont::splitLines(std::ostream& os, std::string what,
     600              :                         int offset, int nextOffset) {
     601        64205 :     while (what.length() > 0) {
     602        43486 :         if ((int)what.length() > 79 - offset) {
     603        22802 :             std::string::size_type splitPos = what.rfind(';', 79 - offset);
     604        22802 :             if (splitPos == std::string::npos) {
     605        22532 :                 splitPos = what.rfind(' ', 79 - offset);
     606              :             } else {
     607          270 :                 splitPos++;
     608              :             }
     609        22802 :             if (splitPos != std::string::npos) {
     610        22767 :                 os << what.substr(0, splitPos) << std::endl;
     611        22767 :                 what = what.substr(splitPos + 1);
     612       927233 :                 for (int r = 0; r < (nextOffset + 1); ++r) {
     613       904466 :                     os << ' ';
     614              :                 }
     615              :             } else {
     616              :                 os << what;
     617              :                 what = "";
     618              :             }
     619              :             offset = nextOffset;
     620              :         } else {
     621              :             os << what;
     622              :             what = "";
     623              :         }
     624              :     }
     625              :     os << std::endl;
     626        20719 : }
     627              : 
     628              : 
     629              : bool
     630        53371 : OptionsCont::processMetaOptions(bool missingOptions) {
     631        53371 :     MsgHandler::setupI18n(getString("language"));
     632        53371 :     localizeDescriptions();
     633        53371 :     if (missingOptions) {
     634              :         // no options are given
     635              :         std::cout << myFullName << std::endl;
     636           76 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     637          153 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     638           77 :             std::cout << " " << copyrightNotice.data() << std::endl;
     639              :         }
     640           76 :         std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
     641           76 :         std::cout << TL(" Use --help to get the list of options.") << std::endl;
     642           76 :         return true;
     643              :     }
     644              : 
     645              :     // check whether the help shall be printed
     646       106590 :     if (getBool("help")) {
     647              :         std::cout << myFullName << std::endl;
     648          155 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     649           78 :             std::cout << " " << copyrightNotice.data() << std::endl;
     650              :         }
     651           77 :         printHelp(std::cout);
     652           77 :         return true;
     653              :     }
     654              :     // check whether the help shall be printed
     655       106436 :     if (getBool("version")) {
     656              :         std::cout << myFullName << std::endl;
     657           13 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     658           27 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     659           14 :             std::cout << " " << copyrightNotice.data() << std::endl;
     660              :         }
     661           13 :         std::cout << "\n" << myFullName << " is part of SUMO.\n";
     662           13 :         std::cout << "This program and the accompanying materials\n";
     663           13 :         std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
     664           13 :         std::cout << "which accompanies this distribution, and is available at\n";
     665           13 :         std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
     666           13 :         std::cout << "This program may also be made available under the following Secondary\n";
     667           13 :         std::cout << "Licenses when the conditions for such availability set forth in the Eclipse\n";
     668           13 :         std::cout << "Public License 2.0 are satisfied: GNU General Public License, version 2\n";
     669           13 :         std::cout << "or later which is available at\n";
     670           13 :         std::cout << "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n";
     671              :         std::cout << "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later" << std::endl;
     672           13 :         return true;
     673              :     }
     674              :     // check whether the settings shall be printed
     675       106410 :     if (getBool("print-options")) {
     676            0 :         std::cout << (*this);
     677              :     }
     678              :     // check whether something has to be done with options
     679              :     // whether the current options shall be saved
     680       106410 :     if (isSet("save-configuration")) {
     681          509 :         const std::string& configPath = getString("save-configuration");
     682          509 :         if (configPath == "-" || configPath == "stdout") {
     683           24 :             writeConfiguration(std::cout, true, false, getBool("save-commented"));
     684           12 :             return true;
     685              :         }
     686          994 :         std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
     687          497 :         if (!out.good()) {
     688            0 :             throw ProcessError(TLF("Could not save configuration to '%'", configPath));
     689              :         } else {
     690          994 :             writeConfiguration(out, true, false, getBool("save-commented"), configPath);
     691          994 :             if (getBool("verbose")) {
     692          876 :                 WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
     693              :             }
     694              :             return true;
     695              :         }
     696          497 :     }
     697              :     // whether the template shall be saved
     698       105392 :     if (isSet("save-template")) {
     699           65 :         if (getString("save-template") == "-" || getString("save-template") == "stdout") {
     700           10 :             writeConfiguration(std::cout, false, true, getBool("save-commented"));
     701            5 :             return true;
     702              :         }
     703           36 :         std::ofstream out(StringUtils::transcodeToLocal(getString("save-template")).c_str());
     704           18 :         if (!out.good()) {
     705            0 :             throw ProcessError(TLF("Could not save template to '%'", getString("save-template")));
     706              :         } else {
     707           36 :             writeConfiguration(out, false, true, getBool("save-commented"));
     708           36 :             if (getBool("verbose")) {
     709            0 :                 WRITE_MESSAGEF(TL("Written template to '%'"), getString("save-template"));
     710              :             }
     711              :             return true;
     712              :         }
     713           18 :     }
     714       105346 :     if (isSet("save-schema")) {
     715            3 :         if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
     716            0 :             writeSchema(std::cout);
     717            0 :             return true;
     718              :         }
     719            2 :         std::ofstream out(StringUtils::transcodeToLocal(getString("save-schema")).c_str());
     720            1 :         if (!out.good()) {
     721            0 :             throw ProcessError(TLF("Could not save schema to '%'", getString("save-schema")));
     722              :         } else {
     723            1 :             writeSchema(out);
     724            2 :             if (getBool("verbose")) {
     725            0 :                 WRITE_MESSAGEF(TL("Written schema to '%'"), getString("save-schema"));
     726              :             }
     727              :             return true;
     728              :         }
     729            1 :     }
     730              :     return false;
     731              : }
     732              : 
     733              : 
     734              : void
     735        53371 : OptionsCont::localizeDescriptions() {
     736        53371 :     if (!myAmLocalized && gLocaleInitialized) {
     737              :         // options
     738     21796704 :         for (auto option : myAddresses) {
     739     43486756 :             option.second->setDescription(TL(option.second->getDescription().c_str()));
     740              :         }
     741              :         // examples
     742       195422 :         for (auto example : myCallExamples) {
     743       142096 :             example.second = TL(example.second.c_str());
     744              :         }
     745              :         // other text
     746        53326 :         setApplicationDescription(TL(myAppDescription.c_str()));
     747        53326 :         myAmLocalized = true;
     748              :     }
     749        53371 : }
     750              : 
     751              : 
     752              : const std::vector<std::string>&
     753            0 : OptionsCont::getSubTopics() const {
     754            0 :     return mySubTopics;
     755              : }
     756              : 
     757              : 
     758              : std::vector<std::string>
     759            0 : OptionsCont::getSubTopicsEntries(const std::string& subtopic) const {
     760              :     if (mySubTopicEntries.count(subtopic) > 0) {
     761            0 :         return mySubTopicEntries.find(subtopic)->second;
     762              :     } else {
     763            0 :         return std::vector<std::string>();
     764              :     }
     765              : }
     766              : 
     767              : 
     768              : std::string
     769            0 : OptionsCont::getTypeName(const std::string name) {
     770            0 :     return getSecure(name)->getTypeName();
     771              : }
     772              : 
     773              : 
     774              : const std::string&
     775            1 : OptionsCont::getFullName() const {
     776            1 :     return myFullName;
     777              : }
     778              : 
     779              : 
     780              : bool
     781            0 : OptionsCont::isEmpty() const {
     782            0 :     return myAddresses.size() == 0;
     783              : }
     784              : 
     785              : 
     786              : std::vector<std::pair<std::string, Option*> >::const_iterator
     787            0 : OptionsCont::begin() const {
     788            0 :     return myAddresses.cbegin();
     789              : }
     790              : 
     791              : 
     792              : std::vector<std::pair<std::string, Option*> >::const_iterator
     793            0 : OptionsCont::end() const {
     794            0 :     return myAddresses.cend();
     795              : }
     796              : 
     797              : 
     798              : void
     799           77 : OptionsCont::printHelp(std::ostream& os) {
     800              :     // print application description
     801          154 :     splitLines(os, TL(myAppDescription.c_str()), 0, 0);
     802              :     os << std::endl;
     803              : 
     804              :     // check option sizes first
     805              :     //  we want to know how large the largest not-too-large-entry will be
     806              :     int tooLarge = 40;
     807              :     int maxSize = 0;
     808         1336 :     for (const auto& subTopic : mySubTopics) {
     809        21901 :         for (const auto& entry : mySubTopicEntries[subTopic]) {
     810        20642 :             Option* o = getSecure(entry);
     811              :             // name, two leading spaces and "--"
     812        20642 :             int csize = (int)entry.length() + 2 + 4;
     813              :             // abbreviation length ("-X, "->4chars) if any
     814        20642 :             const auto synonymes = getSynonymes(entry);
     815        23797 :             for (const auto& synonym : synonymes) {
     816         4871 :                 if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     817         1716 :                     csize += 4;
     818         1716 :                     break;
     819              :                 }
     820              :             }
     821              :             // the type name
     822        20642 :             if (!o->isBool()) {
     823        14210 :                 csize += 1 + (int)o->getTypeName().length();
     824              :             }
     825              :             // divider
     826        20642 :             csize += 2;
     827        20642 :             if (csize < tooLarge && maxSize < csize) {
     828              :                 maxSize = csize;
     829              :             }
     830        20642 :         }
     831              :     }
     832              : 
     833          154 :     const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
     834           77 :     if (helpTopic != "") {
     835              :         bool foundTopic = false;
     836            0 :         for (const auto& topic : mySubTopics) {
     837            0 :             if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
     838              :                 foundTopic = true;
     839            0 :                 printHelpOnTopic(topic, tooLarge, maxSize, os);
     840              :             }
     841              :         }
     842            0 :         if (!foundTopic) {
     843              :             // print topic list
     844            0 :             os << TL("Help Topics:") << std::endl;
     845            0 :             for (const std::string& t : mySubTopics) {
     846              :                 os << "    " << t << std::endl;
     847              :             }
     848              :         }
     849              :         return;
     850              :     }
     851              :     // print usage BNF
     852          154 :     os << TL("Usage: ") << myAppName << TL(" [OPTION]*") << std::endl;
     853              :     // print additional text if any
     854           77 :     if (myAdditionalMessage.length() > 0) {
     855              :         os << myAdditionalMessage << std::endl << std::endl;
     856              :     }
     857              :     // print the options
     858         1336 :     for (const auto& subTopic : mySubTopics) {
     859         1259 :         printHelpOnTopic(subTopic, tooLarge, maxSize, os);
     860              :     }
     861              :     os << std::endl;
     862              :     // print usage examples, calc size first
     863           77 :     if (myCallExamples.size() != 0) {
     864           77 :         os << TL("Examples:") << std::endl;
     865          225 :         for (const auto& callExample : myCallExamples) {
     866          148 :             os << "  " << myAppName << ' ' << callExample.first << std::endl;
     867              :             os << "    " << callExample.second << std::endl;
     868              :         }
     869              :     }
     870              :     os << std::endl;
     871           77 :     os << TLF("Report bugs at %.", "<https://github.com/eclipse-sumo/sumo/issues>") << std::endl;
     872          154 :     os << TLF("Get in contact via %.", "<sumo@dlr.de>") << std::endl;
     873              : }
     874              : 
     875              : 
     876              : void
     877         1259 : OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
     878         2518 :     os << TLF("% Options:", topic) << std::endl;
     879        21901 :     for (const auto& entry : mySubTopicEntries[topic]) {
     880              :         // start length computation
     881        20642 :         int csize = (int)entry.length() + 2;
     882        20642 :         Option* o = getSecure(entry);
     883        20642 :         os << "  ";
     884              :         // write abbreviation if given
     885        20642 :         const auto synonymes = getSynonymes(entry);
     886        23797 :         for (const auto& synonym : synonymes) {
     887         4871 :             if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     888         3432 :                 os << '-' << synonym << ", ";
     889         1716 :                 csize += 4;
     890         1716 :                 break;
     891              :             }
     892              :         }
     893              :         // write leading '-'/"--"
     894        20642 :         os << "--";
     895        20642 :         csize += 2;
     896              :         // write the name
     897              :         os << entry;
     898              :         // write the type if not a bool option
     899        20642 :         if (!o->isBool()) {
     900        14210 :             os << ' ' << o->getTypeName();
     901        14210 :             csize += 1 + (int)o->getTypeName().length();
     902              :         }
     903        20642 :         csize += 2;
     904              :         // write the description formatting it
     905        20642 :         os << "  ";
     906       207257 :         for (int r = maxSize; r > csize; --r) {
     907       186615 :             os << ' ';
     908              :         }
     909        20642 :         int offset = csize > tooLarge ? csize : maxSize;
     910        41284 :         splitLines(os, o->getDescription(), offset, maxSize);
     911        20642 :     }
     912              :     os << std::endl;
     913         1259 : }
     914              : 
     915              : 
     916              : void
     917        86586 : OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
     918              :                                 const bool complete, const bool addComments, const std::string& relativeTo,
     919              :                                 const bool forceRelative, const bool inComment, const std::string& indent) const {
     920        86586 :     if (!inComment) {
     921          772 :         writeXMLHeader(os, false);
     922              :     }
     923       164664 :     const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
     924              :     os << indent << "<" << app << "Configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
     925        86586 :        << "xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/" << app << "Configuration.xsd\">\n\n";
     926      2277771 :     for (std::string subtopic : mySubTopics) {
     927      2191185 :         if (subtopic == "Configuration" && !complete) {
     928              :             continue;
     929              :         }
     930              :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
     931              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
     932      4209244 :         subtopic = StringUtils::to_lower_case(subtopic);
     933              :         bool hadOne = false;
     934     38977446 :         for (const std::string& name : entries) {
     935     36872824 :             Option* o = getSecure(name);
     936     36872824 :             bool write = complete || (filled && !o->isDefault());
     937     36162562 :             if (!write) {
     938     36162562 :                 continue;
     939              :             }
     940       710262 :             if (name == "registry-viewport" && !complete) {
     941            0 :                 continue;
     942              :             }
     943       710262 :             if (!hadOne) {
     944       299571 :                 os << indent << "    <" << subtopic << ">\n";
     945              :             }
     946              :             // add the comment if wished
     947       710262 :             if (addComments) {
     948         4584 :                 os << indent << "        <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->\n";
     949              :             }
     950              :             // write the option and the value (if given)
     951       710262 :             os << indent << "        <" << name << " value=\"";
     952       710262 :             if (o->isSet() && (filled || o->isDefault())) {
     953       709097 :                 if (o->isFileName() && relativeTo != "") {
     954         7542 :                     StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
     955         5083 :                     for (auto& file : fileList) {
     956         5138 :                         if (StringUtils::startsWith(file, "${")) {
     957              :                             // there is an environment variable up front, assume it points to an absolute path
     958              :                             // not even forcing relativity makes sense here
     959           34 :                             file = StringUtils::urlEncode(file, " ;%");
     960              :                         } else {
     961         7656 :                             file = FileHelpers::fixRelative(
     962         5104 :                                        StringUtils::urlEncode(file, " ;%"),
     963         2552 :                                        StringUtils::urlEncode(relativeTo, " ;%"),
     964        10208 :                                        forceRelative || getBool("save-configuration.relative"));
     965              :                         }
     966              :                     }
     967         2514 :                     os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
     968         2514 :                 } else {
     969      1413166 :                     os << StringUtils::escapeXML(o->getValueString(), inComment);
     970              :                 }
     971              :             }
     972       710262 :             if (complete) {
     973         4964 :                 const std::vector<std::string> synonymes = getSynonymes(name);
     974         4964 :                 if (!synonymes.empty()) {
     975         2584 :                     os << "\" synonymes=\"" << toString(synonymes);
     976              :                 }
     977              :                 std::string deprecated;
     978         6788 :                 for (const auto& synonym : synonymes) {
     979              :                     if (myDeprecatedSynonymes.count(synonym) > 0) {
     980         1554 :                         deprecated += " " + synonym;
     981              :                     }
     982              :                 }
     983         4964 :                 if (deprecated != "") {
     984         1398 :                     os << "\" deprecated=\"" << deprecated.substr(1);
     985              :                 }
     986         4964 :                 os << "\" type=\"" << o->getTypeName();
     987         4964 :                 if (!addComments) {
     988         6872 :                     os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
     989              :                 }
     990         4964 :             }
     991       710262 :             os << "\"/>\n";
     992              :             // append an endline if a comment was printed
     993       710262 :             if (addComments) {
     994         1528 :                 os << "\n";
     995              :             }
     996              :             hadOne = true;
     997              :         }
     998      2104622 :         if (hadOne) {
     999       299571 :             os << indent << "    </" << subtopic << ">\n\n";
    1000              :         }
    1001              :     }
    1002              :     os << indent << "</" << app << "Configuration>" << std::endl;  // flushing seems like a good idea here
    1003        86586 : }
    1004              : 
    1005              : 
    1006              : void
    1007            1 : OptionsCont::writeSchema(std::ostream& os) {
    1008            1 :     const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
    1009            1 :     writeXMLHeader(os, false);
    1010            1 :     os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n";
    1011            1 :     os << "    <xsd:complexType name=\"" << app << "ConfigurationType\">\n";
    1012            1 :     os << "        <xsd:all>\n";
    1013           28 :     for (std::string subtopic : mySubTopics) {
    1014           27 :         if (subtopic == "Configuration") {
    1015              :             continue;
    1016              :         }
    1017              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
    1018           52 :         subtopic = StringUtils::to_lower_case(subtopic);
    1019           26 :         os << "            <xsd:element name=\"" << subtopic << "\" type=\"" << app << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
    1020              :     }
    1021            1 :     os << "        </xsd:all>\n";
    1022            1 :     os << "    </xsd:complexType>\n\n";
    1023           28 :     for (std::string subtopic : mySubTopics) {
    1024           27 :         if (subtopic == "Configuration") {
    1025              :             continue;
    1026              :         }
    1027              :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
    1028              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
    1029           52 :         subtopic = StringUtils::to_lower_case(subtopic);
    1030           26 :         os << "    <xsd:complexType name=\"" << app << subtopic << "TopicType\">\n";
    1031           26 :         os << "        <xsd:all>\n";
    1032          479 :         for (const auto& entry : entries) {
    1033          453 :             Option* o = getSecure(entry);
    1034          453 :             std::string type = o->getTypeName();
    1035          453 :             type = StringUtils::to_lower_case(type);
    1036          453 :             if (type == "int[]") {
    1037              :                 type = "intArray";
    1038              :             }
    1039          453 :             if (type == "str[]") {
    1040              :                 type = "strArray";
    1041              :             }
    1042          453 :             os << "            <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
    1043              :         }
    1044           26 :         os << "        </xsd:all>\n";
    1045           26 :         os << "    </xsd:complexType>\n\n";
    1046              :     }
    1047            1 :     os << "</xsd:schema>\n";
    1048            1 : }
    1049              : 
    1050              : 
    1051              : void
    1052        86587 : OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
    1053        86587 :     os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
    1054        86587 :     os << "<!-- ";
    1055       173174 :     if (!getBool("write-metadata")) {
    1056       259758 :         os << "generated on " << StringUtils::isoTimeString() << " by " << myFullName << "\n";
    1057              :     }
    1058       173174 :     if (getBool("write-license")) {
    1059              :         os << "This data file and the accompanying materials\n"
    1060              :            "are made available under the terms of the Eclipse Public License v2.0\n"
    1061              :            "which accompanies this distribution, and is available at\n"
    1062              :            "http://www.eclipse.org/legal/epl-v20.html\n"
    1063              :            "This file may also be made available under the following Secondary\n"
    1064              :            "Licenses when the conditions for such availability set forth in the Eclipse\n"
    1065              :            "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
    1066              :            "or later which is available at\n"
    1067              :            "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
    1068        34979 :            "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
    1069              :     }
    1070       172402 :     if (includeConfig && !getBool("write-metadata")) {
    1071       171626 :         writeConfiguration(os, true, false, false, "", false, true);
    1072              :     }
    1073        86587 :     os << "-->\n\n";
    1074        86587 : }
    1075              : 
    1076              : 
    1077              : bool
    1078        40934 : OptionsCont::isInStringVector(const std::string& optionName,
    1079              :                               const std::string& itemName) const {
    1080        40934 :     if (isSet(optionName)) {
    1081            0 :         std::vector<std::string> values = getStringVector(optionName);
    1082            0 :         return std::find(values.begin(), values.end(), itemName) != values.end();
    1083            0 :     }
    1084              :     return false;
    1085              : }
    1086              : 
    1087              : 
    1088              : OptionsCont*
    1089          174 : OptionsCont::clone() const {
    1090              :     // build a clone to call writeConfiguration on
    1091              :     // (with the possibility of changing a few settings and not affecting the original)
    1092          174 :     OptionsCont* oc = new OptionsCont(*this);
    1093          174 :     oc->resetWritable();
    1094        63510 :     for (auto& addr : oc->myAddresses) {
    1095        63336 :         addr.second = addr.second->clone();
    1096              :     }
    1097          174 :     return oc;
    1098              : }
    1099              : 
    1100              : 
    1101              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1