Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file TemplateHandler.cpp
15 : /// @author Pablo Alvarez Lopez
16 : /// @date Dec 2022
17 : ///
18 : // A SAX-Handler for loading templates
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <algorithm>
23 : #include <string>
24 : #include <vector>
25 : #include <xercesc/sax/HandlerBase.hpp>
26 : #include <xercesc/sax/AttributeList.hpp>
27 : #include <xercesc/sax/SAXParseException.hpp>
28 : #include <xercesc/sax/SAXException.hpp>
29 : #include <utils/common/StringUtils.h>
30 : #include <utils/xml/XMLSubSys.h>
31 : #include <utils/common/StringTokenizer.h>
32 : #include <utils/common/UtilExceptions.h>
33 : #include <utils/common/FileHelpers.h>
34 : #include <utils/common/MsgHandler.h>
35 : #include <utils/common/ToString.h>
36 : #include <utils/options/OptionsCont.h>
37 : #include <xercesc/parsers/SAXParser.hpp>
38 : #include <xercesc/sax2/XMLReaderFactory.hpp>
39 : #include <xercesc/framework/LocalFileInputSource.hpp>
40 : #include <xercesc/framework/MemBufInputSource.hpp>
41 :
42 : #include "TemplateHandler.h"
43 :
44 :
45 : const std::string TemplateHandler::INVALID_INT_STR = toString(INVALID_INT);
46 : const std::string TemplateHandler::INVALID_DOUBLE_STR = toString(INVALID_DOUBLE);
47 :
48 : // ===========================================================================
49 : // method definitions
50 : // ===========================================================================
51 :
52 : void
53 0 : TemplateHandler::parseTemplate(OptionsCont& options, const std::string& templateString) {
54 : // build parser
55 0 : XERCES_CPP_NAMESPACE::SAXParser parser;
56 : // disable validation
57 0 : parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never);
58 0 : parser.setDisableDefaultEntityResolution(true);
59 : // build TemplateHandler
60 0 : TemplateHandler handler(options);
61 : // start parsing
62 : try {
63 0 : parser.setDocumentHandler(&handler);
64 0 : parser.setErrorHandler(&handler);
65 0 : XERCES_CPP_NAMESPACE::MemBufInputSource memBufIS((const XMLByte*)templateString.c_str(), templateString.size(), "template");
66 0 : parser.parse(memBufIS);
67 0 : if (handler.myError) {
68 0 : throw ProcessError(TLF("Could not load template '%'.", templateString));
69 : }
70 0 : } catch (const XERCES_CPP_NAMESPACE::XMLException& e) {
71 0 : throw ProcessError("Could not load template '" + templateString + "':\n " + XMLSubSys::transcode(e.getMessage()));
72 0 : }
73 : // mark al loaded options as default
74 0 : options.resetDefault();
75 0 : }
76 :
77 0 : TemplateHandler::TemplateHandler(OptionsCont& options) :
78 0 : myError(false),
79 0 : myOptions(options) {
80 0 : }
81 :
82 :
83 0 : TemplateHandler::~TemplateHandler() {}
84 :
85 :
86 : void
87 0 : TemplateHandler::startElement(const XMLCh* const name, XERCES_CPP_NAMESPACE::AttributeList& attributes) {
88 : // get current topic
89 0 : myOptionName = XMLSubSys::transcode(name);
90 0 : if (myLevel++ == 0) {
91 : // skip root elemnt
92 : return;
93 : }
94 : // check if this is a subtopic
95 0 : if (attributes.getLength() == 0) {
96 0 : mySubTopic = myOptionName;
97 0 : myOptions.addOptionSubTopic(mySubTopic);
98 : } else {
99 : // declare options parameters (by default all empty)
100 : std::string value;
101 : std::string synonymes;
102 : std::string type;
103 : std::string help;
104 : bool required = false;
105 : bool positional = false;
106 0 : std::string listSep = "";
107 : // iterate over attributes
108 0 : for (int i = 0; i < (int)attributes.getLength(); i++) {
109 0 : const std::string attributeName = XMLSubSys::transcode(attributes.getName(i));
110 0 : const std::string attributeValue = XMLSubSys::transcode(attributes.getValue(i));
111 : // check attribute name
112 0 : if (attributeName == "value") {
113 : value = attributeValue;
114 0 : } else if (attributeName == "synonymes") {
115 : synonymes = attributeValue;
116 0 : } else if (attributeName == "type") {
117 : type = attributeValue;
118 0 : } else if (attributeName == "help") {
119 : help = attributeValue;
120 0 : } else if (attributeName == "category") {
121 : // tool templates have subtopic as attribute category
122 0 : mySubTopic = attributeValue;
123 0 : const auto& topics = myOptions.getSubTopics();
124 0 : if (std::find(topics.begin(), topics.end(), attributeValue) == topics.end()) {
125 0 : myOptions.addOptionSubTopic(attributeValue);
126 : }
127 0 : } else if (attributeName == "required") {
128 0 : required = StringUtils::toBool(attributeValue);
129 0 : } else if (attributeName == "positional") {
130 0 : positional = StringUtils::toBool(attributeValue);
131 0 : } else if (attributeName == "listSeparator") {
132 : listSep = attributeValue;
133 : }
134 : }
135 : // add option
136 0 : addOption(value, synonymes, type, help, required, positional, listSep);
137 : }
138 : }
139 :
140 :
141 : bool
142 0 : TemplateHandler::addOption(std::string value, const std::string& synonymes, const std::string& type,
143 : const std::string& help, bool required, bool positional, const std::string& listSep) const {
144 0 : if (myOptions.exists(myOptionName)) {
145 0 : WRITE_WARNING(myOptionName + " already exists");
146 0 : return false;
147 : } else {
148 : // declare option
149 : Option* option = nullptr;
150 : // handle "None" as empty
151 0 : if (value == "None") {
152 : value.clear();
153 : }
154 : // create register depending of type
155 0 : if ((type == "STR") || (type == "string")) {
156 0 : option = new Option_String(value);
157 0 : } else if ((type == "TIME") || (type == "time")) {
158 0 : option = new Option_String(value, "TIME");
159 0 : } else if ((type == "INT") || (type == "int")) {
160 0 : option = new Option_Integer(0);
161 0 : if (value.empty()) {
162 0 : option->set(INVALID_INT_STR, "", true);
163 : } else {
164 0 : option->set(value, value, true);
165 : }
166 0 : } else if ((type == "FLOAT") || (type == "float")) {
167 0 : option = new Option_Float(0);
168 0 : if (value.empty()) {
169 0 : option->set(INVALID_DOUBLE_STR, "", true);
170 : } else {
171 0 : option->set(value, value, true);
172 : }
173 0 : } else if ((type == "BOOL") || (type == "bool")) {
174 0 : option = new Option_Bool(false);
175 0 : if (value.empty()) {
176 0 : option->set("false", "", true);
177 : } else {
178 0 : option->set(value, value, true);
179 : }
180 0 : } else if (type == "INT[]") {
181 0 : option = new Option_IntVector();
182 0 : } else if (type == "STR[]") {
183 0 : option = new Option_StringVector();
184 0 : } else if ((type == "FILE") || (type == "file")) {
185 0 : option = new Option_FileName();
186 0 : } else if ((type == "NETWORK") || (type == "net_file")) {
187 0 : option = new Option_Network(value);
188 0 : } else if ((type == "ADDITIONAL") || (type == "additional_file")) {
189 0 : option = new Option_Additional(value);
190 0 : } else if ((type == "ROUTE") || (type == "route_file")) {
191 0 : option = new Option_Route(value);
192 0 : } else if ((type == "DATA") || (type == "data_file") || (type == "edgedata_file")) {
193 0 : option = new Option_Data(value);
194 0 : } else if ((type == "SUMOCONFIG") || (type == "sumoconfig_file")) {
195 0 : option = new Option_SumoConfig(value);
196 0 : } else if ((type == "EDGE") || (type == "edge")) {
197 0 : if (listSep.empty()) {
198 0 : option = new Option_Edge(value);
199 : } else {
200 0 : option = new Option_EdgeVector(value);
201 : }
202 0 : } else if (type.size() > 0) {
203 0 : WRITE_WARNING(type + " is an invalid type");
204 : }
205 : // check if option was created
206 : if (option) {
207 : // set value
208 0 : if (!option->isSet()) {
209 0 : option->set(value, "", false);
210 : }
211 0 : myOptions.doRegister(myOptionName, option);
212 : // check if add synonyme
213 0 : if (synonymes.size() > 0) {
214 0 : myOptions.addSynonyme(myOptionName, synonymes);
215 : }
216 : // check if add help
217 0 : if (help.size() > 0) {
218 0 : myOptions.addDescription(myOptionName, mySubTopic, help);
219 : }
220 0 : myOptions.setFurtherAttributes(myOptionName, mySubTopic, required, positional, listSep);
221 0 : return true;
222 : } else {
223 0 : return false;
224 : }
225 : }
226 : }
227 :
228 :
229 : void
230 0 : TemplateHandler::endElement(const XMLCh* const /*name*/) {
231 0 : myLevel--;
232 0 : if (myOptionName.length() == 0) {
233 : return;
234 : }
235 0 : myOptionName = "";
236 : }
237 :
238 :
239 : void
240 0 : TemplateHandler::warning(const XERCES_CPP_NAMESPACE::SAXParseException& exception) {
241 0 : WRITE_WARNING(XMLSubSys::transcode(exception.getMessage()));
242 0 : WRITE_WARNING(" (At line/column " \
243 : + toString(exception.getLineNumber() + 1) + '/' \
244 : + toString(exception.getColumnNumber()) + ").");
245 0 : myError = true;
246 0 : }
247 :
248 :
249 : void
250 0 : TemplateHandler::error(const XERCES_CPP_NAMESPACE::SAXParseException& exception) {
251 0 : WRITE_ERROR(XMLSubSys::transcode(exception.getMessage()));
252 0 : WRITE_ERROR(" (At line/column "
253 : + toString(exception.getLineNumber() + 1) + '/'
254 : + toString(exception.getColumnNumber()) + ").");
255 0 : myError = true;
256 0 : }
257 :
258 :
259 : void
260 0 : TemplateHandler::fatalError(const XERCES_CPP_NAMESPACE::SAXParseException& exception) {
261 0 : WRITE_ERROR(XMLSubSys::transcode(exception.getMessage()));
262 0 : WRITE_ERROR(" (At line/column "
263 : + toString(exception.getLineNumber() + 1) + '/'
264 : + toString(exception.getColumnNumber()) + ").");
265 0 : myError = true;
266 0 : }
267 :
268 : /****************************************************************************/
|