LCOV - code coverage report
Current view: top level - src/utils/options - OptionsCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 80.3 % 522 419
Test Date: 2026-06-15 15:46:12 Functions: 73.8 % 65 48

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

Generated by: LCOV version 2.0-1