LCOV - code coverage report
Current view: top level - src/utils/options - OptionsCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 80.0 % 519 415
Test Date: 2026-01-01 15:49:29 Functions: 73.4 % 64 47

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-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    943819322 : OptionsCont::getOptions() {
      62    943819322 :     return myOptions;
      63              : }
      64              : 
      65              : 
      66       100980 : OptionsCont::OptionsCont() {
      67       100980 :     myCopyrightNotices.push_back(TL("Copyright (C) 2001-2025 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
      68       100980 : }
      69              : 
      70              : 
      71       101152 : OptionsCont::~OptionsCont() {
      72       101152 :     clear();
      73       202304 : }
      74              : 
      75              : 
      76              : void
      77     25097404 : OptionsCont::doRegister(const std::string& name, Option* o) {
      78              :     // first check that option isn't null
      79     25097404 :     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     25097404 :     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   5384413016 :     for (const auto& addresse : myAddresses) {
      89   5359315612 :         if (addresse.second == o) {
      90              :             isSynonym = true;
      91              :         }
      92              :     }
      93     25097404 :     if (!isSynonym) {
      94     40838086 :         myAddresses.push_back(std::make_pair(name, o));
      95              :     }
      96              :     // insert in values
      97     25097404 :     myValues[name] = o;
      98     25097404 : }
      99              : 
     100              : 
     101              : void
     102      1326340 : OptionsCont::doRegister(const std::string& name1, char abbr, Option* o) {
     103      1326340 :     doRegister(name1, o);
     104      1326340 :     doRegister(convertChar(abbr), o);
     105      1326340 : }
     106              : 
     107              : 
     108              : void
     109      3352021 : 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      3352021 :     if (i1 == myValues.end() && i2 == myValues.end()) {
     113            0 :         throw ProcessError("Neither the option '" + name1 + "' nor the option '" + name2 + "' is known yet");
     114              :     }
     115      3352021 :     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      3352021 :     if (i1 == myValues.end() && i2 != myValues.end()) {
     122       108987 :         doRegister(name1, (*i2).second);
     123       108987 :         if (isDeprecated) {
     124            0 :             myDeprecatedSynonymes[name1] = false;
     125              :         }
     126              :     }
     127      3352021 :     if (i1 != myValues.end() && i2 == myValues.end()) {
     128      3243034 :         doRegister(name2, (*i1).second);
     129      3243034 :         if (isDeprecated) {
     130      1739068 :             myDeprecatedSynonymes[name2] = false;
     131              :         }
     132              :     }
     133              : }
     134              : 
     135              : 
     136              : void
     137        94151 : OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
     138        94151 :     myXMLDefaults[xmlRoot] = name;
     139        94151 : }
     140              : 
     141              : 
     142              : bool
     143    362577129 : OptionsCont::exists(const std::string& name) const {
     144    362577129 :     return myValues.count(name) > 0;
     145              : }
     146              : 
     147              : 
     148              : bool
     149    992567420 : OptionsCont::isSet(const std::string& name, bool failOnNonExistant) const {
     150              :     auto i = myValues.find(name);
     151    992567420 :     if (i == myValues.end()) {
     152          201 :         if (failOnNonExistant) {
     153            0 :             throw ProcessError(TLF("Internal request for unknown option '%'!", name));
     154              :         } else {
     155              :             return false;
     156              :         }
     157              :     }
     158    992567219 :     return (*i).second->isSet();
     159              : }
     160              : 
     161              : 
     162              : bool
     163      2203121 : OptionsCont::isDefault(const std::string& name) const {
     164              :     auto i = myValues.find(name);
     165      2203121 :     if (i == myValues.end()) {
     166              :         return false;
     167              :     }
     168      2202673 :     return (*i).second->isDefault();
     169              : }
     170              : 
     171              : 
     172              : Option*
     173    343388558 : OptionsCont::getSecure(const std::string& name) const {
     174              :     const auto& valuesFinder = myValues.find(name);
     175    343388558 :     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    343388504 :     if ((synonymFinder != myDeprecatedSynonymes.end()) && !synonymFinder->second) {
     180              :         std::string defaultName;
     181         7018 :         for (const auto& subtopicEntry : mySubTopicEntries) {
     182       118752 :             for (const auto& value : subtopicEntry.second) {
     183              :                 const auto l = myValues.find(value);
     184       112935 :                 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    343388504 :     return valuesFinder->second;
     197              : }
     198              : 
     199              : 
     200              : std::string
     201     14998631 : OptionsCont::getValueString(const std::string& name) const {
     202     14998631 :     Option* o = getSecure(name);
     203     14998631 :     return o->getValueString();
     204              : }
     205              : 
     206              : 
     207              : std::string
     208     29499257 : OptionsCont::getString(const std::string& name) const {
     209     29499257 :     Option* o = getSecure(name);
     210     29499257 :     return o->getString();
     211              : }
     212              : 
     213              : 
     214              : double
     215    112522736 : OptionsCont::getFloat(const std::string& name) const {
     216    112522736 :     Option* o = getSecure(name);
     217    112522736 :     return o->getFloat();
     218              : }
     219              : 
     220              : 
     221              : int
     222      2123182 : OptionsCont::getInt(const std::string& name) const {
     223      2123182 :     Option* o = getSecure(name);
     224      2123182 :     return o->getInt();
     225              : }
     226              : 
     227              : 
     228              : bool
     229    125514353 : OptionsCont::getBool(const std::string& name) const {
     230    125514353 :     Option* o = getSecure(name);
     231    125514353 :     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       522233 : OptionsCont::getStringVector(const std::string& name) const {
     243       522233 :     Option* o = getSecure(name);
     244       522233 :     return o->getStringVector();
     245              : }
     246              : 
     247              : 
     248              : bool
     249      1020728 : OptionsCont::set(const std::string& name, const std::string& value, const bool append) {
     250      1020728 :     Option* o = getSecure(name);
     251      1020710 :     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      2041402 :         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       153911 : OptionsCont::setDefault(const std::string& name, const std::string& value) {
     270       153911 :     Option* const o = getSecure(name);
     271       153911 :     if (o->isWriteable() && set(name, value)) {
     272       134919 :         o->resetDefault();
     273       134919 :         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        46016 : OptionsCont::getSynonymes(const std::string& name) const {
     293        46016 :     Option* o = getSecure(name);
     294              :     std::vector<std::string> synonymes;
     295     20988798 :     for (const auto& value : myValues) {
     296     20942782 :         if ((value.second == o) && (name != value.first)) {
     297        13212 :             synonymes.push_back(value.first);
     298              :         }
     299              :     }
     300        46016 :     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        12879 : OptionsCont::relocateFiles(const std::string& configuration) const {
     351      5755404 :     for (const auto& addresse : myAddresses) {
     352      5742525 :         if (addresse.second->isFileName() && addresse.second->isSet()) {
     353        76798 :             StringVector fileList = StringVector(addresse.second->getStringVector());
     354       164559 :             for (auto& file : fileList) {
     355        87761 :                 if (addresse.first != "configuration-file") {
     356       149764 :                     file = FileHelpers::checkForRelativity(file, configuration);
     357              :                 }
     358              :                 try {
     359       175520 :                     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       230394 :             StringVector rawList = StringTokenizer(addresse.second->getValueString(), ",").getVector();
     365       164559 :             for (auto& file : rawList) {
     366       175522 :                 file = FileHelpers::checkForRelativity(file, configuration);
     367              :             }
     368        76798 :             const std::string conv = joinToString(fileList, ',');
     369       153596 :             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        76798 :         }
     377              :     }
     378        12879 : }
     379              : 
     380              : 
     381              : bool
     382        79122 : OptionsCont::isUsableFileList(const std::string& name) const {
     383        79122 :     Option* const o = getSecure(name);
     384        79122 :     if (!o->isSet()) {
     385              :         return false;
     386              :     }
     387              :     // check whether the list of files is valid
     388              :     bool ok = true;
     389        71115 :     std::vector<std::string> files = getStringVector(name);
     390        71115 :     if (files.size() == 0) {
     391            0 :         WRITE_ERRORF(TL("The file list for '%' is empty."), name);
     392              :         ok = false;
     393              :     }
     394       159488 :     for (const auto& file : files) {
     395       176746 :         if (!FileHelpers::isReadable(file)) {
     396           30 :             if (file != "") {
     397           90 :                 WRITE_ERRORF(TL("File '%' is not accessible (%)."), file, std::strerror(errno));
     398              :                 ok = false;
     399              :             } else {
     400            0 :                 WRITE_WARNING(TL("Empty file name given; ignoring."));
     401              :             }
     402              :         }
     403              :     }
     404              :     return ok;
     405        71115 : }
     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      2931552 :     for (const auto& value : myValues) {
     417      2925520 :         if (std::find(seenSynonymes.begin(), seenSynonymes.end(), value.first) != seenSynonymes.end()) {
     418            1 :             continue;
     419              :         }
     420      3020784 :         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      1326340 : OptionsCont::convertChar(char abbr) const {
     450              :     char buf[2];
     451      1326340 :     buf[0] = abbr;
     452      1326340 :     buf[1] = 0;
     453      1326340 :     std::string s(buf);
     454      1326340 :     return s;
     455              : }
     456              : 
     457              : 
     458              : bool
     459       685598 : OptionsCont::isBool(const std::string& name) const {
     460       685598 :     Option* o = getSecure(name);
     461       685568 :     return o->isBool();
     462              : }
     463              : 
     464              : 
     465              : void
     466        62995 : OptionsCont::resetWritable() {
     467     25994401 :     for (const auto& addresse : myAddresses) {
     468     25931406 :         addresse.second->resetWritable();
     469              :     }
     470        62995 : }
     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        73780 : OptionsCont::isWriteable(const std::string& name) {
     489        73780 :     return getSecure(name)->isWriteable();
     490              : }
     491              : 
     492              : 
     493              : bool
     494            0 : OptionsCont::isEditable(const std::string& name) {
     495            0 :     return getSecure(name)->isEditable();
     496              : 
     497              : }
     498              : 
     499              : 
     500              : void
     501       185548 : OptionsCont::clear() {
     502              :     // delete only address (because synonyms placed in values aim to the same Option)
     503     20666683 :     for (const auto& addresse : myAddresses) {
     504     20481135 :         delete addresse.second;
     505              :     }
     506              :     myAddresses.clear();
     507              :     myValues.clear();
     508              :     mySubTopics.clear();
     509              :     mySubTopicEntries.clear();
     510       185548 : }
     511              : 
     512              : 
     513              : void
     514     20378330 : OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
     515              :                             const std::string& description) {
     516     20378330 :     Option* o = getSecure(name);
     517     20378330 :     if (o == nullptr) {
     518            0 :         throw ProcessError("Option doesn't exist");
     519              :     }
     520     20378330 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     521            0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     522              :     }
     523     20378330 :     o->setDescription(description);
     524     20378330 :     o->setSubtopic(subtopic);
     525     20378330 :     mySubTopicEntries[subtopic].push_back(name);
     526     20378330 : }
     527              : 
     528              : 
     529              : void
     530            0 : OptionsCont::setFurtherAttributes(const std::string& name, const std::string& subtopic, bool required, bool positional, const std::string& listSep) {
     531            0 :     Option* o = getSecure(name);
     532            0 :     if (o == nullptr) {
     533            0 :         throw ProcessError("Option doesn't exist");
     534              :     }
     535            0 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     536            0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     537              :     }
     538            0 :     if (required) {
     539            0 :         o->setRequired();
     540              :     }
     541            0 :     if (positional) {
     542            0 :         o->setPositional();
     543              :     }
     544            0 :     o->setListSeparator(listSep);
     545            0 : }
     546              : 
     547              : 
     548              : void
     549            0 : OptionsCont::setOptionEditable(const std::string& name, const bool value) {
     550            0 :     getSecure(name)->setEditable(value);
     551            0 : }
     552              : 
     553              : 
     554              : void
     555        49987 : OptionsCont::setApplicationName(const std::string& appName, const std::string& fullName) {
     556        49987 :     myAppName = appName;
     557        49987 :     myFullName = fullName;
     558        49987 : }
     559              : 
     560              : 
     561              : void
     562        99377 : OptionsCont::setApplicationDescription(const std::string& appDesc) {
     563        99377 :     myAppDescription = appDesc;
     564        99377 : }
     565              : 
     566              : 
     567              : void
     568       134168 : OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
     569       134168 :     myCallExamples.push_back(std::make_pair(example, desc));
     570       134168 : }
     571              : 
     572              : 
     573              : void
     574          120 : OptionsCont::setAdditionalHelpMessage(const std::string& add) {
     575          120 :     myAdditionalMessage = add;
     576          120 : }
     577              : 
     578              : 
     579              : void
     580           14 : OptionsCont::addCopyrightNotice(const std::string& copyrightLine) {
     581           14 :     myCopyrightNotices.push_back(copyrightLine);
     582           14 : }
     583              : 
     584              : 
     585              : void
     586            0 : OptionsCont::clearCopyrightNotices() {
     587              :     myCopyrightNotices.clear();
     588            0 : }
     589              : 
     590              : 
     591              : void
     592      1209802 : OptionsCont::addOptionSubTopic(const std::string& topic) {
     593      1209802 :     mySubTopics.push_back(topic);
     594      1209802 :     mySubTopicEntries[topic] = std::vector<std::string>();
     595      1209802 : }
     596              : 
     597              : 
     598              : void
     599        20616 : OptionsCont::splitLines(std::ostream& os, std::string what,
     600              :                         int offset, int nextOffset) {
     601        63900 :     while (what.length() > 0) {
     602        43284 :         if ((int)what.length() > 79 - offset) {
     603        22702 :             std::string::size_type splitPos = what.rfind(';', 79 - offset);
     604        22702 :             if (splitPos == std::string::npos) {
     605        22432 :                 splitPos = what.rfind(' ', 79 - offset);
     606              :             } else {
     607          270 :                 splitPos++;
     608              :             }
     609        22702 :             if (splitPos != std::string::npos) {
     610        22668 :                 os << what.substr(0, splitPos) << std::endl;
     611        22668 :                 what = what.substr(splitPos + 1);
     612       923174 :                 for (int r = 0; r < (nextOffset + 1); ++r) {
     613       900506 :                     os << ' ';
     614              :                 }
     615              :             } else {
     616              :                 os << what;
     617              :                 what = "";
     618              :             }
     619              :             offset = nextOffset;
     620              :         } else {
     621              :             os << what;
     622              :             what = "";
     623              :         }
     624              :     }
     625              :     os << std::endl;
     626        20616 : }
     627              : 
     628              : 
     629              : bool
     630        50030 : OptionsCont::processMetaOptions(bool missingOptions) {
     631        50030 :     MsgHandler::setupI18n(getString("language"));
     632        50030 :     localizeDescriptions();
     633        50030 :     if (missingOptions) {
     634              :         // no options are given
     635              :         std::cout << myFullName << std::endl;
     636           76 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     637          153 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     638           77 :             std::cout << " " << copyrightNotice.data() << std::endl;
     639              :         }
     640           76 :         std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
     641           76 :         std::cout << TL(" Use --help to get the list of options.") << std::endl;
     642           76 :         return true;
     643              :     }
     644              : 
     645              :     // check whether the help shall be printed
     646        99908 :     if (getBool("help")) {
     647              :         std::cout << myFullName << std::endl;
     648          155 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     649           78 :             std::cout << " " << copyrightNotice.data() << std::endl;
     650              :         }
     651           77 :         printHelp(std::cout);
     652           77 :         return true;
     653              :     }
     654              :     // check whether the help shall be printed
     655        99754 :     if (getBool("version")) {
     656              :         std::cout << myFullName << std::endl;
     657           13 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     658           27 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     659           14 :             std::cout << " " << copyrightNotice.data() << std::endl;
     660              :         }
     661           13 :         std::cout << "\n" << myFullName << " is part of SUMO.\n";
     662           13 :         std::cout << "This program and the accompanying materials\n";
     663           13 :         std::cout << "are made available under the terms of the Eclipse Public License v2.0\n";
     664           13 :         std::cout << "which accompanies this distribution, and is available at\n";
     665           13 :         std::cout << "http://www.eclipse.org/legal/epl-v20.html\n";
     666           13 :         std::cout << "This program may also be made available under the following Secondary\n";
     667           13 :         std::cout << "Licenses when the conditions for such availability set forth in the Eclipse\n";
     668           13 :         std::cout << "Public License 2.0 are satisfied: GNU General Public License, version 2\n";
     669           13 :         std::cout << "or later which is available at\n";
     670           13 :         std::cout << "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n";
     671              :         std::cout << "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later" << std::endl;
     672           13 :         return true;
     673              :     }
     674              :     // check whether the settings shall be printed
     675        99728 :     if (getBool("print-options")) {
     676            0 :         std::cout << (*this);
     677              :     }
     678              :     // check whether something has to be done with options
     679              :     // whether the current options shall be saved
     680        99728 :     if (isSet("save-configuration")) {
     681          509 :         const std::string& configPath = getString("save-configuration");
     682          509 :         if (configPath == "-" || configPath == "stdout") {
     683           24 :             writeConfiguration(std::cout, true, false, getBool("save-commented"));
     684           12 :             return true;
     685              :         }
     686          994 :         std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
     687          497 :         if (!out.good()) {
     688            0 :             throw ProcessError(TLF("Could not save configuration to '%'", configPath));
     689              :         } else {
     690          994 :             writeConfiguration(out, true, false, getBool("save-commented"), configPath);
     691          994 :             if (getBool("verbose")) {
     692          876 :                 WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
     693              :             }
     694              :             return true;
     695              :         }
     696          497 :     }
     697              :     // whether the template shall be saved
     698        98710 :     if (isSet("save-template")) {
     699           65 :         if (getString("save-template") == "-" || getString("save-template") == "stdout") {
     700           10 :             writeConfiguration(std::cout, false, true, getBool("save-commented"));
     701            5 :             return true;
     702              :         }
     703           36 :         std::ofstream out(StringUtils::transcodeToLocal(getString("save-template")).c_str());
     704           18 :         if (!out.good()) {
     705            0 :             throw ProcessError(TLF("Could not save template to '%'", getString("save-template")));
     706              :         } else {
     707           36 :             writeConfiguration(out, false, true, getBool("save-commented"));
     708           36 :             if (getBool("verbose")) {
     709            0 :                 WRITE_MESSAGEF(TL("Written template to '%'"), getString("save-template"));
     710              :             }
     711              :             return true;
     712              :         }
     713           18 :     }
     714        98664 :     if (isSet("save-schema")) {
     715            3 :         if (getString("save-schema") == "-" || getString("save-schema") == "stdout") {
     716            0 :             writeSchema(std::cout);
     717            0 :             return true;
     718              :         }
     719            2 :         std::ofstream out(StringUtils::transcodeToLocal(getString("save-schema")).c_str());
     720            1 :         if (!out.good()) {
     721            0 :             throw ProcessError(TLF("Could not save schema to '%'", getString("save-schema")));
     722              :         } else {
     723            1 :             writeSchema(out);
     724            2 :             if (getBool("verbose")) {
     725            0 :                 WRITE_MESSAGEF(TL("Written schema to '%'"), getString("save-schema"));
     726              :             }
     727              :             return true;
     728              :         }
     729            1 :     }
     730              :     return false;
     731              : }
     732              : 
     733              : 
     734              : void 
     735        50030 : OptionsCont::localizeDescriptions() {
     736        50030 :     if (!myAmLocalized && gLocaleInitialized) {
     737              :         // options
     738     20187810 :         for (auto option : myAddresses) {
     739     40275650 :             option.second->setDescription(TL(option.second->getDescription().c_str()));
     740              :         }
     741              :         // examples
     742       182563 :         for (auto example : myCallExamples) {
     743       132578 :             example.second = TL(example.second.c_str());
     744              :         }
     745              :         // other text
     746        49985 :         setApplicationDescription(TL(myAppDescription.c_str()));
     747        49985 :         myAmLocalized = true;
     748              :     }
     749        50030 : }
     750              : 
     751              : 
     752              : const std::vector<std::string>&
     753            0 : OptionsCont::getSubTopics() const {
     754            0 :     return mySubTopics;
     755              : }
     756              : 
     757              : 
     758              : std::vector<std::string>
     759            0 : OptionsCont::getSubTopicsEntries(const std::string& subtopic) const {
     760              :     if (mySubTopicEntries.count(subtopic) > 0) {
     761            0 :         return mySubTopicEntries.find(subtopic)->second;
     762              :     } else {
     763            0 :         return std::vector<std::string>();
     764              :     }
     765              : }
     766              : 
     767              : 
     768              : std::string
     769            0 : OptionsCont::getTypeName(const std::string name) {
     770            0 :     return getSecure(name)->getTypeName();
     771              : }
     772              : 
     773              : 
     774              : const std::string&
     775            1 : OptionsCont::getFullName() const {
     776            1 :     return myFullName;
     777              : }
     778              : 
     779              : 
     780              : bool
     781            0 : OptionsCont::isEmpty() const {
     782            0 :     return myAddresses.size() == 0;
     783              : }
     784              : 
     785              : 
     786              : std::vector<std::pair<std::string, Option*> >::const_iterator
     787            0 : OptionsCont::begin() const {
     788            0 :     return myAddresses.cbegin();
     789              : }
     790              : 
     791              : 
     792              : std::vector<std::pair<std::string, Option*> >::const_iterator
     793            0 : OptionsCont::end() const {
     794            0 :     return myAddresses.cend();
     795              : }
     796              : 
     797              : 
     798              : void
     799           77 : OptionsCont::printHelp(std::ostream& os) {
     800              :     // print application description
     801          154 :     splitLines(os, TL(myAppDescription.c_str()), 0, 0);
     802              :     os << std::endl;
     803              : 
     804              :     // check option sizes first
     805              :     //  we want to know how large the largest not-too-large-entry will be
     806              :     int tooLarge = 40;
     807              :     int maxSize = 0;
     808         1336 :     for (const auto& subTopic : mySubTopics) {
     809        21798 :         for (const auto& entry : mySubTopicEntries[subTopic]) {
     810        20539 :             Option* o = getSecure(entry);
     811              :             // name, two leading spaces and "--"
     812        20539 :             int csize = (int)entry.length() + 2 + 4;
     813              :             // abbreviation length ("-X, "->4chars) if any
     814        20539 :             const auto synonymes = getSynonymes(entry);
     815        23659 :             for (const auto& synonym : synonymes) {
     816         4835 :                 if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     817         1715 :                     csize += 4;
     818         1715 :                     break;
     819              :                 }
     820              :             }
     821              :             // the type name
     822        20539 :             if (!o->isBool()) {
     823        14142 :                 csize += 1 + (int)o->getTypeName().length();
     824              :             }
     825              :             // divider
     826        20539 :             csize += 2;
     827        20539 :             if (csize < tooLarge && maxSize < csize) {
     828              :                 maxSize = csize;
     829              :             }
     830        20539 :         }
     831              :     }
     832              : 
     833          154 :     const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
     834           77 :     if (helpTopic != "") {
     835              :         bool foundTopic = false;
     836            0 :         for (const auto& topic : mySubTopics) {
     837            0 :             if (StringUtils::to_lower_case(topic).find(helpTopic) != std::string::npos) {
     838              :                 foundTopic = true;
     839            0 :                 printHelpOnTopic(topic, tooLarge, maxSize, os);
     840              :             }
     841              :         }
     842            0 :         if (!foundTopic) {
     843              :             // print topic list
     844            0 :             os << TL("Help Topics:") << std::endl;
     845            0 :             for (const std::string& t : mySubTopics) {
     846              :                 os << "    " << t << std::endl;
     847              :             }
     848              :         }
     849              :         return;
     850              :     }
     851              :     // print usage BNF
     852          154 :     os << TL("Usage: ") << myAppName << TL(" [OPTION]*") << std::endl;
     853              :     // print additional text if any
     854           77 :     if (myAdditionalMessage.length() > 0) {
     855              :         os << myAdditionalMessage << std::endl << std::endl;
     856              :     }
     857              :     // print the options
     858         1336 :     for (const auto& subTopic : mySubTopics) {
     859         1259 :         printHelpOnTopic(subTopic, tooLarge, maxSize, os);
     860              :     }
     861              :     os << std::endl;
     862              :     // print usage examples, calc size first
     863           77 :     if (myCallExamples.size() != 0) {
     864           77 :         os << TL("Examples:") << std::endl;
     865          225 :         for (const auto& callExample : myCallExamples) {
     866          148 :             os << "  " << myAppName << ' ' << callExample.first << std::endl;
     867              :             os << "    " << callExample.second << std::endl;
     868              :         }
     869              :     }
     870              :     os << std::endl;
     871           77 :     os << TLF("Report bugs at %.", "<https://github.com/eclipse-sumo/sumo/issues>") << std::endl;
     872          154 :     os << TLF("Get in contact via %.", "<sumo@dlr.de>") << std::endl;
     873              : }
     874              : 
     875              : 
     876              : void
     877         1259 : OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
     878         2518 :     os << TLF("% Options:", topic) << std::endl;
     879        21798 :     for (const auto& entry : mySubTopicEntries[topic]) {
     880              :         // start length computation
     881        20539 :         int csize = (int)entry.length() + 2;
     882        20539 :         Option* o = getSecure(entry);
     883        20539 :         os << "  ";
     884              :         // write abbreviation if given
     885        20539 :         const auto synonymes = getSynonymes(entry);
     886        23659 :         for (const auto& synonym : synonymes) {
     887         4835 :             if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     888         3430 :                 os << '-' << synonym << ", ";
     889         1715 :                 csize += 4;
     890         1715 :                 break;
     891              :             }
     892              :         }
     893              :         // write leading '-'/"--"
     894        20539 :         os << "--";
     895        20539 :         csize += 2;
     896              :         // write the name
     897              :         os << entry;
     898              :         // write the type if not a bool option
     899        20539 :         if (!o->isBool()) {
     900        14142 :             os << ' ' << o->getTypeName();
     901        14142 :             csize += 1 + (int)o->getTypeName().length();
     902              :         }
     903        20539 :         csize += 2;
     904              :         // write the description formatting it
     905        20539 :         os << "  ";
     906       206862 :         for (int r = maxSize; r > csize; --r) {
     907       186323 :             os << ' ';
     908              :         }
     909        20539 :         int offset = csize > tooLarge ? csize : maxSize;
     910        41078 :         splitLines(os, o->getDescription(), offset, maxSize);
     911        20539 :     }
     912              :     os << std::endl;
     913         1259 : }
     914              : 
     915              : 
     916              : void
     917        84410 : OptionsCont::writeConfiguration(std::ostream& os, const bool filled,
     918              :                                 const bool complete, const bool addComments, const std::string& relativeTo,
     919              :                                 const bool forceRelative, const bool inComment, const std::string& indent) const {
     920        84410 :     if (!inComment) {
     921          765 :         writeXMLHeader(os, false);
     922              :     }
     923       160932 :     const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
     924              :     os << indent << "<" << app << "Configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
     925        84410 :        << "xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/" << app << "Configuration.xsd\">\n\n";
     926      2222787 :     for (std::string subtopic : mySubTopics) {
     927      2138377 :         if (subtopic == "Configuration" && !complete) {
     928              :             continue;
     929              :         }
     930              :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
     931              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
     932      4107980 :         subtopic = StringUtils::to_lower_case(subtopic);
     933              :         bool hadOne = false;
     934     37777015 :         for (const std::string& name : entries) {
     935     35723025 :             Option* o = getSecure(name);
     936     35723025 :             bool write = complete || (filled && !o->isDefault());
     937     35030916 :             if (!write) {
     938     35030916 :                 continue;
     939              :             }
     940       692109 :             if (name == "registry-viewport" && !complete) {
     941            0 :                 continue;
     942              :             }
     943       692109 :             if (!hadOne) {
     944       291669 :                 os << indent << "    <" << subtopic << ">\n";
     945              :             }
     946              :             // add the comment if wished
     947       692109 :             if (addComments) {
     948         4563 :                 os << indent << "        <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->\n";
     949              :             }
     950              :             // write the option and the value (if given)
     951       692109 :             os << indent << "        <" << name << " value=\"";
     952       692109 :             if (o->isSet() && (filled || o->isDefault())) {
     953       690954 :                 if (o->isFileName() && relativeTo != "") {
     954         7542 :                     StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
     955         5082 :                     for (auto& file : fileList) {
     956         5136 :                         if (StringUtils::startsWith(file, "${")) {
     957              :                             // there is an environment variable up front, assume it points to an absolute path
     958              :                             // not even forcing relativity makes sense here
     959           34 :                             file = StringUtils::urlEncode(file, " ;%");
     960              :                         } else {
     961         7653 :                             file = FileHelpers::fixRelative(
     962         5102 :                                        StringUtils::urlEncode(file, " ;%"),
     963         2551 :                                        StringUtils::urlEncode(relativeTo, " ;%"),
     964        10204 :                                        forceRelative || getBool("save-configuration.relative"));
     965              :                         }
     966              :                     }
     967         2514 :                     os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
     968         2514 :                 } else {
     969      1376880 :                     os << StringUtils::escapeXML(o->getValueString(), inComment);
     970              :                 }
     971              :             }
     972       692109 :             if (complete) {
     973         4937 :                 const std::vector<std::string> synonymes = getSynonymes(name);
     974         4937 :                 if (!synonymes.empty()) {
     975         2568 :                     os << "\" synonymes=\"" << toString(synonymes);
     976              :                 }
     977              :                 std::string deprecated;
     978         6748 :                 for (const auto& synonym : synonymes) {
     979              :                     if (myDeprecatedSynonymes.count(synonym) > 0) {
     980         1554 :                         deprecated += " " + synonym;
     981              :                     }
     982              :                 }
     983         4937 :                 if (deprecated != "") {
     984         1398 :                     os << "\" deprecated=\"" << deprecated.substr(1);
     985              :                 }
     986         4937 :                 os << "\" type=\"" << o->getTypeName();
     987         4937 :                 if (!addComments) {
     988         6832 :                     os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
     989              :                 }
     990         4937 :             }
     991       692109 :             os << "\"/>\n";
     992              :             // append an endline if a comment was printed
     993       692109 :             if (addComments) {
     994         1521 :                 os << "\n";
     995              :             }
     996              :             hadOne = true;
     997              :         }
     998      2053990 :         if (hadOne) {
     999       291669 :             os << indent << "    </" << subtopic << ">\n\n";
    1000              :         }
    1001              :     }
    1002              :     os << indent << "</" << app << "Configuration>" << std::endl;  // flushing seems like a good idea here
    1003        84410 : }
    1004              : 
    1005              : 
    1006              : void
    1007            1 : OptionsCont::writeSchema(std::ostream& os) {
    1008            1 :     const std::string& app = myAppName == "sumo-gui" ? "sumo" : myAppName;
    1009            1 :     writeXMLHeader(os, false);
    1010            1 :     os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n";
    1011            1 :     os << "    <xsd:complexType name=\"" << app << "ConfigurationType\">\n";
    1012            1 :     os << "        <xsd:all>\n";
    1013           28 :     for (std::string subtopic : mySubTopics) {
    1014           27 :         if (subtopic == "Configuration") {
    1015              :             continue;
    1016              :         }
    1017              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
    1018           52 :         subtopic = StringUtils::to_lower_case(subtopic);
    1019           26 :         os << "            <xsd:element name=\"" << subtopic << "\" type=\"" << app << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
    1020              :     }
    1021            1 :     os << "        </xsd:all>\n";
    1022            1 :     os << "    </xsd:complexType>\n\n";
    1023           28 :     for (std::string subtopic : mySubTopics) {
    1024           27 :         if (subtopic == "Configuration") {
    1025              :             continue;
    1026              :         }
    1027              :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
    1028              :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
    1029           52 :         subtopic = StringUtils::to_lower_case(subtopic);
    1030           26 :         os << "    <xsd:complexType name=\"" << app << subtopic << "TopicType\">\n";
    1031           26 :         os << "        <xsd:all>\n";
    1032          476 :         for (const auto& entry : entries) {
    1033          450 :             Option* o = getSecure(entry);
    1034          450 :             std::string type = o->getTypeName();
    1035          450 :             type = StringUtils::to_lower_case(type);
    1036          450 :             if (type == "int[]") {
    1037              :                 type = "intArray";
    1038              :             }
    1039          450 :             if (type == "str[]") {
    1040              :                 type = "strArray";
    1041              :             }
    1042          450 :             os << "            <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
    1043              :         }
    1044           26 :         os << "        </xsd:all>\n";
    1045           26 :         os << "    </xsd:complexType>\n\n";
    1046              :     }
    1047            1 :     os << "</xsd:schema>\n";
    1048            1 : }
    1049              : 
    1050              : 
    1051              : void
    1052        84411 : OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
    1053        84411 :     os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
    1054        84411 :     os << "<!-- ";
    1055       168822 :     if (!getBool("write-metadata")) {
    1056       253230 :         os << "generated on " << StringUtils::isoTimeString() << " by " << myFullName << "\n";
    1057              :     }
    1058       168822 :     if (getBool("write-license")) {
    1059              :         os << "This data file and the accompanying materials\n"
    1060              :            "are made available under the terms of the Eclipse Public License v2.0\n"
    1061              :            "which accompanies this distribution, and is available at\n"
    1062              :            "http://www.eclipse.org/legal/epl-v20.html\n"
    1063              :            "This file may also be made available under the following Secondary\n"
    1064              :            "Licenses when the conditions for such availability set forth in the Eclipse\n"
    1065              :            "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
    1066              :            "or later which is available at\n"
    1067              :            "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
    1068        33884 :            "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
    1069              :     }
    1070       168057 :     if (includeConfig && !getBool("write-metadata")) {
    1071       167288 :         writeConfiguration(os, true, false, false, "", false, true);
    1072              :     }
    1073        84411 :     os << "-->\n\n";
    1074        84411 : }
    1075              : 
    1076              : 
    1077              : bool
    1078        39926 : OptionsCont::isInStringVector(const std::string& optionName,
    1079              :                               const std::string& itemName) const {
    1080        39926 :     if (isSet(optionName)) {
    1081            0 :         std::vector<std::string> values = getStringVector(optionName);
    1082            0 :         return std::find(values.begin(), values.end(), itemName) != values.end();
    1083            0 :     }
    1084              :     return false;
    1085              : }
    1086              : 
    1087              : 
    1088              : OptionsCont*
    1089          172 : OptionsCont::clone() const {
    1090              :     // build a clone to call writeConfiguration on
    1091              :     // (with the possibility of changing a few settings and not affecting the original)
    1092          172 :     OptionsCont* oc = new OptionsCont(*this);
    1093          172 :     oc->resetWritable();
    1094        62264 :     for (auto& addr : oc->myAddresses) {
    1095        62092 :         addr.second = addr.second->clone();
    1096              :     }
    1097          172 :     return oc;
    1098              : }
    1099              : 
    1100              : 
    1101              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1