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

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

Generated by: LCOV version 2.0-1