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 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/common/StringTokenizer.h>
31 : #include <utils/common/UtilExceptions.h>
32 : #include <utils/common/FileHelpers.h>
33 : #include <utils/common/MsgHandler.h>
34 : #include <utils/common/ToString.h>
35 : #include <utils/options/OptionsCont.h>
36 : #include <xercesc/parsers/SAXParser.hpp>
37 : #include <xercesc/sax2/XMLReaderFactory.hpp>
38 : #include <xercesc/framework/LocalFileInputSource.hpp>
39 : #include <xercesc/framework/MemBufInputSource.hpp>
40 :
41 : #include "TemplateHandler.h"
42 :
43 :
44 : const std::string TemplateHandler::INVALID_INT_STR = toString(INVALID_INT);
45 : const std::string TemplateHandler::INVALID_DOUBLE_STR = toString(INVALID_DOUBLE);
46 :
47 : // ===========================================================================
48 : // method definitions
49 : // ===========================================================================
50 :
51 : void
52 0 : TemplateHandler::parseTemplate(OptionsCont& options, const std::string& templateString) {
53 : // build parser
54 0 : XERCES_CPP_NAMESPACE::SAXParser parser;
55 : // disable validation
56 0 : parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never);
57 0 : parser.setDisableDefaultEntityResolution(true);
58 : // build TemplateHandler
59 0 : TemplateHandler handler(options);
60 : // start parsing
61 : try {
62 0 : parser.setDocumentHandler(&handler);
63 0 : parser.setErrorHandler(&handler);
64 0 : XERCES_CPP_NAMESPACE::MemBufInputSource memBufIS((const XMLByte*)templateString.c_str(), templateString.size(), "template");
65 0 : parser.parse(memBufIS);
66 0 : if (handler.myError) {
67 0 : throw ProcessError(TLF("Could not load template '%'.", templateString));
68 : }
69 0 : } catch (const XERCES_CPP_NAMESPACE::XMLException& e) {
70 0 : throw ProcessError("Could not load template '" + templateString + "':\n " + StringUtils::transcode(e.getMessage()));
71 0 : }
72 : // mark al loaded options as default
73 0 : options.resetDefault();
74 0 : }
75 :
76 0 : TemplateHandler::TemplateHandler(OptionsCont& options) :
77 0 : myError(false),
78 0 : myOptions(options) {
79 0 : }
80 :
81 :
82 0 : TemplateHandler::~TemplateHandler() {}
83 :
84 :
85 : void
86 0 : TemplateHandler::startElement(const XMLCh* const name, XERCES_CPP_NAMESPACE::AttributeList& attributes) {
87 : // get current topic
88 0 : myOptionName = StringUtils::transcode(name);
89 0 : if (myLevel++ == 0) {
90 : // skip root elemnt
91 : return;
92 : }
93 : // check if this is a subtopic
94 0 : if (attributes.getLength() == 0) {
95 0 : mySubTopic = myOptionName;
96 0 : myOptions.addOptionSubTopic(mySubTopic);
97 : } else {
98 : // declare options parameters (by default all empty)
99 : std::string value;
100 : std::string synonymes;
101 : std::string type;
102 : std::string help;
103 : bool required = false;
104 : bool positional = false;
105 0 : std::string listSep = "";
106 : // iterate over attributes
107 0 : for (int i = 0; i < (int)attributes.getLength(); i++) {
108 0 : const std::string attributeName = StringUtils::transcode(attributes.getName(i));
109 0 : const std::string attributeValue = StringUtils::transcode(attributes.getValue(i));
110 : // check attribute name
111 0 : if (attributeName == "value") {
112 : value = attributeValue;
113 0 : } else if (attributeName == "synonymes") {
114 : synonymes = attributeValue;
115 0 : } else if (attributeName == "type") {
116 : type = attributeValue;
117 0 : } else if (attributeName == "help") {
118 : help = attributeValue;
119 0 : } else if (attributeName == "category") {
120 : // tool templates have subtopic as attribute category
121 0 : mySubTopic = attributeValue;
122 0 : const auto& topics = myOptions.getSubTopics();
123 0 : if (std::find(topics.begin(), topics.end(), attributeValue) == topics.end()) {
124 0 : myOptions.addOptionSubTopic(attributeValue);
125 : }
126 0 : } else if (attributeName == "required") {
127 0 : required = StringUtils::toBool(attributeValue);
128 0 : } else if (attributeName == "positional") {
129 0 : positional = StringUtils::toBool(attributeValue);
130 0 : } else if (attributeName == "listSeparator") {
131 : listSep = attributeValue;
132 : }
133 : }
134 : // add option
135 0 : addOption(value, synonymes, type, help, required, positional, listSep);
136 : }
137 : }
138 :
139 :
140 : bool
141 0 : TemplateHandler::addOption(std::string value, const std::string& synonymes, const std::string& type,
142 : const std::string& help, bool required, bool positional, const std::string& listSep) const {
143 0 : if (myOptions.exists(myOptionName)) {
144 0 : WRITE_WARNING(myOptionName + " already exists");
145 0 : return false;
146 : } else {
147 : // declare option
148 : Option* option = nullptr;
149 : // handle "None" as empty
150 0 : if (value == "None") {
151 : value.clear();
152 : }
153 : // create register depending of type
154 0 : if ((type == "STR") || (type == "string")) {
155 0 : option = new Option_String(value);
156 0 : } else if ((type == "TIME") || (type == "time")) {
157 0 : option = new Option_String(value, "TIME");
158 0 : } else if ((type == "INT") || (type == "int")) {
159 0 : option = new Option_Integer(0);
160 0 : if (value.empty()) {
161 0 : option->set(INVALID_INT_STR, "", true);
162 : } else {
163 0 : option->set(value, value, true);
164 : }
165 0 : } else if ((type == "FLOAT") || (type == "float")) {
166 0 : option = new Option_Float(0);
167 0 : if (value.empty()) {
168 0 : option->set(INVALID_DOUBLE_STR, "", true);
169 : } else {
170 0 : option->set(value, value, true);
171 : }
172 0 : } else if ((type == "BOOL") || (type == "bool")) {
173 0 : option = new Option_Bool(false);
174 0 : if (value.empty()) {
175 0 : option->set("false", "", true);
176 : } else {
177 0 : option->set(value, value, true);
178 : }
179 0 : } else if (type == "INT[]") {
180 0 : option = new Option_IntVector();
181 0 : } else if (type == "STR[]") {
182 0 : option = new Option_StringVector();
183 0 : } else if ((type == "FILE") || (type == "file")) {
184 0 : option = new Option_FileName();
185 0 : } else if ((type == "NETWORK") || (type == "net_file")) {
186 0 : option = new Option_Network(value);
187 0 : } else if ((type == "ADDITIONAL") || (type == "additional_file")) {
188 0 : option = new Option_Additional(value);
189 0 : } else if ((type == "ROUTE") || (type == "route_file")) {
190 0 : option = new Option_Route(value);
191 0 : } else if ((type == "DATA") || (type == "data_file") || (type == "edgedata_file")) {
192 0 : option = new Option_Data(value);
193 0 : } else if ((type == "SUMOCONFIG") || (type == "sumoconfig_file")) {
194 0 : option = new Option_SumoConfig(value);
195 0 : } else if ((type == "EDGE") || (type == "edge")) {
196 0 : if (listSep.empty()) {
197 0 : option = new Option_Edge(value);
198 : } else {
199 0 : option = new Option_EdgeVector(value);
200 : }
201 0 : } else if (type.size() > 0) {
202 0 : WRITE_WARNING(type + " is an invalid type");
203 : }
204 : // check if option was created
205 : if (option) {
206 : // set value
207 0 : if (!option->isSet()) {
208 0 : option->set(value, "", false);
209 : }
210 0 : myOptions.doRegister(myOptionName, option);
211 : // check if add synonyme
212 0 : if (synonymes.size() > 0) {
213 0 : myOptions.addSynonyme(myOptionName, synonymes);
214 : }
215 : // check if add help
216 0 : if (help.size() > 0) {
217 0 : myOptions.addDescription(myOptionName, mySubTopic, help);
218 : }
219 0 : myOptions.setFurtherAttributes(myOptionName, mySubTopic, required, positional, listSep);
220 0 : return true;
221 : } else {
222 0 : return false;
223 : }
224 : }
225 : }
226 :
227 :
228 : void
229 0 : TemplateHandler::endElement(const XMLCh* const /*name*/) {
230 0 : myLevel--;
231 0 : if (myOptionName.length() == 0) {
232 : return;
233 : }
234 0 : myOptionName = "";
235 : }
236 :
237 :
238 : void
239 0 : TemplateHandler::warning(const XERCES_CPP_NAMESPACE::SAXParseException& exception) {
240 0 : WRITE_WARNING(StringUtils::transcode(exception.getMessage()));
241 0 : WRITE_WARNING(" (At line/column " \
242 : + toString(exception.getLineNumber() + 1) + '/' \
243 : + toString(exception.getColumnNumber()) + ").");
244 0 : myError = true;
245 0 : }
246 :
247 :
248 : void
249 0 : TemplateHandler::error(const XERCES_CPP_NAMESPACE::SAXParseException& exception) {
250 0 : WRITE_ERROR(StringUtils::transcode(exception.getMessage()));
251 0 : WRITE_ERROR(" (At line/column "
252 : + toString(exception.getLineNumber() + 1) + '/'
253 : + toString(exception.getColumnNumber()) + ").");
254 0 : myError = true;
255 0 : }
256 :
257 :
258 : void
259 0 : TemplateHandler::fatalError(const XERCES_CPP_NAMESPACE::SAXParseException& exception) {
260 0 : WRITE_ERROR(StringUtils::transcode(exception.getMessage()));
261 0 : WRITE_ERROR(" (At line/column "
262 : + toString(exception.getLineNumber() + 1) + '/'
263 : + toString(exception.getColumnNumber()) + ").");
264 0 : myError = true;
265 0 : }
266 :
267 : /****************************************************************************/
|