LCOV - code coverage report
Current view: top level - src/utils/options - OptionsCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 81.0 % 515 417
Test Date: 2025-11-13 15:38:19 Functions: 75.8 % 62 47

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 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   1115818371 : OptionsCont::getOptions() {
      62   1115818371 :     return myOptions;
      63              : }
      64              : 
      65              : 
      66        97302 : OptionsCont::OptionsCont() {
      67        97302 :     myCopyrightNotices.push_back(TL("Copyright (C) 2001-2025 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
      68        97302 : }
      69              : 
      70              : 
      71        97474 : OptionsCont::~OptionsCont() {
      72        97474 :     clear();
      73       194948 : }
      74              : 
      75              : 
      76              : void
      77     23954886 : OptionsCont::doRegister(const std::string& name, Option* o) {
      78              :     // first check that option isn't null
      79     23954886 :     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     23954886 :     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   5098691898 :     for (const auto& addresse : myAddresses) {
      89   5074737012 :         if (addresse.second == o) {
      90              :             isSynonym = true;
      91              :         }
      92              :     }
      93     23954886 :     if (!isSynonym) {
      94     38994090 :         myAddresses.push_back(std::make_pair(name, o));
      95              :     }
      96              :     // insert in values
      97     23954886 :     myValues[name] = o;
      98     23954886 : }
      99              : 
     100              : 
     101              : void
     102      1235370 : OptionsCont::doRegister(const std::string& name1, char abbr, Option* o) {
     103      1235370 :     doRegister(name1, o);
     104      1235370 :     doRegister(convertChar(abbr), o);
     105      1235370 : }
     106              : 
     107              : 
     108              : void
     109      3222471 : 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      3222471 :     if (i1 == myValues.end() && i2 == myValues.end()) {
     113            0 :         throw ProcessError("Neither the option '" + name1 + "' nor the option '" + name2 + "' is known yet");
     114              :     }
     115      3222471 :     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      3222471 :     if (i1 == myValues.end() && i2 != myValues.end()) {
     122       105309 :         doRegister(name1, (*i2).second);
     123       105309 :         if (isDeprecated) {
     124            0 :             myDeprecatedSynonymes[name1] = false;
     125              :         }
     126              :     }
     127      3222471 :     if (i1 != myValues.end() && i2 == myValues.end()) {
     128      3117162 :         doRegister(name2, (*i1).second);
     129      3117162 :         if (isDeprecated) {
     130      1673224 :             myDeprecatedSynonymes[name2] = false;
     131              :         }
     132              :     }
     133              : }
     134              : 
     135              : 
     136              : void
     137        90503 : OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
     138        90503 :     myXMLDefaults[xmlRoot] = name;
     139        90503 : }
     140              : 
     141              : 
     142              : bool
     143    363989487 : OptionsCont::exists(const std::string& name) const {
     144    363989487 :     return myValues.count(name) > 0;
     145              : }
     146              : 
     147              : 
     148              : bool
     149   1183665648 : OptionsCont::isSet(const std::string& name, bool failOnNonExistant) const {
     150              :     auto i = myValues.find(name);
     151   1183665648 :     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   1183665446 :     return (*i).second->isSet();
     159              : }
     160              : 
     161              : 
     162              : bool
     163      2202381 : OptionsCont::isDefault(const std::string& name) const {
     164              :     auto i = myValues.find(name);
     165      2202381 :     if (i == myValues.end()) {
     166              :         return false;
     167              :     }
     168      2201933 :     return (*i).second->isDefault();
     169              : }
     170              : 
     171              : 
     172              : Option*
     173    334980191 : OptionsCont::getSecure(const std::string& name) const {
     174              :     const auto& valuesFinder = myValues.find(name);
     175    334980191 :     if (valuesFinder == myValues.end()) {
     176          162 :         throw ProcessError(TLF("No option with the name '%' exists.", name));
     177              :     }
     178              :     const auto& synonymFinder = myDeprecatedSynonymes.find(name);
     179    334980137 :     if ((synonymFinder != myDeprecatedSynonymes.end()) && !synonymFinder->second) {
     180              :         std::string defaultName;
     181         7018 :         for (const auto& subtopicEntry : mySubTopicEntries) {
     182       116951 :             for (const auto& value : subtopicEntry.second) {
     183              :                 const auto l = myValues.find(value);
     184       111134 :                 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    334980137 :     return valuesFinder->second;
     197              : }
     198              : 
     199              : 
     200              : std::string
     201     15156675 : OptionsCont::getValueString(const std::string& name) const {
     202     15156675 :     Option* o = getSecure(name);
     203     15156675 :     return o->getValueString();
     204              : }
     205              : 
     206              : 
     207              : std::string
     208     22787943 : OptionsCont::getString(const std::string& name) const {
     209     22787943 :     Option* o = getSecure(name);
     210     22787943 :     return o->getString();
     211              : }
     212              : 
     213              : 
     214              : double
     215    113298126 : OptionsCont::getFloat(const std::string& name) const {
     216    113298126 :     Option* o = getSecure(name);
     217    113298126 :     return o->getFloat();
     218              : }
     219              : 
     220              : 
     221              : int
     222      2084174 : OptionsCont::getInt(const std::string& name) const {
     223      2084174 :     Option* o = getSecure(name);
     224      2084174 :     return o->getInt();
     225              : }
     226              : 
     227              : 
     228              : bool
     229    126171840 : OptionsCont::getBool(const std::string& name) const {
     230    126171840 :     Option* o = getSecure(name);
     231    126171840 :     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       504188 : OptionsCont::getStringVector(const std::string& name) const {
     243       504188 :     Option* o = getSecure(name);
     244       504188 :     return o->getStringVector();
     245              : }
     246              : 
     247              : 
     248              : bool
     249      1031830 : OptionsCont::set(const std::string& name, const std::string& value, const bool append) {
     250      1031830 :     Option* o = getSecure(name);
     251      1031812 :     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      2063606 :         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       150707 : OptionsCont::setDefault(const std::string& name, const std::string& value) {
     270       150707 :     Option* const o = getSecure(name);
     271       150707 :     if (o->isWriteable() && set(name, value)) {
     272       131836 :         o->resetDefault();
     273       131836 :         return true;
     274              :     }
     275              :     return false;
     276              : }
     277              : 
     278              : 
     279              : bool
     280           34 : 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           66 :     if (myXMLDefaults.count("") > 0) {
     285           66 :         return set(myXMLDefaults[""], value);
     286              :     }
     287              :     return false;
     288              : }
     289              : 
     290              : 
     291              : std::vector<std::string>
     292        45336 : OptionsCont::getSynonymes(const std::string& name) const {
     293        45336 :     Option* o = getSecure(name);
     294              :     std::vector<std::string> synonymes;
     295     20585957 :     for (const auto& value : myValues) {
     296     20540621 :         if ((value.second == o) && (name != value.first)) {
     297        13019 :             synonymes.push_back(value.first);
     298              :         }
     299              :     }
     300        45336 :     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        11664 : OptionsCont::relocateFiles(const std::string& configuration) const {
     351      5176861 :     for (const auto& addresse : myAddresses) {
     352      5165197 :         if (addresse.second->isFileName() && addresse.second->isSet()) {
     353        69602 :             StringVector fileList = StringVector(addresse.second->getStringVector());
     354       148036 :             for (auto& file : fileList) {
     355        78434 :                 if (addresse.first != "configuration-file") {
     356       133540 :                     file = FileHelpers::checkForRelativity(file, configuration);
     357              :                 }
     358              :                 try {
     359       156866 :                     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       208806 :             StringVector rawList = StringTokenizer(addresse.second->getValueString(), ",").getVector();
     365       148036 :             for (auto& file : rawList) {
     366       156868 :                 file = FileHelpers::checkForRelativity(file, configuration);
     367              :             }
     368        69602 :             const std::string conv = joinToString(fileList, ',');
     369       139204 :             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        69602 :         }
     377              :     }
     378        11664 : }
     379              : 
     380              : 
     381              : bool
     382        76245 : OptionsCont::isUsableFileList(const std::string& name) const {
     383        76245 :     Option* const o = getSecure(name);
     384        76245 :     if (!o->isSet()) {
     385              :         return false;
     386              :     }
     387              :     // check whether the list of files is valid
     388              :     bool ok = true;
     389        68267 :     std::vector<std::string> files = getStringVector(name);
     390        68267 :     if (files.size() == 0) {
     391            0 :         WRITE_ERRORF(TL("The file list for '%' is empty."), name);
     392              :         ok = false;
     393              :     }
     394       152207 :     for (const auto& file : files) {
     395       167880 :         if (!FileHelpers::isReadable(file)) {
     396          177 :             if (file != "") {
     397          531 :                 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        68267 : }
     406              : 
     407              : 
     408              : bool
     409         6051 : OptionsCont::checkDependingSuboptions(const std::string& name, const std::string& prefix) const {
     410         6051 :     Option* o = getSecure(name);
     411         6051 :     if (o->isSet()) {
     412              :         return true;
     413              :     }
     414              :     bool ok = true;
     415              :     std::vector<std::string> seenSynonymes;
     416      2925520 :     for (const auto& value : myValues) {
     417      2919488 :         if (std::find(seenSynonymes.begin(), seenSynonymes.end(), value.first) != seenSynonymes.end()) {
     418            1 :             continue;
     419              :         }
     420      3014752 :         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         6032 : }
     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      1235370 : OptionsCont::convertChar(char abbr) const {
     450              :     char buf[2];
     451      1235370 :     buf[0] = abbr;
     452      1235370 :     buf[1] = 0;
     453      1235370 :     std::string s(buf);
     454      1235370 :     return s;
     455              : }
     456              : 
     457              : 
     458              : bool
     459       675954 : OptionsCont::isBool(const std::string& name) const {
     460       675954 :     Option* o = getSecure(name);
     461       675924 :     return o->isBool();
     462              : }
     463              : 
     464              : 
     465              : void
     466        60360 : OptionsCont::resetWritable() {
     467     24685106 :     for (const auto& addresse : myAddresses) {
     468     24624746 :         addresse.second->resetWritable();
     469              :     }
     470        60360 : }
     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       101242 : OptionsCont::isWriteable(const std::string& name) {
     489       101242 :     Option* o = getSecure(name);
     490       101236 :     return o->isWriteable();
     491              : }
     492              : 
     493              : 
     494              : void
     495       178288 : OptionsCont::clear() {
     496              :     // delete only address (because synonyms placed in values aim to the same Option)
     497     19737253 :     for (const auto& addresse : myAddresses) {
     498     19558965 :         delete addresse.second;
     499              :     }
     500              :     myAddresses.clear();
     501              :     myValues.clear();
     502              :     mySubTopics.clear();
     503              :     mySubTopicEntries.clear();
     504       178288 : }
     505              : 
     506              : 
     507              : void
     508     19457721 : OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
     509              :                             const std::string& description) {
     510     19457721 :     Option* o = getSecure(name);
     511     19457721 :     if (o == nullptr) {
     512            0 :         throw ProcessError("Option doesn't exist");
     513              :     }
     514     19457721 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     515            0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     516              :     }
     517     19457721 :     o->setDescription(description);
     518     19457721 :     o->setSubtopic(subtopic);
     519     19457721 :     mySubTopicEntries[subtopic].push_back(name);
     520     19457721 : }
     521              : 
     522              : 
     523              : void
     524            0 : OptionsCont::setFurtherAttributes(const std::string& name, const std::string& subtopic, bool required, bool positional, const std::string& listSep) {
     525            0 :     Option* o = getSecure(name);
     526            0 :     if (o == nullptr) {
     527            0 :         throw ProcessError("Option doesn't exist");
     528              :     }
     529            0 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     530            0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     531              :     }
     532            0 :     if (required) {
     533            0 :         o->setRequired();
     534              :     }
     535            0 :     if (positional) {
     536            0 :         o->setPositional();
     537              :     }
     538            0 :     o->setListSeparator(listSep);
     539            0 : }
     540              : 
     541              : 
     542              : void
     543        48156 : OptionsCont::setApplicationName(const std::string& appName,
     544              :                                 const std::string& fullName) {
     545        48156 :     myAppName = appName;
     546        48156 :     myFullName = fullName;
     547        48156 : }
     548              : 
     549              : 
     550              : void
     551        96128 : OptionsCont::setApplicationDescription(const std::string& appDesc) {
     552        96128 :     myAppDescription = appDesc;
     553        96128 : }
     554              : 
     555              : 
     556              : void
     557       128712 : OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
     558       128712 :     myCallExamples.push_back(std::make_pair(example, desc));
     559       128712 : }
     560              : 
     561              : 
     562              : void
     563          120 : OptionsCont::setAdditionalHelpMessage(const std::string& add) {
     564          120 :     myAdditionalMessage = add;
     565          120 : }
     566              : 
     567              : 
     568              : void
     569           14 : OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
     570           14 :     myCopyrightNotices.push_back(copyrightLine);
     571           14 : }
     572              : 
     573              : 
     574              : void
     575            0 : OptionsCont::clearCopyrightNotices() {
     576              :     myCopyrightNotices.clear();
     577            0 : }
     578              : 
     579              : 
     580              : void
     581      1160729 : OptionsCont::addOptionSubTopic(const std::string& topic) {
     582      1160729 :     mySubTopics.push_back(topic);
     583      1160729 :     mySubTopicEntries[topic] = std::vector<std::string>();
     584      1160729 : }
     585              : 
     586              : 
     587              : void
     588        20470 : OptionsCont::splitLines(std::ostream& os, std::string what,
     589              :                         int offset, int nextOffset) {
     590        63352 :     while (what.length() > 0) {
     591        42882 :         if ((int)what.length() > 79 - offset) {
     592        22446 :             std::string::size_type splitPos = what.rfind(';', 79 - offset);
     593        22446 :             if (splitPos == std::string::npos) {
     594        22176 :                 splitPos = what.rfind(' ', 79 - offset);
     595              :             } else {
     596          270 :                 splitPos++;
     597              :             }
     598        22446 :             if (splitPos != std::string::npos) {
     599        22412 :                 os << what.substr(0, splitPos) << std::endl;
     600        22412 :                 what = what.substr(splitPos + 1);
     601       912812 :                 for (int r = 0; r < (nextOffset + 1); ++r) {
     602       890400 :                     os << ' ';
     603              :                 }
     604              :             } else {
     605              :                 os << what;
     606              :                 what = "";
     607              :             }
     608              :             offset = nextOffset;
     609              :         } else {
     610              :             os << what;
     611              :             what = "";
     612              :         }
     613              :     }
     614              :     os << std::endl;
     615        20470 : }
     616              : 
     617              : 
     618              : bool
     619        48610 : OptionsCont::processMetaOptions(bool missingOptions) {
     620        48610 :     MsgHandler::setupI18n(getString("language"));
     621        48610 :     localizeDescriptions();
     622        48610 :     if (missingOptions) {
     623              :         // no options are given
     624              :         std::cout << myFullName << std::endl;
     625           76 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     626          153 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     627           77 :             std::cout << " " << copyrightNotice.data() << std::endl;
     628              :         }
     629           76 :         std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
     630           76 :         std::cout << TL(" Use --help to get the list of options.") << std::endl;
     631           76 :         return true;
     632              :     }
     633              : 
     634              :     // check whether the help shall be printed
     635        97068 :     if (getBool("help")) {
     636              :         std::cout << myFullName << std::endl;
     637          155 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     638           78 :             std::cout << " " << copyrightNotice.data() << std::endl;
     639              :         }
     640           77 :         printHelp(std::cout);
     641           77 :         return true;
     642              :     }
     643              :     // check whether the help shall be printed
     644        96914 :     if (getBool("version")) {
     645              :         std::cout << myFullName << std::endl;
     646           13 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     647           27 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     648           14 :             std::cout << " " << copyrightNotice.data() << std::endl;
     649              :         }
     650           13 :         std::cout << "\n" << myFullName << " is part of SUMO.\n";
     651           13 :         std::cout << "This program and the accompanying materials\n";
     652           13 :         std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
     653           13 :         std::cout << "which accompanies this distribution, and is available at\n";
     654           13 :         std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
     655           13 :         std::cout << "This program may also be made available under the following Secondary\n";
     656           13 :         std::cout << "Licenses when the conditions for such availability set forth in the Eclipse\n";
     657           13 :         std::cout << "Public License 2.0 are satisfied: GNU General Public License, version 2\n";
     658           13 :         std::cout << "or later which is available at\n";
     659           13 :         std::cout << "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n";
     660              :         std::cout << "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later" << std::endl;
     661           13 :         return true;
     662              :     }
     663              :     // check whether the settings shall be printed
     664        96888 :     if (getBool("print-options")) {
     665            0 :         std::cout << (*this);
     666              :     }
     667              :     // check whether something has to be done with options
     668              :     // whether the current options shall be saved
     669        96888 :     if (isSet("save-configuration")) {
     670          509 :         const std::string& configPath = getString("save-configuration");
     671          509 :         if (configPath == "-" || configPath == "stdout") {
     672           24 :             writeConfiguration(std::cout, true, false, getBool("save-commented"));
     673           12 :             return true;
     674              :         }
     675          994 :         std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
     676          497 :         if (!out.good()) {
     677            0 :             throw ProcessError(TLF("Could not save configuration to '%'", configPath));
     678              :         } else {
     679          994 :             writeConfiguration(out, true, false, getBool("save-commented"), configPath);
     680          994 :             if (getBool("verbose")) {
     681          876 :                 WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
     682              :             }
     683              :             return true;
     684              :         }
     685          497 :     }
     686              :     // whether the template shall be saved
     687        95870 :     if (isSet("save-template")) {
     688           62 :         if (getString("save-template") == "-" || getString("save-template") == "stdout") {
     689            8 :             writeConfiguration(std::cout, false, true, getBool("save-commented"));
     690            4 :             return true;
     691              :         }
     692           36 :         std::ofstream out(StringUtils::transcodeToLocal(getString("save-template")).c_str());
     693           18 :         if (!out.good()) {
     694            0 :             throw ProcessError(TLF("Could not save template to '%'", getString("save-template")));
     695              :         } else {
     696           36 :             writeConfiguration(out, false, true, getBool("save-commented"));
     697           36 :             if (getBool("verbose")) {
     698            0 :                 WRITE_MESSAGEF(TL("Written template to '%'"), getString("save-template"));
     699              :             }
     700              :             return true;
     701              :         }
     702           18 :     }
     703        95826 :     if (isSet("save-schema")) {
     704            3 :         if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
     705            0 :             writeSchema(std::cout);
     706            0 :             return true;
     707              :         }
     708            2 :         std::ofstream out(StringUtils::transcodeToLocal(getString("save-schema")).c_str());
     709            1 :         if (!out.good()) {
     710            0 :             throw ProcessError(TLF("Could not save schema to '%'", getString("save-schema")));
     711              :         } else {
     712            1 :             writeSchema(out);
     713            2 :             if (getBool("verbose")) {
     714            0 :                 WRITE_MESSAGEF(TL("Written schema to '%'"), getString("save-schema"));
     715              :             }
     716              :             return true;
     717              :         }
     718            1 :     }
     719              :     return false;
     720              : }
     721              : 
     722              : 
     723              : void 
     724        48610 : OptionsCont::localizeDescriptions() {
     725        48610 :     if (!myAmLocalized && gLocaleInitialized) {
     726              :         // options
     727     19457074 :         for (auto option : myAddresses) {
     728     38817018 :             option.second->setDescription(TL(option.second->getDescription().c_str()));
     729              :         }
     730              :         // examples
     731       176944 :         for (auto example : myCallExamples) {
     732       128379 :             example.second = TL(example.second.c_str());
     733              :         }
     734              :         // other text
     735        48565 :         setApplicationDescription(TL(myAppDescription.c_str()));
     736        48565 :         myAmLocalized = true;
     737              :     }
     738        48610 : }
     739              : 
     740              : 
     741              : const std::vector<std::string>&
     742            0 : OptionsCont::getSubTopics() const {
     743            0 :     return mySubTopics;
     744              : }
     745              : 
     746              : 
     747              : std::vector<std::string>
     748            0 : OptionsCont::getSubTopicsEntries(const std::string& subtopic) const {
     749              :     if (mySubTopicEntries.count(subtopic) > 0) {
     750            0 :         return mySubTopicEntries.find(subtopic)->second;
     751              :     } else {
     752            0 :         return std::vector<std::string>();
     753              :     }
     754              : }
     755              : 
     756              : 
     757              : std::string
     758            0 : OptionsCont::getTypeName(const std::string name) {
     759            0 :     return getSecure(name)->getTypeName();
     760              : }
     761              : 
     762              : 
     763              : const std::string&
     764            1 : OptionsCont::getFullName() const {
     765            1 :     return myFullName;
     766              : }
     767              : 
     768              : 
     769              : bool
     770            0 : OptionsCont::isEmpty() const {
     771            0 :     return myAddresses.size() == 0;
     772              : }
     773              : 
     774              : 
     775              : std::vector<std::pair<std::string, Option*> >::const_iterator
     776            0 : OptionsCont::begin() const {
     777            0 :     return myAddresses.cbegin();
     778              : }
     779              : 
     780              : 
     781              : std::vector<std::pair<std::string, Option*> >::const_iterator
     782            0 : OptionsCont::end() const {
     783            0 :     return myAddresses.cend();
     784              : }
     785              : 
     786              : 
     787              : void
     788           77 : OptionsCont::printHelp(std::ostream& os) {
     789              :     // print application description
     790          154 :     splitLines(os, TL(myAppDescription.c_str()), 0, 0);
     791              :     os << std::endl;
     792              : 
     793              :     // check option sizes first
     794              :     //  we want to know how large the largest not-too-large-entry will be
     795              :     int tooLarge = 40;
     796              :     int maxSize = 0;
     797         1336 :     for (const auto& subTopic : mySubTopics) {
     798        21652 :         for (const auto& entry : mySubTopicEntries[subTopic]) {
     799        20393 :             Option* o = getSecure(entry);
     800              :             // name, two leading spaces and "--"
     801        20393 :             int csize = (int)entry.length() + 2 + 4;
     802              :             // abbreviation length ("-X, "->4chars) if any
     803        20393 :             const auto synonymes = getSynonymes(entry);
     804        23513 :             for (const auto& synonym : synonymes) {
     805         4802 :                 if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     806         1682 :                     csize += 4;
     807         1682 :                     break;
     808              :                 }
     809              :             }
     810              :             // the type name
     811        20393 :             if (!o->isBool()) {
     812        14029 :                 csize += 1 + (int)o->getTypeName().length();
     813              :             }
     814              :             // divider
     815        20393 :             csize += 2;
     816        20393 :             if (csize < tooLarge && maxSize < csize) {
     817              :                 maxSize = csize;
     818              :             }
     819        20393 :         }
     820              :     }
     821              : 
     822          154 :     const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
     823           77 :     if (helpTopic != "") {
     824              :         bool foundTopic = false;
     825            0 :         for (const auto& topic : mySubTopics) {
     826            0 :             if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
     827              :                 foundTopic = true;
     828            0 :                 printHelpOnTopic(topic, tooLarge, maxSize, os);
     829              :             }
     830              :         }
     831            0 :         if (!foundTopic) {
     832              :             // print topic list
     833            0 :             os << TL("Help Topics:") << std::endl;
     834            0 :             for (const std::string& t : mySubTopics) {
     835              :                 os << "    " << t << std::endl;
     836              :             }
     837              :         }
     838              :         return;
     839              :     }
     840              :     // print usage BNF
     841          154 :     os << TL("Usage: ") << myAppName << TL(" [OPTION]*") << std::endl;
     842              :     // print additional text if any
     843           77 :     if (myAdditionalMessage.length() > 0) {
     844              :         os << myAdditionalMessage << std::endl << std::endl;
     845              :     }
     846              :     // print the options
     847         1336 :     for (const auto& subTopic : mySubTopics) {
     848         1259 :         printHelpOnTopic(subTopic, tooLarge, maxSize, os);
     849              :     }
     850              :     os << std::endl;
     851              :     // print usage examples, calc size first
     852           77 :     if (myCallExamples.size() != 0) {
     853           77 :         os << TL("Examples:") << std::endl;
     854          225 :         for (const auto& callExample : myCallExamples) {
     855          148 :             os << "  " << myAppName << ' ' << callExample.first << std::endl;
     856              :             os << "    " << callExample.second << std::endl;
     857              :         }
     858              :     }
     859              :     os << std::endl;
     860           77 :     os << TLF("Report bugs at %.", "<https://github.com/eclipse-sumo/sumo/issues>") << std::endl;
     861          154 :     os << TLF("Get in contact via %.", "<sumo@dlr.de>") << std::endl;
     862              : }
     863              : 
     864              : 
     865              : void
     866         1259 : OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
     867         2518 :     os << TLF("% Options:", topic) << std::endl;
     868        21652 :     for (const auto& entry : mySubTopicEntries[topic]) {
     869              :         // start length computation
     870        20393 :         int csize = (int)entry.length() + 2;
     871        20393 :         Option* o = getSecure(entry);
     872        20393 :         os << "  ";
     873              :         // write abbreviation if given
     874        20393 :         const auto synonymes = getSynonymes(entry);
     875        23513 :         for (const auto& synonym : synonymes) {
     876         4802 :             if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     877         3364 :                 os << '-' << synonym << ", ";
     878         1682 :                 csize += 4;
     879         1682 :                 break;
     880              :             }
     881              :         }
     882              :         // write leading '-'/"--"
     883        20393 :         os << "--";
     884        20393 :         csize += 2;
     885              :         // write the name
     886              :         os << entry;
     887              :         // write the type if not a bool option
     888        20393 :         if (!o->isBool()) {
     889        14029 :             os << ' ' << o->getTypeName();
     890        14029 :             csize += 1 + (int)o->getTypeName().length();
     891              :         }
     892        20393 :         csize += 2;
     893              :         // write the description formatting it
     894        20393 :         os << "  ";
     895       205065 :         for (int r = maxSize; r > csize; --r) {
     896       184672 :             os << ' ';
     897              :         }
     898        20393 :         int offset = csize > tooLarge ? csize : maxSize;
     899        40786 :         splitLines(os, o->getDescription(), offset, maxSize);
     900        20393 :     }
     901              :     os << std::endl;
     902         1259 : }
     903              : 
     904              : 
     905              : void
     906        79532 : OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
     907              :                                 const bool complete, const bool addComments, const std::string& relativeTo,
     908              :                                 const bool forceRelative, const bool inComment, const std::string& indent) const {
     909        79532 :     if (!inComment) {
     910          764 :         writeXMLHeader(os, false);
     911              :     }
     912       151224 :     const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
     913              :     os << indent << "<" << app << "Configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
     914        79532 :        << "xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/" << app << "Configuration.xsd\">\n\n";
     915      2087258 :     for (std::string subtopic : mySubTopics) {
     916      2007726 :         if (subtopic == "Configuration" && !complete) {
     917              :             continue;
     918              :         }
     919              :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
     920              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
     921      3856432 :         subtopic = StringUtils::to_lower_case(subtopic);
     922              :         bool hadOne = false;
     923     35319064 :         for (const std::string& name : entries) {
     924     33390848 :             Option* o = getSecure(name);
     925     33390848 :             bool write = complete || (filled && !o->isDefault());
     926     32587837 :             if (!write) {
     927     32587837 :                 continue;
     928              :             }
     929       803011 :             if (name == "registry-viewport" && !complete) {
     930            0 :                 continue;
     931              :             }
     932       803011 :             if (!hadOne) {
     933       351929 :                 os << indent << "    <" << subtopic << ">\n";
     934              :             }
     935              :             // add the comment if wished
     936       803011 :             if (addComments) {
     937         4533 :                 os << indent << "        <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->\n";
     938              :             }
     939              :             // write the option and the value (if given)
     940       803011 :             os << indent << "        <" << name << " value=\"";
     941       803011 :             if (o->isSet() && (filled || o->isDefault())) {
     942       801963 :                 if (o->isFileName() && relativeTo != "") {
     943         7542 :                     StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
     944         5083 :                     for (auto& file : fileList) {
     945         5138 :                         if (StringUtils::startsWith(file, "${")) {
     946              :                             // there is an environment variable up front, assume it points to an absolute path
     947              :                             // not even forcing relativity makes sense here
     948           34 :                             file = StringUtils::urlEncode(file, " ;%");
     949              :                         } else {
     950         7656 :                             file = FileHelpers::fixRelative(
     951         5104 :                                        StringUtils::urlEncode(file, " ;%"),
     952         2552 :                                        StringUtils::urlEncode(relativeTo, " ;%"),
     953        10208 :                                        forceRelative || getBool("save-configuration.relative"));
     954              :                         }
     955              :                     }
     956         2514 :                     os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
     957         2514 :                 } else {
     958      1598898 :                     os << StringUtils::escapeXML(o->getValueString(), inComment);
     959              :                 }
     960              :             }
     961       803011 :             if (complete) {
     962         4549 :                 const std::vector<std::string> synonymes = getSynonymes(name);
     963         4549 :                 if (!synonymes.empty()) {
     964         2388 :                     os << "\" synonymes=\"" << toString(synonymes);
     965              :                 }
     966              :                 std::string deprecated;
     967         6233 :                 for (const auto& synonym : synonymes) {
     968              :                     if (myDeprecatedSynonymes.count(synonym) > 0) {
     969         1422 :                         deprecated += " " + synonym;
     970              :                     }
     971              :                 }
     972         4549 :                 if (deprecated != "") {
     973         1276 :                     os << "\" deprecated=\"" << deprecated.substr(1);
     974              :                 }
     975         4549 :                 os << "\" type=\"" << o->getTypeName();
     976         4549 :                 if (!addComments) {
     977         6076 :                     os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
     978              :                 }
     979         4549 :             }
     980       803011 :             os << "\"/>\n";
     981              :             // append an endline if a comment was printed
     982       803011 :             if (addComments) {
     983         1511 :                 os << "\n";
     984              :             }
     985              :             hadOne = true;
     986              :         }
     987      1928216 :         if (hadOne) {
     988       351929 :             os << indent << "    </" << subtopic << ">\n\n";
     989              :         }
     990              :     }
     991              :     os << indent << "</" << app << "Configuration>" << std::endl;  // flushing seems like a good idea here
     992        79532 : }
     993              : 
     994              : 
     995              : void
     996            1 : OptionsCont::writeSchema(std::ostream& os) {
     997            1 :     const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
     998            1 :     writeXMLHeader(os, false);
     999            1 :     os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n";
    1000            1 :     os << "    <xsd:complexType name=\"" << app << "ConfigurationType\">\n";
    1001            1 :     os << "        <xsd:all>\n";
    1002           28 :     for (std::string subtopic : mySubTopics) {
    1003           27 :         if (subtopic == "Configuration") {
    1004              :             continue;
    1005              :         }
    1006              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
    1007           52 :         subtopic = StringUtils::to_lower_case(subtopic);
    1008           26 :         os << "            <xsd:element name=\"" << subtopic << "\" type=\"" << app << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
    1009              :     }
    1010            1 :     os << "        </xsd:all>\n";
    1011            1 :     os << "    </xsd:complexType>\n\n";
    1012           28 :     for (std::string subtopic : mySubTopics) {
    1013           27 :         if (subtopic == "Configuration") {
    1014              :             continue;
    1015              :         }
    1016              :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
    1017              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
    1018           52 :         subtopic = StringUtils::to_lower_case(subtopic);
    1019           26 :         os << "    <xsd:complexType name=\"" << app << subtopic << "TopicType\">\n";
    1020           26 :         os << "        <xsd:all>\n";
    1021          474 :         for (const auto& entry : entries) {
    1022          448 :             Option* o = getSecure(entry);
    1023          448 :             std::string type = o->getTypeName();
    1024          448 :             type = StringUtils::to_lower_case(type);
    1025          448 :             if (type == "int[]") {
    1026              :                 type = "intArray";
    1027              :             }
    1028          448 :             if (type == "str[]") {
    1029              :                 type = "strArray";
    1030              :             }
    1031          448 :             os << "            <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
    1032              :         }
    1033           26 :         os << "        </xsd:all>\n";
    1034           26 :         os << "    </xsd:complexType>\n\n";
    1035              :     }
    1036            1 :     os << "</xsd:schema>\n";
    1037            1 : }
    1038              : 
    1039              : 
    1040              : void
    1041        79533 : OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
    1042        79533 :     os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
    1043        79533 :     os << "<!-- ";
    1044       159066 :     if (!getBool("write-metadata")) {
    1045       238596 :         os << "generated on " << StringUtils::isoTimeString() << " by " << myFullName << "\n";
    1046              :     }
    1047       159066 :     if (getBool("write-license")) {
    1048              :         os << "This data file and the accompanying materials\n"
    1049              :            "are made available under the terms of the Eclipse Public License v2.0\n"
    1050              :            "which accompanies this distribution, and is available at\n"
    1051              :            "http://www.eclipse.org/legal/epl-v20.html\n"
    1052              :            "This file may also be made available under the following Secondary\n"
    1053              :            "Licenses when the conditions for such availability set forth in the Eclipse\n"
    1054              :            "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
    1055              :            "or later which is available at\n"
    1056              :            "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
    1057        69038 :            "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
    1058              :     }
    1059       158302 :     if (includeConfig && !getBool("write-metadata")) {
    1060       157534 :         writeConfiguration(os, true, false, false, "", false, true);
    1061              :     }
    1062        79533 :     os << "-->\n\n";
    1063        79533 : }
    1064              : 
    1065              : 
    1066              : bool
    1067        39926 : OptionsCont::isInStringVector(const std::string& optionName,
    1068              :                               const std::string& itemName) const {
    1069        39926 :     if (isSet(optionName)) {
    1070            0 :         std::vector<std::string> values = getStringVector(optionName);
    1071            0 :         return std::find(values.begin(), values.end(), itemName) != values.end();
    1072            0 :     }
    1073              :     return false;
    1074              : }
    1075              : 
    1076              : 
    1077              : OptionsCont*
    1078          172 : OptionsCont::clone() const {
    1079              :     // build a clone to call writeConfiguration on
    1080              :     // (with the possibility of changing a few settings and not affecting the original)
    1081          172 :     OptionsCont* oc = new OptionsCont(*this);
    1082          172 :     oc->resetWritable();
    1083        62092 :     for (auto& addr : oc->myAddresses) {
    1084        61920 :         addr.second = addr.second->clone();
    1085              :     }
    1086          172 :     return oc;
    1087              : }
    1088              : 
    1089              : 
    1090              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1