/** * @file * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) * @copyright Simplified BSD * * @cond * This program is free software: you can redistribute it and/or modify * it under the terms of the FreeBSD license as published by the FreeBSD * project. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the FreeBSD license along with this * program. If not, see . * @endcond */ #include "uscxml/Common.h" #include "uscxml/UUID.h" #include "SCXMLDotWriter.h" #include "../transform/FlatStateIdentifier.h" #include "uscxml/dom/DOMUtils.h" #include // replace_all #include namespace uscxml { using namespace Arabica::DOM; using namespace Arabica::XPath; SCXMLDotWriter::SCXMLDotWriter() { _iteration = 0; _indentation = 0; } SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, const std::list& stateAnchors, const Element& transition) { _interpreter = interpreter; _xmlNSPrefix = _interpreter.getNameSpaceInfo().xmlNSPrefix; _transition = transition; _anchors = stateAnchors; NodeList scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); _scxml = (Element)scxmlElems.item(0); _isFlat = HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat")); if (_anchors.size() == 0) { StateAnchor anchor; anchor.element = _scxml; _anchors.push_back(anchor); } for (std::list::iterator anchIter = _anchors.begin(); anchIter != _anchors.end(); anchIter++) { if (!anchIter->element) anchIter->element = _scxml; if (anchIter->childDepth >= 0 && anchIter->transDepth == -1) anchIter->transDepth = anchIter->childDepth; _portType = anchIter->type; assembleGraph(anchIter->element, anchIter->childDepth, anchIter->transDepth + 1); } _iteration = 0; _indentation = 0; } SCXMLDotWriter::~SCXMLDotWriter() { } void SCXMLDotWriter::onStableConfiguration(Interpreter interpreter) { std::ostringstream fileSS; fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; toDot(fileSS.str(), interpreter); } void SCXMLDotWriter::afterCompletion(Interpreter interpreter) { std::ostringstream fileSS; fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; toDot(fileSS.str(), interpreter); } void SCXMLDotWriter::beforeMicroStep(Interpreter interpreter) { // std::ostringstream fileSS; // fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; // toDot(fileSS.str(), interpreter); } void SCXMLDotWriter::beforeTakingTransition(Interpreter interpreter, const Element& transition, bool moreComing) { std::ostringstream fileSS; fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; toDot(fileSS.str(), interpreter, transition); } std::string SCXMLDotWriter::getPrefix() { std::string prefix = ""; for (size_t i = 0; i < _indentation; i++) prefix += " "; return prefix; } void SCXMLDotWriter::writeTo(std::ostream& os) { os << "digraph {" << std::endl; os << " rankdir=LR;" << std::endl; os << " fontsize=10;" << std::endl; // outfile << " splines=ortho;" << std::endl; // outfile << " splines=false;" << std::endl; // outfile << " nodesep=1.0;" << std::endl; _indentation++; writeStateElement(os, _scxml); // write edges at end of file for(std::set::iterator edgeIter = _edges.begin(); edgeIter != _edges.end(); edgeIter++) { if (edgeIter->from == edgeIter->to) continue; if (_histories.find(edgeIter->to) != _histories.end()) { if (_histories.find(edgeIter->to)->second.from == _histories.find(edgeIter->to)->second.to) continue; } os << getPrefix() << "\"" << portEscape(edgeIter->from) << "\""; if (edgeIter->fromPort.size() > 0) { os << std::string(":\"") + portEscape(edgeIter->fromPort) + "\":e"; } else { os << ":__name"; } os << " -> "; if (_histories.find(edgeIter->to) != _histories.end()) { os << getPrefix() << "\"" << portEscape(_histories.find(edgeIter->to)->second.to) << "\""; if (_histories.find(edgeIter->to)->second.toPort.size() > 0) { os << std::string(":\"") + portEscape(_histories.find(edgeIter->to)->second.toPort) + "\""; } else { os << ":__name"; } } else { os << getPrefix() << "\"" << portEscape(edgeIter->to) << "\""; if (edgeIter->toPort.size() > 0) { os << std::string(":\"") + portEscape(edgeIter->toPort) + "\""; } else { os << ":__name"; } } if (edgeIter->type == EDGE_INITAL) { os << ":nw [style=\"dashed\", color=\"black\"]"; } else { os << " [color=\"black\"]"; } os << std::endl; } _indentation--; os << "}" << std::endl; } void SCXMLDotWriter::toDot(const std::string& filename, Interpreter interpreter, const std::list& stateAnchors, const Element& transition) { std::ofstream outfile(filename.c_str()); SCXMLDotWriter writer(interpreter, stateAnchors, transition); writer.writeTo(outfile); } /** * Walk the subset of the graph that is reachable and remember the nodes */ void SCXMLDotWriter::assembleGraph(const Element& state, int32_t childDepth, int32_t transDepth) { std::string nodeId = idForNode(state); // this node is neither included per child, nor per transition if (childDepth <= 0 && transDepth <= 0) { return; } // been here if (_graph.find(nodeId) != _graph.end()) return; _graph[nodeId].node = state; _graph[nodeId].portType = _portType; if (childDepth == 0 && transDepth == 0) { _graph[nodeId].isBorder = true; } NodeSet childElems = DOMUtils::filterChildType(Node_base::ELEMENT_NODE, state); for (size_t i = 0; i < childElems.size(); i++) { Element childElem(childElems[i]); // remember histories we passed if (iequals(TAGNAME(childElem), "history")) { _histories[ATTR(childElem, "id")].to = ATTR(state, "id"); _histories[ATTR(childElem, "id")].toPort = ATTR(childElem, "id"); } // follow transitions if (iequals(TAGNAME(childElem), "transition")) { _graph[nodeId].transitions.push_back(childElem); NodeSet targetStates = _interpreter.getImpl()->getTargetStates(childElem); for (size_t j = 0; j < targetStates.size(); j++) { std::string remoteNodeId = idForNode(targetStates[j]); _graph[nodeId].targets.insert(std::make_pair(remoteNodeId, childElem)); // recurse along the transition targets, no weight from child depth assembleGraph((Element)targetStates[j], 0, transDepth - 1); } if (targetStates.size() == 0) _graph[nodeId].targets.insert(std::make_pair(nodeId, childElem)); std::list eventNames; if (HAS_ATTR(childElem, "event")) eventNames = tokenize(ATTR(childElem, "event")); if (eventNames.size() == 0) _graph[nodeId].events.insert(std::make_pair("", childElem)); for (std::list::iterator evIter = eventNames.begin(); evIter != eventNames.end(); evIter++) { _graph[nodeId].events.insert(std::make_pair(*evIter, childElem)); } } // follow childs if (InterpreterImpl::isState(Element(childElem))) { if (_interpreter.getImpl()->isInitial(Element(childElem))) { // add to initial states if it is initial _graph[nodeId].initialchilds.insert(idForNode(childElem)); } else if (_interpreter.getImpl()->isParallel(Element(state))) { // add to initial states if we are parallel _graph[nodeId].initialchilds.insert(idForNode(childElem)); } // in any case, it is a child state _graph[nodeId].childs.insert(idForNode(childElem)); // recurse (do we really need to?) assembleGraph(childElem, childDepth - 1, transDepth); } } } /** * Walk the complete graph and draw reachable subset */ void SCXMLDotWriter::writeStateElement(std::ostream& os, const Element& stateElem) { std::string stateId = idForNode(stateElem); if (_knownIds.find(stateId) != _knownIds.end()) return; _knownIds.insert(stateId); std::list > invisParents; bool isVisible = (_graph.find(stateId) != _graph.end()); bool subgraph = InterpreterImpl::isCompound(stateElem) || InterpreterImpl::isParallel(stateElem); bool fatherIsParallel = (stateElem.getParentNode() && stateElem.getParentNode().getNodeType() == Node_base::ELEMENT_NODE && InterpreterImpl::isParallel(Element(stateElem.getParentNode()))); if (subgraph) { _indentation++; os << std::endl; os << getPrefix() << "subgraph \"cluster_" << portEscape(stateId) << "\" {" << std::endl; _indentation++; os << getPrefix() << "fontsize=14" << std::endl; os << getPrefix() << "label=<" << nameForNode(stateElem) << ">" << std::endl; // os << getPrefix() << "rank=\"same\"" << std::endl; os << getPrefix() << "labeljust=l" << std::endl; os << getPrefix() << (fatherIsParallel ? "style=dashed" : "style=solid") << std::endl; // os << getPrefix() << "ranksep=\"equally\"" << std::endl; } if (isVisible) { // this state is visible! const DotState& dotState = _graph.find(stateId)->second; // is this a subgraph? os << std::endl; os << getPrefix() << "\"" << portEscape(stateId) << "\" [" << std::endl; _indentation++; os << getPrefix() << "fontsize=10," << std::endl; os << getPrefix() << "shape=plaintext," << std::endl; os << getPrefix() << (fatherIsParallel ? "style=dashed," : "style=solid,") << std::endl; // is the current state in the basic configuration? if (InterpreterImpl::isMember(stateElem, _interpreter.getBasicConfiguration())) os << getPrefix() << "color=red, penwidth=3," << std::endl; // is the current state in the basic configuration? if (dotState.isBorder) os << getPrefix() << "color=blue," << std::endl; // is this state final? if (_interpreter.getImpl()->isFinal(stateElem)) { os << getPrefix() << "shape=doublecircle," << std::endl; os << getPrefix() << "color=black," << std::endl; os << getPrefix() << "penwidth=2," << std::endl; os << getPrefix() << "label=<
" << nameForNode(stateElem) << "
>" << std::endl; _indentation--; os << getPrefix() << "];" << std::endl; return; } // is the state initial? bool isInitial = _interpreter.getImpl()->isInitial(stateElem); // if (isInitial) // os << getPrefix() << "style=filled, fillcolor=lightgrey, " << std::endl; DotState::mmap_s_e_t::const_iterator destIterF, destIterB; //std::list outPorts; // count unique keys int nrOutPorts = 0; switch (dotState.portType) { case PORT_TARGET: // outports are per target for(DotState::mmap_s_e_t::const_iterator it = dotState.targets.begin(), end = dotState.targets.end(); it != end; it = dotState.targets.upper_bound(it->first)) { nrOutPorts++; } break; case PORT_EVENT: // outports are per event for(DotState::mmap_s_e_t::const_iterator it = dotState.events.begin(), end = dotState.events.end(); it != end; it = dotState.events.upper_bound(it->first)) { nrOutPorts++; } break; case PORT_TRANSITION: nrOutPorts = dotState.transitions.size(); // for (size_t i = 0; i < dotState.transitions.size(); i++) { // outPorts.push_back(idForNode(dotState.transitions[i])); // } break; } os << getPrefix() << "label = < " << std::endl; /*
step
foo.error.port
bar
baz
*/ std::string details = getDetailedLabel(stateElem); std::string stateLabel = nameForNode(stateElem); int stateLines = 0; std::string::size_type start = 0; while ((start = stateLabel.find("" << std::endl; os << " " << stateLabel << "" << std::endl; switch (dotState.portType) { case PORT_TARGET: // outports are per target writePerTargetPorts(os, dotState, stateLines); break; case PORT_EVENT: // outports are per event writePerEventPorts(os, dotState, stateLines); break; case PORT_TRANSITION: writePerTransitionPorts(os, dotState, stateLines); break; } // write details of the state if (details.size() > 0) { os << " " << std::endl; os << details << std::endl; os << " " << std::endl; } // write history states NodeSet histories = DOMUtils::filterChildElements(_xmlNSPrefix + "history", stateElem); for (size_t i = 0; i < histories.size(); i++) { os << " history: " << ATTR_CAST(histories[i], "id") << "" << std::endl; } os << "" << std::endl << getPrefix() << ">" << std::endl; _indentation--; os << getPrefix() << "];" << std::endl; for (std::set::iterator initIter = dotState.initialchilds.begin(); initIter != dotState.initialchilds.end(); initIter++) { std::string destId = *initIter; DotEdge edge(stateId, destId); edge.type = EDGE_INITAL; if (_graph.find(destId) != _graph.end()) _edges.insert(edge); } } // recurse into children and search others to draw NodeSet childElems = DOMUtils::filterChildType(Node_base::ELEMENT_NODE, stateElem); for (size_t i = 0; i < childElems.size(); i++) { Element childElem(childElems[i]); if (InterpreterImpl::isState(Element(childElem))) { writeStateElement(os, childElem); } } if (subgraph) { _indentation--; os << getPrefix() << "} #" << stateId << std::endl; _indentation--; } } void SCXMLDotWriter::writePerTransitionPorts(std::ostream& os, const DotState& dotState, int stateLines) { // TODO: Not implemented } void SCXMLDotWriter::writePerEventPorts(std::ostream& os, const DotState& dotState, int stateLines) { // std::multimap > events; // key is event name, value is transitions that react std::string stateId = idForNode(dotState.node); DotState::mmap_s_e_t::const_iterator destIterF, destIterB; for(DotState::mmap_s_e_t::const_iterator it = dotState.events.begin(), end = dotState.events.end(); it != end; it = dotState.events.upper_bound(it->first)) { os << " first) << "\" align=\"right\">" << it->first << "" << std::endl; } } std::string SCXMLDotWriter::htmlLabelForId(const std::string& stateId, int minRows) { FlatStateIdentifier flatId(stateId); std::list::const_iterator listIter; std::stringstream labelSS; std::string seperator; labelSS << "active: "; labelSS << "{"; for (listIter = flatId.getActive().begin(); listIter != flatId.getActive().end(); listIter++) { labelSS << seperator << *listIter; seperator = ", "; } labelSS << "}"; if (flatId.getVisited().size() > 0) { minRows--; labelSS << "
init: "; labelSS << "{"; seperator = ""; for (listIter = flatId.getVisited().begin(); listIter != flatId.getVisited().end(); listIter++) { labelSS << seperator << *listIter; seperator = ", "; } labelSS << "}"; } if (flatId.getHistory().size() > 0) { minRows--; seperator = ""; std::string histSeperator = "
"; labelSS << "
history: "; std::map >::const_iterator histIter; for (histIter = flatId.getHistory().begin(); histIter != flatId.getHistory().end(); histIter++) { labelSS << histSeperator << histIter->first << ": {"; for (listIter = histIter->second.begin(); listIter != histIter->second.end(); listIter++) { labelSS << seperator << *listIter; seperator = ", "; } labelSS << "}"; seperator = ""; } } return labelSS.str(); } void SCXMLDotWriter::writePerTargetPorts(std::ostream& os, const DotState& dotState, int stateLines) { // std::multimap > targets; // key is remote node, transition is element int nrOutports = 0; std::string stateId = idForNode(dotState.node); typedef DotState::mmap_s_e_t iter_t; // we need to count outports first for vertical padding for(iter_t::const_iterator targetIter = dotState.targets.begin(), end = dotState.targets.end(); targetIter != end; targetIter = dotState.targets.upper_bound(targetIter->first)) { nrOutports++; } for(iter_t::const_iterator targetIter = dotState.targets.begin(), end = dotState.targets.end(); targetIter != end; targetIter = dotState.targets.upper_bound(targetIter->first)) { // gather all events that activate the transition std::string targetId = targetIter->first; std::set eventNames; DotEdge edge(stateId, targetId); edge.fromPort = targetId; std::pair targetKeyRange = dotState.targets.equal_range(targetId); for (iter_t::const_iterator transIter = targetKeyRange.first; transIter != targetKeyRange.second; ++transIter) { const Element& transElem = transIter->second; std::list events = tokenize(ATTR(transElem, "event")); eventNames.insert(events.begin(), events.end()); if (events.size() == 0) { // spontaneous transition eventNames.insert("#"); edge.type = EDGE_SPONTANEOUS; } } if (_graph.find(targetId) != _graph.end()) _edges.insert(edge); std::stringstream outPortSS; outPortSS << (_isFlat ? htmlLabelForId(targetId) : "" + targetId + "" ); if (_isFlat) { outPortSS << "
events: {"; } else { outPortSS << "
{"; } std::string seperator; for (std::set::const_iterator eventIter = eventNames.begin(); eventIter != eventNames.end(); eventIter++) { outPortSS << seperator << *eventIter << std::endl; seperator = ", "; } outPortSS << "}"; if (nrOutports == 1) { int missing = stateLines - nrOutports; while (_isFlat && missing-- >= 1) { outPortSS << "
"; } } os << " " << outPortSS.str() << "" << std::endl; } } std::string SCXMLDotWriter::getDetailedLabel(const Element& elem, int indentation) { /*
onEntry
Details Nested Content
*/ std::list content; NodeList childElems = elem.getChildNodes(); for (size_t i = 0; i < childElems.getLength(); i++) { if (childElems.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; Element elem = Element(childElems.item(i)); if (InterpreterImpl::isState(elem) || iequals(TAGNAME(elem), "transition") || iequals(TAGNAME(elem), "initial") || false) continue; struct ElemDetails details; details.name = "" + TAGNAME(elem) + ":"; if (iequals(TAGNAME(elem), "history")) { continue; } // provide details for special elements here // param --------- if (iequals(TAGNAME(elem), "param")) { if (HAS_ATTR(elem, "name")) details.name += " " + ATTR(elem, "name") + " = "; if (HAS_ATTR(elem, "expr")) details.name += ATTR(elem, "expr"); if (HAS_ATTR(elem, "location")) details.name += ATTR(elem, "location"); } // data --------- if (iequals(TAGNAME(elem), "data")) { if (HAS_ATTR(elem, "id")) details.name += " " + ATTR(elem, "id"); if (HAS_ATTR(elem, "src")) details.name += ATTR(elem, "src"); if (HAS_ATTR(elem, "expr")) details.name += " = " + ATTR(elem, "expr"); NodeList grandChildElems = elem.getChildNodes(); for (size_t j = 0; j < grandChildElems.getLength(); j++) { if (grandChildElems.item(j).getNodeType() == Node_base::TEXT_NODE) { details.name += dotEscape(grandChildElems.item(j).getNodeValue()); } } } // invoke --------- if (iequals(TAGNAME(elem), "invoke")) { if (HAS_ATTR(elem, "type")) details.name += "
type = " + ATTR(elem, "type"); if (HAS_ATTR(elem, "typeexpr")) details.name += "
type = " + ATTR(elem, "typeexpr"); if (HAS_ATTR(elem, "src")) details.name += "
src = " + ATTR(elem, "src"); if (HAS_ATTR(elem, "srcexpr")) details.name += "
src = " + ATTR(elem, "srcexpr"); if (HAS_ATTR(elem, "id")) details.name += "
id = " + ATTR(elem, "id"); if (HAS_ATTR(elem, "idlocation")) details.name += "
id = " + ATTR(elem, "idlocation"); } // send --------- if (iequals(TAGNAME(elem), "raise")) { if (HAS_ATTR(elem, "event ")) details.name += "
event = " + ATTR(elem, "event"); } // send --------- if (iequals(TAGNAME(elem), "send")) { if (HAS_ATTR(elem, "id")) details.name += "
id = " + ATTR(elem, "id"); if (HAS_ATTR(elem, "type")) details.name += "
type = " + ATTR(elem, "type"); if (HAS_ATTR(elem, "typeexpr")) details.name += "
type = " + ATTR(elem, "typeexpr"); if (HAS_ATTR(elem, "event")) details.name += "
event = " + ATTR(elem, "event"); if (HAS_ATTR(elem, "eventexpr")) details.name += "
event = " + ATTR(elem, "eventexpr"); if (HAS_ATTR(elem, "target")) details.name += "
target = " + ATTR(elem, "target"); if (HAS_ATTR(elem, "targetexpr")) details.name += "
target = " + ATTR(elem, "targetexpr"); if (HAS_ATTR(elem, "delay")) details.name += "
delay = " + ATTR(elem, "delay"); if (HAS_ATTR(elem, "delayexpr")) details.name += "
delay = " + ATTR(elem, "delayexpr"); } // cancel --------- if (iequals(TAGNAME(elem), "cancel")) { if (HAS_ATTR(elem, "sendid")) details.name += " " + ATTR(elem, "sendid"); } // script --------- if (iequals(TAGNAME(elem), "script")) { details.name += " "; if (HAS_ATTR(elem, "src")) details.name += ATTR(elem, "src"); NodeList grandChildElems = childElems.item(i).getChildNodes(); for (size_t j = 0; j < grandChildElems.getLength(); j++) { if (grandChildElems.item(j).getNodeType() == Node_base::TEXT_NODE) { details.name += dotEscape(grandChildElems.item(j).getNodeValue()); } } } // if --------- if (iequals(TAGNAME(elem), "if")) { if (HAS_ATTR(elem, "cond")) details.name += " cond = " + dotEscape(ATTR(elem, "cond")); } // elseif --------- if (iequals(TAGNAME(elem), "elseif")) { if (HAS_ATTR(elem, "cond")) details.name += " cond = " + dotEscape(ATTR(elem, "cond")); } // log --------- if (iequals(TAGNAME(elem), "log")) { details.name += " "; if (HAS_ATTR(elem, "label")) details.name += ATTR(elem, "label") + " = "; if (HAS_ATTR(elem, "expr")) details.name += ATTR(elem, "expr"); } // foreach --------- if (iequals(TAGNAME(elem), "foreach")) { if (HAS_ATTR(elem, "item")) details.name += "
  item = " + ATTR(elem, "item"); if (HAS_ATTR(elem, "array")) details.name += "
  array = " + ATTR(elem, "array"); if (HAS_ATTR(elem, "index")) details.name += "
  index = " + ATTR(elem, "index"); } // recurse details.content = getDetailedLabel((Element)childElems.item(i), indentation + 1); content.push_back(details); } std::stringstream ssContent; if (content.size() > 0) { ssContent << ""; std::list::iterator contentIter = content.begin(); while(contentIter != content.end()) { ssContent << ""; // ssContent << ""; ssContent << ""; ssContent << ""; if (contentIter->content.size() > 0) { ssContent << ""; // ssContent << ""; ssContent << ""; ssContent << ""; } contentIter++; } ssContent << "
" << contentIter->name << "" << contentIter->name << "
" << contentIter->details << "" << contentIter->content << "
"; } return ssContent.str(); } std::string SCXMLDotWriter::portEscape(const std::string& text) { std::string escaped(text); boost::replace_all(escaped, ".", "-"); boost::replace_all(escaped, "{", "-"); boost::replace_all(escaped, "}", "-"); boost::replace_all(escaped, ":", "-"); return escaped; } std::string SCXMLDotWriter::dotEscape(const std::string& text) { std::string escaped(text); boost::replace_all(escaped, " ", " "); boost::replace_all(escaped, "\t", "   "); boost::replace_all(escaped, "<", "<"); boost::replace_all(escaped, ">", ">"); boost::replace_all(escaped, "\"", """); boost::replace_all(escaped, "\n", "
"); return escaped; } std::string SCXMLDotWriter::colorForIndent(int indent) { int color = 255 - (16 * indent); std::stringstream ss; ss << std::hex << color; ss << std::hex << color; ss << std::hex << color; return ss.str(); } std::string SCXMLDotWriter::nameForNode(const Node& node) { std::string elemName; if (node.getNodeType() == Node_base::ELEMENT_NODE) { Element elem = (Element)node; if (InterpreterImpl::isFinal(elem) && _isFlat) { // ignore visited and history with final elements FlatStateIdentifier flatId(elem.getAttribute("id")); return "" + flatId.getActive().front() + ""; } if (elem.hasAttribute("id") && _isFlat) { elemName = htmlLabelForId(elem.getAttribute("id")); if (elemName.size() > 0) return elemName; } else if (elem.getTagName() == "scxml") { if (elem.hasAttribute("name") && !UUID::isUUID(elem.getAttribute("name"))) { elemName += elem.getAttribute("name"); } else if (elem.hasAttribute("id") && !UUID::isUUID(elem.getAttribute("id"))) { elemName += elem.getAttribute("id"); } } else if (InterpreterImpl::isCompound(elem)) { elemName = "Compound: " + elem.getAttribute("id"); } else if (InterpreterImpl::isParallel(elem)) { elemName = "Parallel: " + elem.getAttribute("id"); } else if (elem.hasAttribute("id")) { elemName += elem.getAttribute("id"); } } if (elemName.size() == 0) elemName = boost::lexical_cast(node.getLocalName()); return "" + elemName + ""; } std::string SCXMLDotWriter::idForNode(const Node& node) { std::string elemId; // try to get the id as the name or id attribute if (node.getNodeType() == Node_base::ELEMENT_NODE) { Element elem = (Element)node; // if (InterpreterImpl::isFinal(elem) && _isFlat) { // // ignore visited and history with final elements // FlatStateIdentifier flatId(elem.getAttribute("id")); // // std::stringstream activeSS; // activeSS << "active-"; // for (std::list::const_iterator activeIter = flatId.getActive().begin(); activeIter != flatId.getActive().end(); activeIter++) { // activeSS << *activeIter << "-"; // } // return activeSS.str(); // } if (elem.hasAttribute("name")) { elemId = elem.getAttribute("name"); } else if (elem.hasAttribute("id")) { elemId = elem.getAttribute("id"); } } // no luck, create id from position in tree if (elemId.size() == 0) { Node tmpParent = node; Node tmpIndex; do { if (tmpParent.getNodeType() != Node_base::ELEMENT_NODE) continue; tmpIndex = tmpParent; int index = 0; while((tmpIndex = tmpIndex.getPreviousSibling())) index++; std::stringstream ssElemId; ssElemId << TAGNAME_CAST(tmpParent) << index << "."; elemId = ssElemId.str() + elemId; } while ((tmpParent = tmpParent.getParentNode())); // elemId = ssElemId.str(); } return elemId; } }