XmlReader.cpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* ArmarX is free software; you can redistribute it and/or modify
5* it under the terms of the GNU General Public License version 2 as
6* published by the Free Software Foundation.
7*
8* ArmarX is distributed in the hope that it will be useful, but
9* WITHOUT ANY WARRANTY; without even the implied warranty of
10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11* GNU General Public License for more details.
12*
13* You should have received a copy of the GNU General Public License
14* along with this program. If not, see <http://www.gnu.org/licenses/>.
15*
16* @package ArmarX::
17* @author Christian Mandery (christian.mandery at kit dot edu)
18* @date 2014
19* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20* GNU General Public License
21*/
22
23#include "XmlReader.h"
24
30
34
35using namespace armarx::exceptions::local;
36using namespace armarx::statechartio;
37using namespace armarx::statechartmodel;
38
40 iceCommunicator(iceCommunicator), info(info)
41{
42}
43
44void
45XmlReader::parseXml(const QString& xmlString)
46{
47 QByteArray utf8Xml = xmlString.toUtf8();
49
50 try
51 {
52 document.parse<rapidxml::parse_validate_closing_tags>(utf8Xml.data());
53 }
54 catch (const rapidxml::parse_error& e)
55 {
56 throw XmlReaderException(std::string("Could not parse XML file: ") + e.what());
57 }
58
59 rapidxml::xml_node<>* stateNode = XmlNodeIterator(&document, "State", true).getNext();
60 QString version = readAttribute(stateNode, "version");
61
62 if (version != "1.0" && version != "1.1" && version != "1.2")
63 {
65 "Only statechart XML definition version 1.0 - 1.2 are supported right now!");
66 }
67
68 if (readAttribute(stateNode, "type", false) ==
70 {
71 loadedState = statechartmodel::StatePtr(
72 new DynamicRemoteStateClass(readAttribute(stateNode, "uuid")));
73 ARMARX_IMPORTANT_S << "Found DynamicRemoteStateClass";
74 }
75 else
76 {
77 loadedState =
78 statechartmodel::StatePtr(new statechartmodel::State(readAttribute(stateNode, "uuid")));
79 }
80
81 loadedState->setStateName(readAttribute(stateNode, "name"));
82 loadedState->setSize(QSizeF(readAttribute(stateNode, "width").toFloat(),
83 readAttribute(stateNode, "height").toFloat()));
84 loadedState->setDescription(readDescription(stateNode));
85
86 rapidxml::xml_node<>* curNode;
88
89 XmlNodeIterator inputParameterNI(stateNode, "InputParameters", false);
90
91 // parameterList = loadedState->getInputParameters();
92 while ((curNode = inputParameterNI.getNext()))
93 {
94 parameterList.unite(readParameterList(curNode));
95 }
96
97 loadedState->setInputParameters(parameterList);
98
99 parameterList.clear();
100 // parameterList = loadedState->getLocalParameters();
101 XmlNodeIterator localParameterNI(stateNode, "LocalParameters", false);
102
103 while ((curNode = localParameterNI.getNext()))
104 {
105 parameterList.unite(readParameterList(curNode));
106 }
107
108 loadedState->setLocalParameters(parameterList);
109
110 parameterList.clear();
111 // parameterList = loadedState->getOutputParameters();
112 XmlNodeIterator outputParameterNI(stateNode, "OutputParameters", false);
113
114 while ((curNode = outputParameterNI.getNext()))
115 {
116 parameterList.unite(readParameterList(curNode));
117 }
118
119 loadedState->setOutputParameters(parameterList);
120
121 StateInstanceMap substateList;
122 XmlNodeIterator substatesNI(stateNode, "Substates", false);
123
124 while ((curNode = substatesNI.getNext()))
125 {
126 substateList.unite(readSubstateList(curNode));
127 }
128
129 loadedState->replaceSubstates(substateList);
130
131 EventList eventList;
132 XmlNodeIterator eventNI(stateNode, "Events", false);
133
134 while ((curNode = eventNI.getNext()))
135 {
136 eventList.append(readEventList(curNode));
137 }
138
139 loadedState->setOutgoingEvents(eventList);
140
141 rapidxml::xml_node<>* startStateNode =
142 XmlNodeIterator(stateNode, "StartState", false).getNext();
143
144 if (startStateNode)
145 {
146 readStartState(startStateNode);
147 }
148
149 TransitionList transitionList;
150 XmlNodeIterator transitionNI(stateNode, "Transitions", false);
151
152 while ((curNode = transitionNI.getNext()))
153 {
154 transitionList.append(readTransitionList(curNode));
155 }
156
157 loadedState->replaceTransitions(transitionList);
158}
159
160QString
161XmlReader::readDescription(rapidxml::xml_node<>* rootNode) const
162{
163 rapidxml::xml_node<>* descriptionNode =
164 XmlNodeIterator(rootNode, "Description", false).getNext();
165 return descriptionNode ? QString::fromUtf8(descriptionNode->value()) : "";
166}
167
169XmlReader::readEventList(rapidxml::xml_node<>* eventListNode) const
170{
171 EventList eventList;
172 rapidxml::xml_node<>* curNode;
173
174 XmlNodeIterator eventNI(eventListNode, "Event", false);
175
176 while ((curNode = eventNI.getNext()))
177 {
179 event->name = readAttribute(curNode, "name");
180 event->description = readDescription(curNode);
181
182 for (EventList::const_iterator i = eventList.begin(); i != eventList.end(); ++i)
183 {
184 if ((*i)->name == event->name)
185 {
186 throw XmlReaderException("Duplicate event name: " + event->name.toStdString());
187 }
188 }
189
190 eventList.append(event);
191 }
192
193 return eventList;
194}
195
197XmlReader::readParameterList(rapidxml::xml_node<>* parameterListNode) const
198{
200 rapidxml::xml_node<>* curNode;
201
202 XmlNodeIterator parameterNI(parameterListNode, "Parameter", false);
203
204 while ((curNode = parameterNI.getNext()))
205 {
206 statechartmodel::StateParameterPtr stateParameter(new statechartmodel::StateParameter());
207 stateParameter->type = readAttribute(curNode, "type");
208 //stateParameter->dataType = readAttribute(curNode, "dataType");
209 stateParameter->description = readDescription(curNode);
210
211 // Support for legacy "default" attribute
212 stateParameter->setDefaultValueJson(readAttribute(curNode, "default", false));
213
214 if (!stateParameter->getDefaultValueJson().isEmpty())
215 {
216 std::function<bool(std::string, std::string)> loadVar;
217 loadVar = [&](std::string curType, std::string prevType)
218 {
219 if (curType == prevType)
220 {
221 return false;
222 }
223 try
224 {
225
226 JSONObjectPtr jsonObject = new JSONObject(iceCommunicator);
227 jsonObject->fromString(stateParameter->getDefaultValueJson().toUtf8().data());
228
229 stateParameter->profileDefaultValues[QString::fromUtf8(
230 StatechartProfiles::GetRootName().c_str())] = {
231 VariantContainerBasePtr::dynamicCast(jsonObject->deserializeIceObject()),
232 stateParameter->getDefaultValueJson()};
233 }
234 catch (Ice::NoValueFactoryException& e)
235 {
236 ARMARX_INFO_S << "Variant missing - trying to load variant " << e.type
237 << " now";
238 info->loadLibraryOfVariant(e.type);
240 return loadVar(e.type, curType);
241 }
242 return true;
243 };
244
245 try
246 {
247 loadVar("", "-");
248 }
249 catch (exceptions::user::UnknownTypeException& e)
250 {
251 ARMARX_WARNING_S << "Could not deserialize JSONString for type "
252 << stateParameter->type;
253 }
254 catch (const armarx::JSONException& e)
255 {
256 throw XmlReaderException("Could not parse JSON: " +
257 stateParameter->getDefaultValueJson().toStdString());
258 }
259 }
260
261 // End of code snippet for legacy support
262
263 XmlNodeIterator defaultValueNI(curNode, "DefaultValue", false);
264 rapidxml::xml_node<>* defaultValueCurNode;
265
266 while ((defaultValueCurNode = defaultValueNI.getNext()))
267 {
268 QString profileName = readAttribute(defaultValueCurNode, "profile", false);
269
270 if (profileName.size() == 0)
271 {
272 profileName = QString::fromUtf8(StatechartProfiles::GetRootName().c_str());
273 }
274
275 QString valueJson = readAttribute(defaultValueCurNode, "value", true);
276 VariantContainerBasePtr valueVariant;
277 std::function<bool(std::string, std::string)> loadVar;
278 loadVar = [&](std::string curType, std::string prevType)
279 {
280 if (curType == prevType)
281 {
282 return false;
283 }
284 try
285 {
286
287 JSONObjectPtr jsonObject = new JSONObject(iceCommunicator);
288 jsonObject->fromString(valueJson.toUtf8().data());
289
290 valueVariant =
291 VariantContainerBasePtr::dynamicCast(jsonObject->deserializeIceObject());
292 }
293 catch (Ice::NoValueFactoryException& e)
294 {
295 ARMARX_INFO_S << "Variant missing - trying to load variant " << e.type
296 << " now";
297 info->loadLibraryOfVariant(e.type);
299 return loadVar(e.type, curType);
300 }
301 return true;
302 };
303
304 try
305 {
306 loadVar("", "-");
307 }
308 catch (exceptions::user::UnknownTypeException& e)
309 {
310 ARMARX_WARNING_S << "Could not deserialize JSONString for type "
311 << stateParameter->type;
312 }
313 catch (const armarx::JSONException& e)
314 {
315 throw XmlReaderException("Could not parse JSON: " + valueJson.toStdString());
316 }
317
318 stateParameter->profileDefaultValues[profileName] =
319 QPair<VariantContainerBasePtr, QString>(valueVariant, valueJson);
320 }
321
322 QString optionalStr = readAttribute(curNode, "optional");
323
324 if (optionalStr == "yes")
325 {
326 stateParameter->optional = true;
327 }
328 else if (optionalStr == "no")
329 {
330 stateParameter->optional = false;
331 }
332 else
333 {
334 throw XmlReaderException("\"optional\" attribute must have value \"yes\" or \"no\"!");
335 }
336
337 QString parameterName = readAttribute(curNode, "name");
338
339 if (stateParameterMap.contains(parameterName))
340 {
341 throw XmlReaderException("Duplicate parameter name: " + parameterName.toStdString());
342 }
343
344 stateParameterMap[parameterName] = stateParameter;
345 }
346
347 return stateParameterMap;
348}
349
351XmlReader::readParameterMappingList(rapidxml::xml_node<>* parameterMappingListNode) const
352{
353 ParameterMappingList parameterMappingList;
354 rapidxml::xml_node<>* curNode;
355
356 XmlNodeIterator parameterMappingNI(parameterMappingListNode, "ParameterMapping", false);
357
358 while ((curNode = parameterMappingNI.getNext()))
359 {
361 new statechartmodel::ParameterMapping());
362 parameterMapping->sourceKey = readAttribute(curNode, "from");
363 parameterMapping->destinationKey = readAttribute(curNode, "to");
364
365 QString mappingSourceString = readAttribute(curNode, "sourceType");
366 parameterMapping->source = PM::StringToMappingSource(mappingSourceString.toStdString());
367
368 if (parameterMapping->source == eMappingSourcesCount)
369 {
370 throw XmlReaderException("Unknown value for sourceType: \"" +
371 mappingSourceString.toStdString() + "\"");
372 }
373
374
375 XmlNodeIterator defaultValueNI(curNode, "DefaultValue", false);
376 rapidxml::xml_node<>* defaultValueCurNode;
377
378 while ((defaultValueCurNode = defaultValueNI.getNext()))
379 {
380 QString profileName = readAttribute(defaultValueCurNode, "profile", false);
381
382 if (profileName.size() == 0)
383 {
384 profileName = QString::fromUtf8(StatechartProfiles::GetRootName().c_str());
385 }
386
387 QString valueJson = readAttribute(defaultValueCurNode, "value", true);
388 parameterMapping->profileValues[profileName] = valueJson;
389 }
390
391
392 parameterMappingList.append(parameterMapping);
393 }
394
395 return parameterMappingList;
396}
397
398void
399XmlReader::readStartState(rapidxml::xml_node<>* startStateNode) const
400{
401 loadedState->setStartState(
402 getSubstateByInstanceName(readAttribute(startStateNode, "substateName")));
403
404 rapidxml::xml_node<>* parameterMappingsNode =
405 XmlNodeIterator(startStateNode, "ParameterMappings", false).getNext();
406
407 if (parameterMappingsNode)
408 {
409 loadedState->setStartStateInputMapping(readParameterMappingList(parameterMappingsNode));
410 StateCPtr state = loadedState;
411 TransitionCPtr transition = state->getStartTransition();
412 rapidxml::xml_node<>* supportPointsNode =
413 XmlNodeIterator(startStateNode, "SupportPoints", false).getNext();
414 SupportPoints points;
415 if (supportPointsNode)
416 {
417 rapidxml::xml_node<>* supportPointNode;
418 XmlNodeIterator supportPointNI(supportPointsNode, "SupportPoint", false);
419 QPointList supportPoints;
420 while ((supportPointNode = supportPointNI.getNext()))
421 {
422 QPointF supportPoint(readAttribute(supportPointNode, "posX").toFloat(),
423 readAttribute(supportPointNode, "posY").toFloat());
424
425 supportPoints.append(supportPoint);
426 }
427 points.setControlPoints(supportPoints);
428 loadedState->setTransitionSupportPoints(transition, points);
429 }
430 }
431}
432
434XmlReader::readSubstateList(rapidxml::xml_node<>* substateListNode) const
435{
436 StateInstanceMap stateInstanceMap;
437 rapidxml::xml_node<>* curNode = substateListNode->first_node();
438
439 while (curNode)
440 {
442 curNode->name(), XmlParentPair(curNode, loadedState));
443
444 if (!stateInstanceFactory)
445 {
446 throw XmlReaderException("Invalid substate type: \"" + std::string(curNode->name()) +
447 "\"");
448 }
449
450 StateInstancePtr stateInstance = stateInstanceFactory->getStateInstance();
451
452 QString stateInstanceName = stateInstance->getInstanceName();
453
454 if (stateInstanceMap.contains(stateInstanceName))
455 {
456 throw XmlReaderException("Duplicate substate name: " + stateInstanceName.toStdString());
457 }
458
459 stateInstanceMap[stateInstanceName] = stateInstance;
460
461 curNode = curNode->next_sibling();
462 }
463
464 return stateInstanceMap;
465}
466
468XmlReader::readTransitionList(rapidxml::xml_node<>* transitionListNode) const
469{
470 TransitionList transitionList;
471 rapidxml::xml_node<>* curNode;
472
473 XmlNodeIterator transitionNI(transitionListNode, "Transition", false);
474
475 while ((curNode = transitionNI.getNext()))
476 {
477 TransitionPtr transition(new Transition());
478
479 QString sourceStateName = readAttribute(curNode, "from");
480 QString eventName = readAttribute(curNode, "eventName");
481
482 if (!hasSubstateByInstanceName(sourceStateName))
483 {
484 ARMARX_WARNING_S << "Skipping broken transition in state '"
485 << loadedState->getStateName().toStdString() << "'. SourceState '"
486 << sourceStateName << "' of event '" << eventName
487 << "' could not be found in the list of substates. State UUID="
488 << loadedState->getUUID()
489 << ".\nStatechart group information or state path are not available "
490 "at this point. Sorry. Use grep.";
491 continue;
492 }
493
494 transition->sourceState = getSubstateByInstanceName(sourceStateName);
495 transition->destinationState =
496 getSubstateByInstanceName(readAttribute(curNode, "to", false));
497 transition->eventName = eventName;
498 transition->transitionUserCode =
499 readAttribute(curNode, "transitionCodeEnabled", false) == "1";
500
501 rapidxml::xml_node<>* parameterMappingsNode =
502 XmlNodeIterator(curNode, "ParameterMappings", false).getNext();
503
504 if (parameterMappingsNode)
505 {
506 transition->mappingToNextStatesInput = readParameterMappingList(parameterMappingsNode);
507 }
508
509 parameterMappingsNode =
510 XmlNodeIterator(curNode, "ParameterMappingsToParentsLocal", false).getNext();
511
512 if (parameterMappingsNode)
513 {
514 transition->mappingToParentStatesLocal =
515 readParameterMappingList(parameterMappingsNode);
516 }
517
518 parameterMappingsNode =
519 XmlNodeIterator(curNode, "ParameterMappingsToParentsOutput", false).getNext();
520
521 if (parameterMappingsNode)
522 {
523 transition->mappingToParentStatesOutput =
524 readParameterMappingList(parameterMappingsNode);
525 }
526
527 rapidxml::xml_node<>* supportPointsNode =
528 XmlNodeIterator(curNode, "SupportPoints", false).getNext();
529
530 if (supportPointsNode)
531 {
532 rapidxml::xml_node<>* supportPointNode;
533 XmlNodeIterator supportPointNI(supportPointsNode, "SupportPoint", false);
534 QPointList supportPoints;
535 while ((supportPointNode = supportPointNI.getNext()))
536 {
537 QPointF supportPoint(readAttribute(supportPointNode, "posX").toFloat(),
538 readAttribute(supportPointNode, "posY").toFloat());
539
540 supportPoints.append(supportPoint);
541 }
542 transition->supportPoints.setControlPoints(supportPoints);
543 }
544
545 transitionList.append(transition);
546 }
547
548 return transitionList;
549}
550
551bool
552XmlReader::hasSubstateByInstanceName(const QString& name) const
553{
554 if (name.isEmpty())
555 {
556 return false;
557 }
558
559 return loadedState->getSubstates().contains(name);
560}
561
563XmlReader::getSubstateByInstanceName(const QString& name) const
564{
565 if (name.isEmpty())
566 {
567 return StateInstancePtr();
568 }
569
570 StateInstanceMap substates = loadedState->getSubstates();
571
572 if (!substates.contains(name))
573 {
574 std::stringstream ss;
575 ss << "Available substates (";
576 ss << substates.size();
577 ss << "): ";
578
579 for (QString s : substates.keys())
580 {
581 ss << s.toStdString() << "; ";
582 }
583
584 throw XmlReaderException("Referenced substate \"" + name.toStdString() +
585 "\" not found in State \"" +
586 loadedState->getStateName().toStdString() + "\"! " + ss.str());
587 }
588
589 return substates[name];
590}
591
592QString
593XmlReader::readAttribute(rapidxml::xml_node<>* node,
594 const QString& attributeName,
595 bool required) const
596{
597 rapidxml::xml_attribute<>* attribute = node->first_attribute(attributeName.toUtf8());
598
599 if (!attribute)
600 {
601 if (required)
602 {
603 throw XmlReaderException("Attribute \"" + attributeName.toStdString() + "\" for \"" +
604 node->name() + "\" node not found!");
605 }
606 else
607 {
608 return "";
609 }
610 }
611
612 return QString::fromUtf8(attribute->value());
613}
614
616 const QString& nodeName,
617 bool required) :
618 parentNode(parentNode),
619 currentChildNode(0),
620 nodeName(nodeName),
621 nodeNameUtf8(nodeName.toUtf8()),
622 required(required)
623{
624}
625
628{
629 if (currentChildNode)
630 {
631 rapidxml::xml_node<>* nextChildNode = currentChildNode->next_sibling(nodeNameUtf8.data());
632
633 if (!nextChildNode)
634 {
635 return 0;
636 }
637
638 currentChildNode = nextChildNode;
639 }
640 else
641 {
642 currentChildNode = parentNode->first_node(nodeNameUtf8.data());
643
644 if (required && !currentChildNode)
645 {
646 throw XmlReaderException("Required node \"" + nodeName.toStdString() + "\" not found!");
647 }
648 }
649
650 return currentChildNode;
651}
QString readAttribute(rapidxml::xml_node<> *node, const QString &attributeName, bool required=true)
static SharedPointerType fromName(const std::string &name, XmlParentPair params)
static void RegisterKnownObjectFactoriesWithIce(const Ice::CommunicatorPtr &ic)
Registers all object factories that are known with Ice.
static MappingSource StringToMappingSource(const std::string &mappingSourceString)
static std::string GetRootName()
Iterate over all child nodes with a certain name of a given XML node.
Definition XmlReader.h:95
XmlNodeIterator(rapidxml::xml_node<> *parentNode, const QString &nodeName, bool required)
void parseXml(const QString &xmlString)
Parses the given XML document and builds a State object (that can be retrieved using getRootState()).
Definition XmlReader.cpp:45
XmlReader(Ice::CommunicatorPtr iceCommunicator, VariantInfoPtr info)
Creates a new XmlReader.
Definition XmlReader.cpp:39
static QString StateTypeToString(eStateType type)
Definition State.cpp:1294
Parse error exception.
Definition rapidxml.hpp:77
const char * what() const noexcept override
Gets human readable description of error.
Definition rapidxml.hpp:88
Ch * value() const
Gets value of node.
Definition rapidxml.hpp:795
Ch * name() const
Gets name of the node.
Definition rapidxml.hpp:774
This class represents root of the DOM hierarchy.
void parse(Ch *text)
Parses zero-terminated XML string according to given flags.
Class representing a node of XML document.
xml_node< Ch > * next_sibling(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets next sibling node, optionally matching node name.
xml_node< Ch > * first_node(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets first child node, optionally matching node name.
xml_attribute< Ch > * first_attribute(const Ch *name=nullptr, std::size_t name_size=0, bool case_sensitive=true) const
Gets first attribute of node, optionally matching attribute name.
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_IMPORTANT_S
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:210
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
::IceInternal::Handle<::Ice::Communicator > CommunicatorPtr
Definition IceManager.h:49
std::string toUtf8(QString const &qstring)
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
std::pair< rapidxml::xml_node<> *, armarx::statechartmodel::StatePtr > XmlParentPair
std::shared_ptr< StateInstanceFactoryBase > StateInstanceFactoryBasePtr
std::shared_ptr< State > StatePtr
Definition State.h:48
QMap< QString, StateParameterPtr > StateParameterMap
std::shared_ptr< StateInstance > StateInstancePtr
std::shared_ptr< StateParameter > StateParameterPtr
QList< ParameterMappingPtr > ParameterMappingList
Definition XmlWriter.h:49
QList< TransitionPtr > TransitionList
Definition State.h:50
std::shared_ptr< const State > StateCPtr
Definition XmlWriter.h:45
std::shared_ptr< Event > EventPtr
Definition XmlWriter.h:46
std::shared_ptr< const Transition > TransitionCPtr
Definition Transition.h:91
QList< EventPtr > EventList
Definition XmlWriter.h:47
std::shared_ptr< ParameterMapping > ParameterMappingPtr
Definition XmlWriter.h:48
QMap< QString, StateInstancePtr > StateInstanceMap
Definition State.h:52
std::shared_ptr< Transition > TransitionPtr
Definition Transition.h:143
std::shared_ptr< VariantInfo > VariantInfoPtr
Definition VariantInfo.h:39
IceInternal::Handle< JSONObject > JSONObjectPtr
Definition JSONObject.h:34
float toFloat(const std::string &input)
Converts a string to float and uses always dot as seperator.
QList< QPointF > QPointList
Definition Transition.h:38
const int parse_validate_closing_tags
Parse flag instructing the parser to validate closing tag names.
Definition rapidxml.hpp:235
void setControlPoints(QList< QPointF > list)