LCOV - code coverage report
Current view: top level - src/utils/options - OptionsCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 80.0 % 499 399
Test Date: 2024-12-21 15:45:41 Functions: 73.8 % 61 45

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

Generated by: LCOV version 2.0-1