LCOV - code coverage report
Current view: top level - src/utils/options - OptionsCont.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 389 489 79.6 %
Date: 2024-05-01 15:34:42 Functions: 44 60 73.3 %

          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  1177272586 : OptionsCont::getOptions() {
      61  1177272586 :     return myOptions;
      62             : }
      63             : 
      64             : 
      65       96770 : OptionsCont::OptionsCont() {
      66       96770 :     myCopyrightNotices.push_back(TL("Copyright (C) 2001-2024 German Aerospace Center (DLR) and others; https://sumo.dlr.de"));
      67       96770 : }
      68             : 
      69             : 
      70       96770 : OptionsCont::~OptionsCont() {
      71       96770 :     clear();
      72      193540 : }
      73             : 
      74             : 
      75             : void
      76    20537144 : OptionsCont::doRegister(const std::string& name, Option* o) {
      77             :     // first check that option isn't null
      78    20537144 :     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    20537144 :     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  3848018724 :     for (const auto& addresse : myAddresses) {
      88  3827481580 :         if (addresse.second == o) {
      89             :             isSynonym = true;
      90             :         }
      91             :     }
      92    20537144 :     if (!isSynonym) {
      93    32661856 :         myAddresses.push_back(std::make_pair(name, o));
      94             :     }
      95             :     // insert in values
      96    20537144 :     myValues[name] = o;
      97    20537144 : }
      98             : 
      99             : 
     100             : void
     101     1193307 : OptionsCont::doRegister(const std::string& name1, char abbr, Option* o) {
     102     1193307 :     doRegister(name1, o);
     103     1193307 :     doRegister(convertChar(abbr), o);
     104     1193307 : }
     105             : 
     106             : 
     107             : void
     108     3012909 : 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     3012909 :     if (i1 == myValues.end() && i2 == myValues.end()) {
     112           0 :         throw ProcessError("Neither the option '" + name1 + "' nor the option '" + name2 + "' is known yet");
     113             :     }
     114     3012909 :     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     3012909 :     if (i1 == myValues.end() && i2 != myValues.end()) {
     121      112692 :         doRegister(name1, (*i2).second);
     122      112692 :         if (isDeprecated) {
     123           0 :             myDeprecatedSynonymes[name1] = false;
     124             :         }
     125             :     }
     126     3012909 :     if (i1 != myValues.end() && i2 == myValues.end()) {
     127     2900217 :         doRegister(name2, (*i1).second);
     128     2900217 :         if (isDeprecated) {
     129     1583356 :             myDeprecatedSynonymes[name2] = false;
     130             :         }
     131             :     }
     132             : }
     133             : 
     134             : 
     135             : void
     136       87006 : OptionsCont::addXMLDefault(const std::string& name, const std::string& xmlRoot) {
     137       87006 :     myXMLDefaults[xmlRoot] = name;
     138       87006 : }
     139             : 
     140             : 
     141             : bool
     142   301970194 : OptionsCont::exists(const std::string& name) const {
     143   301970194 :     return myValues.count(name) > 0;
     144             : }
     145             : 
     146             : 
     147             : bool
     148  1257628871 : OptionsCont::isSet(const std::string& name, bool failOnNonExistant) const {
     149             :     auto i = myValues.find(name);
     150  1257628871 :     if (i == myValues.end()) {
     151         246 :         if (failOnNonExistant) {
     152          10 :             throw ProcessError(TLF("Internal request for unknown option '%'!", name));
     153             :         } else {
     154             :             return false;
     155             :         }
     156             :     }
     157  1257628625 :     return (*i).second->isSet();
     158             : }
     159             : 
     160             : 
     161             : bool
     162     2006849 : OptionsCont::isDefault(const std::string& name) const {
     163             :     auto i = myValues.find(name);
     164     2006849 :     if (i == myValues.end()) {
     165             :         return false;
     166             :     }
     167     2006393 :     return (*i).second->isDefault();
     168             : }
     169             : 
     170             : 
     171             : Option*
     172   287271146 : OptionsCont::getSecure(const std::string& name) const {
     173             :     const auto& valuesFinder = myValues.find(name);
     174   287271146 :     if (valuesFinder == myValues.end()) {
     175         238 :         throw ProcessError(TLF("No option with the name '%' exists.", name));
     176             :     }
     177             :     const auto& synonymFinder = myDeprecatedSynonymes.find(name);
     178   287271092 :     if ((synonymFinder != myDeprecatedSynonymes.end()) && !synonymFinder->second) {
     179             :         std::string defaultName;
     180       14018 :         for (const auto& subtopicEntry : mySubTopicEntries) {
     181      206311 :             for (const auto& value : subtopicEntry.second) {
     182             :                 const auto l = myValues.find(value);
     183      194694 :                 if ((l != myValues.end()) && (l->second == valuesFinder->second)) {
     184             :                     defaultName = value;
     185             :                     break;
     186             :                 }
     187             :             }
     188       14018 :             if (defaultName != "") {
     189             :                 break;
     190             :             }
     191             :         }
     192        8003 :         WRITE_WARNINGF(TL("Please note that '%' is deprecated.\n Use '%' instead."), name, defaultName);
     193        2401 :         synonymFinder->second = true;
     194             :     }
     195   287271092 :     return valuesFinder->second;
     196             : }
     197             : 
     198             : 
     199             : std::string
     200     8836152 : OptionsCont::getValueString(const std::string& name) const {
     201     8836152 :     Option* o = getSecure(name);
     202     8836152 :     return o->getValueString();
     203             : }
     204             : 
     205             : 
     206             : std::string
     207    29155753 : OptionsCont::getString(const std::string& name) const {
     208    29155753 :     Option* o = getSecure(name);
     209    29155753 :     return o->getString();
     210             : }
     211             : 
     212             : 
     213             : double
     214    98856839 : OptionsCont::getFloat(const std::string& name) const {
     215    98856839 :     Option* o = getSecure(name);
     216    98856839 :     return o->getFloat();
     217             : }
     218             : 
     219             : 
     220             : int
     221     2017203 : OptionsCont::getInt(const std::string& name) const {
     222     2017203 :     Option* o = getSecure(name);
     223     2017203 :     return o->getInt();
     224             : }
     225             : 
     226             : 
     227             : bool
     228   113208966 : OptionsCont::getBool(const std::string& name) const {
     229   113208966 :     Option* o = getSecure(name);
     230   113208966 :     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     1087983 : OptionsCont::getStringVector(const std::string& name) const {
     242     1087983 :     Option* o = getSecure(name);
     243     1087983 :     return o->getStringVector();
     244             : }
     245             : 
     246             : 
     247             : bool
     248      954171 : OptionsCont::set(const std::string& name, const std::string& value, const bool append) {
     249      954171 :     Option* o = getSecure(name);
     250      954151 :     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     1908284 :         if (!o->set(StringUtils::substituteEnvironment(value, &OptionsIO::getLoadTime()), value, append)) {
     257           0 :             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      135305 : OptionsCont::setDefault(const std::string& name, const std::string& value) {
     269      135305 :     Option* const o = getSecure(name);
     270      135305 :     if (o->isWriteable() && set(name, value)) {
     271      118017 :         o->resetDefault();
     272      118017 :         return true;
     273             :     }
     274             :     return false;
     275             : }
     276             : 
     277             : 
     278             : bool
     279          52 : 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         102 :     if (myXMLDefaults.count("") > 0) {
     284         102 :         return set(myXMLDefaults[""], value);
     285             :     }
     286             :     return false;
     287             : }
     288             : 
     289             : 
     290             : std::vector<std::string>
     291       68961 : OptionsCont::getSynonymes(const std::string& name) const {
     292       68961 :     Option* o = getSecure(name);
     293             :     std::vector<std::string> synonymes;
     294    29185823 :     for (const auto& value : myValues) {
     295    29116862 :         if ((value.second == o) && (name != value.first)) {
     296       21158 :             synonymes.push_back(value.first);
     297             :         }
     298             :     }
     299       68961 :     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       10346 : OptionsCont::relocateFiles(const std::string& configuration) const {
     350     3995974 :     for (const auto& addresse : myAddresses) {
     351     3985628 :         if (addresse.second->isFileName() && addresse.second->isSet()) {
     352       59648 :             StringVector fileList = StringVector(addresse.second->getStringVector());
     353      120936 :             for (auto& file : fileList) {
     354       61288 :                 file = FileHelpers::checkForRelativity(file, configuration);
     355             :                 try {
     356      122573 :                     file = StringUtils::urlDecode(file);
     357           3 :                 } catch (NumberFormatException& e) {
     358           6 :                     WRITE_WARNING(toString(e.what()) + " when trying to decode filename '" + file + "'.");
     359           3 :                 }
     360             :             }
     361      198699 :             StringVector rawList = StringTokenizer(addresse.second->getValueString(), ",").getVector();
     362      120936 :             for (auto& file : rawList) {
     363      122576 :                 file = FileHelpers::checkForRelativity(file, configuration);
     364             :             }
     365       59648 :             const std::string conv = joinToString(fileList, ',');
     366      119296 :             if (conv != joinToString(addresse.second->getStringVector(), ',')) {
     367        5517 :                 const bool hadDefault = addresse.second->isDefault();
     368        5517 :                 addresse.second->set(conv, joinToString(rawList, ','), false);
     369        5517 :                 if (hadDefault) {
     370        1208 :                     addresse.second->resetDefault();
     371             :                 }
     372             :             }
     373       59648 :         }
     374             :     }
     375       10346 : }
     376             : 
     377             : 
     378             : bool
     379       70712 : OptionsCont::isUsableFileList(const std::string& name) const {
     380       70712 :     Option* const o = getSecure(name);
     381       70712 :     if (!o->isSet()) {
     382             :         return false;
     383             :     }
     384             :     // check whether the list of files is valid
     385             :     bool ok = true;
     386       62153 :     std::vector<std::string> files = getStringVector(name);
     387       62153 :     if (files.size() == 0) {
     388           0 :         WRITE_ERRORF(TL("The file list for '%' is empty."), name);
     389             :         ok = false;
     390             :     }
     391      132435 :     for (const auto& file : files) {
     392      140564 :         if (!FileHelpers::isReadable(file)) {
     393         126 :             if (file != "") {
     394         380 :                 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       62153 : }
     403             : 
     404             : 
     405             : bool
     406        6711 : OptionsCont::checkDependingSuboptions(const std::string& name, const std::string& prefix) const {
     407        6711 :     Option* o = getSecure(name);
     408        6711 :     if (o->isSet()) {
     409             :         return true;
     410             :     }
     411             :     bool ok = true;
     412             :     std::vector<std::string> seenSynonymes;
     413     3118472 :     for (const auto& value : myValues) {
     414     3111780 :         if (std::find(seenSynonymes.begin(), seenSynonymes.end(), value.first) != seenSynonymes.end()) {
     415           1 :             continue;
     416             :         }
     417     3211799 :         if (value.second->isSet() && !value.second->isDefault() && value.first.find(prefix) == 0) {
     418           4 :             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        6692 : }
     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     1193307 : OptionsCont::convertChar(char abbr) const {
     447             :     char buf[2];
     448     1193307 :     buf[0] = abbr;
     449     1193307 :     buf[1] = 0;
     450     1193307 :     std::string s(buf);
     451     1193307 :     return s;
     452             : }
     453             : 
     454             : 
     455             : bool
     456      647388 : OptionsCont::isBool(const std::string& name) const {
     457      647388 :     Option* o = getSecure(name);
     458      647360 :     return o->isBool();
     459             : }
     460             : 
     461             : 
     462             : void
     463       58473 : OptionsCont::resetWritable() {
     464    20282811 :     for (const auto& addresse : myAddresses) {
     465    20224338 :         addresse.second->resetWritable();
     466             :     }
     467       58473 : }
     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       83568 : OptionsCont::isWriteable(const std::string& name) {
     486       83568 :     Option* o = getSecure(name);
     487       83562 :     return o->isWriteable();
     488             : }
     489             : 
     490             : 
     491             : void
     492      174712 : OptionsCont::clear() {
     493             :     // delete only address (because synonyms placed in values aim to the same Option)
     494    16505640 :     for (const auto& addresse : myAddresses) {
     495    16330928 :         delete addresse.second;
     496             :     }
     497             :     myAddresses.clear();
     498             :     myValues.clear();
     499             :     mySubTopics.clear();
     500             :     mySubTopicEntries.clear();
     501      174712 : }
     502             : 
     503             : 
     504             : void
     505    16330928 : OptionsCont::addDescription(const std::string& name, const std::string& subtopic,
     506             :                             const std::string& description) {
     507    16330928 :     Option* o = getSecure(name);
     508    16330928 :     if (o == nullptr) {
     509           0 :         throw ProcessError("Option doesn't exist");
     510             :     }
     511    16330928 :     if (find(mySubTopics.begin(), mySubTopics.end(), subtopic) == mySubTopics.end()) {
     512           0 :         throw ProcessError("SubTopic '" + subtopic + "' doesn't exist");
     513             :     }
     514    16330928 :     o->setDescription(description);
     515    16330928 :     o->setSubtopic(subtopic);
     516    16330928 :     mySubTopicEntries[subtopic].push_back(name);
     517    16330928 : }
     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       47941 : OptionsCont::setApplicationName(const std::string& appName,
     541             :                                 const std::string& fullName) {
     542       47941 :     myAppName = appName;
     543       47941 :     myFullName = fullName;
     544       47941 : }
     545             : 
     546             : 
     547             : void
     548       47389 : OptionsCont::setApplicationDescription(const std::string& appDesc) {
     549       47389 :     myAppDescription = appDesc;
     550       47389 : }
     551             : 
     552             : 
     553             : void
     554      119819 : OptionsCont::addCallExample(const std::string& example, const std::string& desc) {
     555      239638 :     myCallExamples.push_back(std::make_pair(example, desc));
     556      119819 : }
     557             : 
     558             : 
     559             : void
     560         122 : OptionsCont::setAdditionalHelpMessage(const std::string& add) {
     561         122 :     myAdditionalMessage = add;
     562         122 : }
     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     1089143 : OptionsCont::addOptionSubTopic(const std::string& topic) {
     579     1089143 :     mySubTopics.push_back(topic);
     580     1089143 :     mySubTopicEntries[topic] = std::vector<std::string>();
     581     1089143 : }
     582             : 
     583             : 
     584             : void
     585       32855 : OptionsCont::splitLines(std::ostream& os, std::string what,
     586             :                         int offset, int nextOffset) {
     587      100482 :     while (what.length() > 0) {
     588       67627 :         if ((int)what.length() > 79 - offset) {
     589       34834 :             std::string::size_type splitPos = what.rfind(';', 79 - offset);
     590       34834 :             if (splitPos == std::string::npos) {
     591       34369 :                 splitPos = what.rfind(' ', 79 - offset);
     592             :             } else {
     593         465 :                 splitPos++;
     594             :             }
     595       34834 :             if (splitPos != std::string::npos) {
     596       34772 :                 os << what.substr(0, splitPos) << std::endl;
     597       34772 :                 what = what.substr(splitPos + 1);
     598     1416441 :                 for (int r = 0; r < (nextOffset + 1); ++r) {
     599     1381669 :                     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       32855 : }
     613             : 
     614             : 
     615             : bool
     616       48339 : OptionsCont::processMetaOptions(bool missingOptions) {
     617       96678 :     MsgHandler::setupI18n(getString("language"));
     618       48339 :     if (missingOptions) {
     619             :         // no options are given
     620             :         std::cout << myFullName << std::endl;
     621         134 :         std::cout << TL(" Build features: ") << HAVE_ENABLED << std::endl;
     622         269 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     623         135 :             std::cout << " " << copyrightNotice.data() << std::endl;
     624             :         }
     625         134 :         std::cout << TL(" License EPL-2.0: Eclipse Public License Version 2 <https://eclipse.org/legal/epl-v20.html>") << std::endl;
     626         134 :         std::cout << TL(" Use --help to get the list of options.") << std::endl;
     627         134 :         return true;
     628             :     }
     629             : 
     630             :     // check whether the help shall be printed
     631       96410 :     if (getBool("help")) {
     632             :         std::cout << myFullName << std::endl;
     633         267 :         for (const auto& copyrightNotice : myCopyrightNotices) {
     634         134 :             std::cout << " " << copyrightNotice.data() << std::endl;
     635             :         }
     636         133 :         printHelp(std::cout);
     637         133 :         return true;
     638             :     }
     639             :     // check whether the help shall be printed
     640       96144 :     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       96118 :     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       96118 :     if (isSet("save-configuration")) {
     666        1428 :         const std::string& configPath = getString("save-configuration");
     667        1428 :         if (configPath == "-" || configPath == "stdout") {
     668          48 :             writeConfiguration(std::cout, true, false, getBool("save-commented"));
     669          24 :             return true;
     670             :         }
     671        1380 :         std::ofstream out(StringUtils::transcodeToLocal(configPath).c_str());
     672         690 :         if (!out.good()) {
     673           0 :             throw ProcessError(TLF("Could not save configuration to '%'", configPath));
     674             :         } else {
     675         690 :             writeConfiguration(out, true, false, getBool("save-commented"), configPath);
     676        1380 :             if (getBool("verbose")) {
     677        1265 :                 WRITE_MESSAGEF(TL("Written configuration to '%'"), configPath);
     678             :             }
     679             :             return true;
     680             :         }
     681         690 :     }
     682             :     // whether the template shall be saved
     683       94690 :     if (isSet("save-template")) {
     684         102 :         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       94650 :     if (isSet("save-schema")) {
     700           5 :         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         133 : OptionsCont::printHelp(std::ostream& os) {
     767             :     // print application description
     768         266 :     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        2372 :     for (const auto& subTopic : mySubTopics) {
     776       34961 :         for (const auto& entry : mySubTopicEntries[subTopic]) {
     777       32722 :             Option* o = getSecure(entry);
     778             :             // name, two leading spaces and "--"
     779       32722 :             int csize = (int)entry.length() + 2 + 4;
     780             :             // abbreviation length ("-X, "->4chars) if any
     781       32722 :             const auto synonymes = getSynonymes(entry);
     782       38161 :             for (const auto& synonym : synonymes) {
     783        8381 :                 if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     784        2942 :                     csize += 4;
     785        2942 :                     break;
     786             :                 }
     787             :             }
     788             :             // the type name
     789       32722 :             if (!o->isBool()) {
     790       22346 :                 csize += 1 + (int)o->getTypeName().length();
     791             :             }
     792             :             // divider
     793       32722 :             csize += 2;
     794       32722 :             if (csize < tooLarge && maxSize < csize) {
     795             :                 maxSize = csize;
     796             :             }
     797       32722 :         }
     798             :     }
     799             : 
     800         266 :     const std::string helpTopic = StringUtils::to_lower_case(getSecure("help")->getValueString());
     801         133 :     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 (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         133 :     if (myAdditionalMessage.length() > 0) {
     822           1 :         os << myAdditionalMessage << std::endl << ' ' << std::endl;
     823             :     }
     824             :     // print the options
     825        2372 :     for (const auto& subTopic : mySubTopics) {
     826        2239 :         printHelpOnTopic(subTopic, tooLarge, maxSize, os);
     827             :     }
     828             :     os << std::endl;
     829             :     // print usage examples, calc size first
     830         133 :     if (myCallExamples.size() != 0) {
     831             :         os << "Examples:" << std::endl;
     832         393 :         for (const auto& callExample : myCallExamples) {
     833         260 :             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/issues>." << std::endl;
     839             :     os << "Get in contact via <sumo@dlr.de>." << std::endl;
     840             : }
     841             : 
     842             : 
     843             : void
     844        2239 : OptionsCont::printHelpOnTopic(const std::string& topic, int tooLarge, int maxSize, std::ostream& os) {
     845             :     os << topic << " Options:" << std::endl;
     846       34961 :     for (const auto& entry : mySubTopicEntries[topic]) {
     847             :         // start length computation
     848       32722 :         int csize = (int)entry.length() + 2;
     849       32722 :         Option* o = getSecure(entry);
     850       32722 :         os << "  ";
     851             :         // write abbreviation if given
     852       32722 :         const auto synonymes = getSynonymes(entry);
     853       38161 :         for (const auto& synonym : synonymes) {
     854        8381 :             if (synonym.length() == 1 && myDeprecatedSynonymes.count(synonym) == 0) {
     855        2942 :                 os << '-' << synonym << ", ";
     856        2942 :                 csize += 4;
     857        2942 :                 break;
     858             :             }
     859             :         }
     860             :         // write leading '-'/"--"
     861       32722 :         os << "--";
     862       32722 :         csize += 2;
     863             :         // write the name
     864             :         os << entry;
     865             :         // write the type if not a bool option
     866       32722 :         if (!o->isBool()) {
     867       22346 :             os << ' ' << o->getTypeName();
     868       22346 :             csize += 1 + (int)o->getTypeName().length();
     869             :         }
     870       32722 :         csize += 2;
     871             :         // write the description formatting it
     872       32722 :         os << "  ";
     873      334848 :         for (int r = maxSize; r > csize; --r) {
     874      302126 :             os << ' ';
     875             :         }
     876       32722 :         int offset = csize > tooLarge ? csize : maxSize;
     877       65444 :         splitLines(os, o->getDescription(), offset, maxSize);
     878       32722 :     }
     879             :     os << std::endl;
     880        2239 : }
     881             : 
     882             : 
     883             : void
     884       44490 : 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       44490 :     if (!inComment) {
     888         791 :         writeXMLHeader(os, false);
     889             :     }
     890       44490 :     os << "<configuration xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://sumo.dlr.de/xsd/";
     891       44490 :     if (myAppName == "sumo-gui") {
     892        6945 :         os << "sumo";
     893             :     } else {
     894             :         os << myAppName;
     895             :     }
     896             :     os << "Configuration.xsd\">" << std::endl << std::endl;
     897     1097816 :     for (std::string subtopic : mySubTopics) {
     898     1053326 :         if (subtopic == "Configuration" && !complete) {
     899             :             continue;
     900             :         }
     901             :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
     902             :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
     903     2017712 :         subtopic = StringUtils::to_lower_case(subtopic);
     904             :         bool hadOne = false;
     905    16753384 :         for (const std::string& name : entries) {
     906    15744528 :             Option* o = getSecure(name);
     907    15744528 :             bool write = complete || (filled && !o->isDefault());
     908    15270780 :             if (!write) {
     909    15270780 :                 continue;
     910             :             }
     911      473748 :             if (name == "registry-viewport" && !complete) {
     912           0 :                 continue;
     913             :             }
     914      473748 :             if (!hadOne) {
     915             :                 os << "    <" << subtopic << ">" << std::endl;
     916             :             }
     917             :             // add the comment if wished
     918      473748 :             if (addComments) {
     919        2782 :                 os << "        <!-- " << StringUtils::escapeXML(o->getDescription(), inComment) << " -->" << std::endl;
     920             :             }
     921             :             // write the option and the value (if given)
     922      473748 :             os << "        <" << name << " value=\"";
     923      473748 :             if (o->isSet() && (filled || o->isDefault())) {
     924      653046 :                 if (o->isFileName() && relativeTo != "") {
     925       12793 :                     StringVector fileList = StringTokenizer(o->getValueString(), ",").getVector();
     926        6710 :                     for (auto& file : fileList) {
     927       13628 :                         file = FileHelpers::fixRelative(
     928        6814 :                                    StringUtils::urlEncode(file, " ;%"),
     929        6814 :                                    StringUtils::urlEncode(relativeTo, " ;%"),
     930       13628 :                                    forceRelative || getBool("save-configuration.relative"));
     931             :                     }
     932        6606 :                     os << StringUtils::escapeXML(joinToString(fileList, ','), inComment);
     933        3303 :                 } else {
     934      939212 :                     os << StringUtils::escapeXML(o->getValueString(), inComment);
     935             :                 }
     936             :             }
     937      473748 :             if (complete) {
     938        3516 :                 std::vector<std::string> synonymes = getSynonymes(name);
     939        3516 :                 if (!synonymes.empty()) {
     940        1011 :                     os << "\" synonymes=\"";
     941        2444 :                     for (auto synonym = synonymes.begin(); synonym != synonymes.end(); synonym++) {
     942        1433 :                         if (synonym != synonymes.begin()) {
     943         422 :                             os << " ";
     944             :                         }
     945             :                         os << (*synonym);
     946             :                     }
     947             :                 }
     948        3516 :                 os << "\" type=\"" << o->getTypeName();
     949        3516 :                 if (!addComments) {
     950        4250 :                     os << "\" help=\"" << StringUtils::escapeXML(o->getDescription());
     951             :                 }
     952        3516 :             }
     953             :             os << "\"/>" << std::endl;
     954             :             // append an endline if a comment was printed
     955      473748 :             if (addComments) {
     956             :                 os << std::endl;
     957             :             }
     958             :             hadOne = true;
     959             :         }
     960     1008856 :         if (hadOne) {
     961             :             os << "    </" << subtopic << ">" << std::endl << std::endl;
     962             :         }
     963             :     }
     964             :     os << "</configuration>" << std::endl;
     965       44490 : }
     966             : 
     967             : 
     968             : void
     969           1 : OptionsCont::writeSchema(std::ostream& os) {
     970           1 :     writeXMLHeader(os, false);
     971           1 :     os << "<xsd:schema elementFormDefault=\"qualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n\n";
     972           1 :     os << "    <xsd:include schemaLocation=\"baseTypes.xsd\"/>\n";
     973           1 :     os << "    <xsd:element name=\"configuration\" type=\"configurationType\"/>\n\n";
     974           1 :     os << "    <xsd:complexType name=\"configurationType\">\n";
     975           1 :     os << "        <xsd:all>\n";
     976          28 :     for (std::string subtopic : mySubTopics) {
     977          27 :         if (subtopic == "Configuration") {
     978             :             continue;
     979             :         }
     980             :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
     981          52 :         subtopic = StringUtils::to_lower_case(subtopic);
     982          26 :         os << "            <xsd:element name=\"" << subtopic << "\" type=\"" << subtopic << "TopicType\" minOccurs=\"0\"/>\n";
     983             :     }
     984           1 :     os << "        </xsd:all>\n";
     985           1 :     os << "    </xsd:complexType>\n\n";
     986          28 :     for (std::string subtopic : mySubTopics) {
     987          27 :         if (subtopic == "Configuration") {
     988             :             continue;
     989             :         }
     990             :         const std::vector<std::string>& entries = mySubTopicEntries.find(subtopic)->second;
     991             :         std::replace(subtopic.begin(), subtopic.end(), ' ', '_');
     992          52 :         subtopic = StringUtils::to_lower_case(subtopic);
     993          26 :         os << "    <xsd:complexType name=\"" << subtopic << "TopicType\">\n";
     994          26 :         os << "        <xsd:all>\n";
     995         427 :         for (const auto& entry : entries) {
     996         401 :             Option* o = getSecure(entry);
     997         401 :             std::string type = o->getTypeName();
     998         802 :             type = StringUtils::to_lower_case(type);
     999         401 :             if (type == "int[]") {
    1000             :                 type = "intArray";
    1001             :             }
    1002         401 :             if (type == "str[]") {
    1003             :                 type = "strArray";
    1004             :             }
    1005         401 :             os << "            <xsd:element name=\"" << entry << "\" type=\"" << type << "OptionType\" minOccurs=\"0\"/>\n";
    1006             :         }
    1007          26 :         os << "        </xsd:all>\n";
    1008          26 :         os << "    </xsd:complexType>\n\n";
    1009             :     }
    1010           1 :     os << "</xsd:schema>\n";
    1011           1 : }
    1012             : 
    1013             : 
    1014             : void
    1015       44491 : OptionsCont::writeXMLHeader(std::ostream& os, const bool includeConfig) const {
    1016             :     time_t rawtime;
    1017             :     char buffer [80];
    1018             : 
    1019       44491 :     os << "<?xml version=\"1.0\"" << SUMOSAXAttributes::ENCODING << "?>\n\n";
    1020       44491 :     time(&rawtime);
    1021       44491 :     strftime(buffer, 80, "<!-- generated on %F %T by ", localtime(&rawtime));
    1022       44491 :     os << buffer << myFullName << "\n";
    1023       88982 :     if (getBool("write-license")) {
    1024             :         os << "This data file and the accompanying materials\n"
    1025             :            "are made available under the terms of the Eclipse Public License v2.0\n"
    1026             :            "which accompanies this distribution, and is available at\n"
    1027             :            "http://www.eclipse.org/legal/epl-v20.html\n"
    1028             :            "This file may also be made available under the following Secondary\n"
    1029             :            "Licenses when the conditions for such availability set forth in the Eclipse\n"
    1030             :            "Public License 2.0 are satisfied: GNU General Public License, version 2\n"
    1031             :            "or later which is available at\n"
    1032             :            "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html\n"
    1033       29560 :            "SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later\n";
    1034             :     }
    1035       44491 :     if (includeConfig) {
    1036       87398 :         writeConfiguration(os, true, false, false, "", false, true);
    1037             :     }
    1038       44491 :     os << "-->\n\n";
    1039       44491 : }
    1040             : 
    1041             : 
    1042             : bool
    1043       62547 : OptionsCont::isInStringVector(const std::string& optionName,
    1044             :                               const std::string& itemName) const {
    1045       62547 :     if (isSet(optionName)) {
    1046           0 :         std::vector<std::string> values = getStringVector(optionName);
    1047           0 :         return std::find(values.begin(), values.end(), itemName) != values.end();
    1048           0 :     }
    1049             :     return false;
    1050             : }
    1051             : 
    1052             : /****************************************************************************/

Generated by: LCOV version 1.14