// %pacpus:license{ // This file is part of the PACPUS framework distributed under the // CECILL-C License, Version 1.0. // %pacpus:license} /// @version $Id: ComponentBase.cpp 76 2013-01-10 17:05:10Z kurdejma $ #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; using namespace pacpus; using namespace std; vector convertAttributesToArgumentVector(const QDomNamedNodeMap & attributes); namespace std { template std::basic_ostream<_Elem, _Traits> & operator<<(std::basic_ostream<_Elem, _Traits> & os, const boost::program_options::variables_map & vm) { for (po::variables_map::const_iterator i = vm.begin(); i != vm.end(); ++i) { const po::variable_value & v = i->second; if (v.empty()) { continue; } const type_info & type = v.value().type(); if (type == typeid(string)) { const string & val = v.as(); os << i->first << "=" << val; } else if (type == typeid(long)) { int val = v.as(); os << i->first << "=" << val; } else if (type == typeid(int)) { int val = v.as(); os << i->first << "=" << val; } else if (type == typeid(unsigned long)) { int val = v.as(); os << i->first << "=" << val; } else if (type == typeid(unsigned int)) { int val = v.as(); os << i->first << "=" << val; } else if (type == typeid(double)) { int val = v.as(); os << i->first << "=" << val; } else if (type == typeid(float)) { int val = v.as(); os << i->first << "=" << val; } else if (type == typeid(bool)) { int val = v.as(); os << i->first << "=" << val; } else { // unknown value type os << i->first; } os << "\n"; } return os; } } // namespace std DECLARE_STATIC_LOGGER("pacpus.core.ComponentBase"); ComponentBase::ComponentBase(const QString & componentName) : m_componentName(componentName) , m_isActive(false) , mIsRecording(true) , m_manager(NULL) , m_ui(NULL) , m_componentState(NOT_MONITORED) , mOptionsDescription("Component parameters") { LOG_TRACE("constructor"); // Get a pointer on the instance of ComponentManager. m_manager = ComponentManager::getInstance(); LOG_INFO("component " << getName() << " was created"); addParameters() ("name", po::value(&mName)->required(), "component name") ("type", po::value(&mTypeName)->required(), "component type") ("ui", po::value(&mHasGui)->default_value(false), "whether to show GUI") ("verbose", po::value(&mVerbose)->default_value(false), "set output verbose") ("verbosity-level", po::value(&mVerbosityLevel)->default_value(0), "set verbosity level") ("recording", po::value(&mIsRecording)->default_value(false), "whether to record data") ; } ComponentBase::~ComponentBase() { LOG_TRACE("destructor"); } bool ComponentBase::isActive() const { return m_isActive; } void ComponentBase::setActive(bool isActive) { m_isActive = isActive; } bool ComponentBase::isRecording() const { return mIsRecording; } void ComponentBase::setRecording(bool isRecording) { mIsRecording = isRecording; } const XmlComponentConfig ComponentBase::xmlParameters() const { return param; } int ComponentBase::startComponent() { if (isActive()) { LOG_DEBUG("component already started, cannot (re-)start"); return false; } setActive(true); startActivity(); return true; } int ComponentBase::stopComponent() { if (!isActive()) { LOG_DEBUG("component already stopped, cannot (re-)stop"); return false; } setActive(false); stopActivity(); return true; } void ComponentBase::setState(const COMPONENT_STATE state) { m_componentState = state; } // FIXME: this should be const. ComponentBase::COMPONENT_STATE ComponentBase::getState() { COMPONENT_STATE state = m_componentState; if (ComponentBase::NOT_MONITORED != m_componentState) { m_componentState = ComponentBase::MONITOR_NOK; } return state; } ComponentBase::COMPONENT_CONFIGURATION ComponentBase::configurationState() const { return m_configurationState; } void ComponentBase::setConfigurationState(COMPONENT_CONFIGURATION state) { m_configurationState = state; } bool ComponentBase::isConfigured() const { return (m_configurationState == CONFIGURED_OK); } QString ComponentBase::getName() const { return m_componentName; } ComponentBase::InputsMap & ComponentBase::inputs() { return m_inputs; } const ComponentBase::InputsMap & ComponentBase::inputs() const { return m_inputs; } ComponentBase::OutputsMap & ComponentBase::outputs() { return m_outputs; } const ComponentBase::OutputsMap & ComponentBase::outputs() const { return m_outputs; } InputInterfaceBase * ComponentBase::getInput(QString inputName) const { if (inputs().contains(inputName)) { return inputs()[inputName]; } LOG_WARN("Component " << getName() << " does not contain input " << inputName); return NULL; } OutputInterfaceBase * ComponentBase::getOutput(QString outputName) const { if (outputs().contains(outputName)) { return outputs()[outputName]; } LOG_WARN("Component " << getName() << " does not contain output " << outputName); return NULL; } bool ComponentBase::hasGui() const { return mHasGui; } bool ComponentBase::isOutputVerbose() const { return mVerbose || (getVerbosityLevel() > 0); } int ComponentBase::getVerbosityLevel() const { return mVerbosityLevel; } po::options_description_easy_init ComponentBase::addParameters() { return mOptionsDescription.add_options(); } class DomElementParser { public: DomElementParser(QDomElement const& args); /** Sets options descriptions to use. */ DomElementParser& options(const boost::program_options::options_description& desc); /** Parses the options and returns the result of parsing. Throws on error. */ boost::program_options::basic_parsed_options run(); /** Specifies that unregistered options are allowed and should be passed though. For each command like token that looks like an option but does not contain a recognized name, an instance of basic_option will be added to result, with 'unrecognized' field set to 'true'. It's possible to collect all unrecognized options with the 'collect_unrecognized' funciton. */ DomElementParser& allow_unregistered(); private: boost::program_options::basic_parsed_options parseDomElement( const QDomElement& dom_element, const boost::program_options::options_description& desc, bool allow_unregistered = false); private: const boost::program_options::options_description* m_desc; const QDomElement& m_dom_element; bool m_allow_unregistered; }; DomElementParser::DomElementParser(const QDomElement& dom_element) : m_dom_element(dom_element) , m_allow_unregistered(false) { } DomElementParser& DomElementParser::options(const boost::program_options::options_description& desc) { m_desc = &desc; return *this; } DomElementParser& DomElementParser::allow_unregistered() { m_allow_unregistered = true; return *this; } #include #include namespace detail { template class basic_dom_element_iterator : public boost::iterator_facade< basic_dom_element_iterator , const boost::program_options::option , boost::random_access_traversal_tag > { public: typedef boost::program_options::option ValueType; typedef basic_dom_element_iterator self_type; typedef typename self_type::iterator_facade_ base_type; typedef typename self_type::difference_type difference_type; basic_dom_element_iterator(QDomElement const& dom_element) : m_dom_element(NULL) , m_at_eof(true) , m_i(dom_element.attributes().size()) { } basic_dom_element_iterator(QDomElement const& dom_element, std::set const& allowed_options, bool allow_unregistered = false) : m_dom_element(&dom_element) , m_allowed_options(allowed_options) , m_allow_unregistered(allow_unregistered) , m_i(0) { m_attrs = m_dom_element->attributes(); m_at_eof = !(m_i < m_attrs.size()); if (!m_at_eof) { get(); } } private: friend class ::boost::iterator_core_access; bool equal(const basic_dom_element_iterator& other) const { if (m_at_eof && other.m_at_eof) { return true; } return false; } void increment() { ++m_i; m_at_eof = !(m_i < m_attrs.size()); if (!m_at_eof) { get(); } } const ValueType& dereference() const { return m_value; } void advance(size_t n) { m_i += n; m_at_eof = !(m_i < m_attrs.size()); if (!m_at_eof) { get(); } } difference_type distance_to(const basic_dom_element_iterator& other) const { return other.m_i - this->m_i; } private: ValueType& value() { return m_value; } void get() { using namespace boost::program_options; QDomNode node = m_attrs.item(m_i); QDomAttr attr = node.toAttr(); string name = attr.name().toStdString(); string value = attr.value().toStdString(); bool registered = allowed_option(name); if (!registered && !m_allow_unregistered) { boost::throw_exception(unknown_option(name)); } this->value().string_key = name; this->value().value.clear(); this->value().value.push_back(value); this->value().unregistered = !registered; //this->value().original_tokens.push_back(name); //this->value().original_tokens.push_back(value); } bool allowed_option(const std::string& s) const { set::const_iterator it = m_allowed_options.find(s); if (it != m_allowed_options.end()) { return true; } return false; } private: const QDomElement* m_dom_element; std::set m_allowed_options; bool m_allow_unregistered; QDomNamedNodeMap m_attrs; int m_i; bool m_at_eof; ValueType m_value; }; typedef basic_dom_element_iterator dom_element_iterator; typedef basic_dom_element_iterator wdom_element_iterator; } boost::program_options::basic_parsed_options DomElementParser::run() { assert(m_desc); return parseDomElement(m_dom_element, *m_desc, m_allow_unregistered); } boost::program_options::basic_parsed_options DomElementParser::parseDomElement( const QDomElement& dom_element, const boost::program_options::options_description& desc, bool allow_unregistered) { // TODO: use XPath paths typedef char charT; using boost::program_options::error; using boost::shared_ptr; using namespace boost::program_options; using ::detail::basic_dom_element_iterator; set allowed_options; const vector >& options = desc.options(); for (unsigned i = 0; i < options.size(); ++i) { const option_description& d = *options[i]; if (d.long_name().empty()) { boost::throw_exception( error("abbreviated option names are not permitted when parsing DOM elements")); } allowed_options.insert(d.long_name()); } // Parser returns char strings parsed_options result(&desc); copy(basic_dom_element_iterator(dom_element, allowed_options, allow_unregistered), basic_dom_element_iterator(dom_element), back_inserter(result.options)); // Convert char strings into desired type. return basic_parsed_options(result); } DomElementParser parseDomElement(); /** Creates instance of 'command_line_parser', passes parameters to it, and returns the result of calling the 'run' method. */ boost::program_options::basic_parsed_options parseDomElement(QDomElement const& domElement, const boost::program_options::options_description&); boost::program_options::basic_parsed_options parseDomElement(QDomElement const& domElement, const boost::program_options::options_description& desc) { return DomElementParser(domElement) .options(desc) .run(); } void ComponentBase::parseParameters(XmlComponentConfig const& cfg) { LOG_INFO("Parsing parameters..."); LOG_INFO(mOptionsDescription); po::variables_map vm; try { po::store( DomElementParser(cfg.getDomElement()) .options(mOptionsDescription) .allow_unregistered() // FIXME: temporary only, at term all the components specify all parameters .run() , vm); po::notify(vm); } catch (po::error& e) { LOG_WARN(e.what()); throw PacpusException(e.what()); } LOG_INFO("Parsed parameter values:\n" << vm); } vector convertAttributesToArgumentVector(const QDomNamedNodeMap & attributes) { vector xargs; xargs.reserve(attributes.size()); for (int i = 0; i < attributes.size(); ++i) { QDomAttr parameter = attributes.item(i).toAttr(); if (parameter.isNull()) { LOG_WARN("node is not a parameter"); continue; } QString arg = QString("--") + parameter.name() + "="; bool shouldAddQuotes = parameter.value().contains(' '); if (shouldAddQuotes) { arg += '\"'; arg += parameter.value(); arg += '\"'; } else { arg += parameter.value(); } LOG_DEBUG("parameter: " << arg); xargs.push_back(arg.toStdString()); } return xargs; }