/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2013 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : ICLUtils/src/ICLUtils/ConfigFile.cpp ** ** Module : ICLUtils ** ** Authors: Christof Elbrechter ** ** ** ** ** ** GNU LESSER GENERAL PUBLIC LICENSE ** ** This file may be used under the terms of the GNU Lesser General ** ** Public License version 3.0 as published by the ** ** ** ** Free Software Foundation and appearing in the file LICENSE.LGPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the license requirements will ** ** be met: http://www.gnu.org/licenses/lgpl-3.0.txt ** ** ** ** The development of this software was supported by the ** ** Excellence Cluster EXC 277 Cognitive Interaction Technology. ** ** The Excellence Cluster EXC 277 is a grant of the Deutsche ** ** Forschungsgemeinschaft (DFG) in the context of the German ** ** Excellence Initiative. ** ** ** ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace icl{ namespace utils{ ConfigFile::Maps *ConfigFile::getMapsInstance(){ static SmartPtr typeMap = new Maps; return typeMap.get(); } // std::map ConfigFile::s_typeMap; //std::map ConfigFile::s_typeMapReverse; void XMLDocumentDelOp::delete_func(XMLDocument *h){ ICL_DELETE(h); } namespace{ using std::string; struct StaticConfigFileTypeRegistering{ StaticConfigFileTypeRegistering(){ REGISTER_CONFIG_FILE_TYPE(char); REGISTER_CONFIG_FILE_TYPE(unsigned char); REGISTER_CONFIG_FILE_TYPE(short); REGISTER_CONFIG_FILE_TYPE(unsigned short); REGISTER_CONFIG_FILE_TYPE(int); REGISTER_CONFIG_FILE_TYPE(unsigned int); REGISTER_CONFIG_FILE_TYPE(float); REGISTER_CONFIG_FILE_TYPE(double); REGISTER_CONFIG_FILE_TYPE(string); REGISTER_CONFIG_FILE_TYPE(Any); REGISTER_CONFIG_FILE_TYPE(long int); REGISTER_CONFIG_FILE_TYPE(bool); REGISTER_CONFIG_FILE_TYPE(Size); REGISTER_CONFIG_FILE_TYPE(Point); REGISTER_CONFIG_FILE_TYPE(Rect); REGISTER_CONFIG_FILE_TYPE(Size32f); REGISTER_CONFIG_FILE_TYPE(Point32f); REGISTER_CONFIG_FILE_TYPE(Rect32f); REGISTER_CONFIG_FILE_TYPE(Range32s); REGISTER_CONFIG_FILE_TYPE(Range32f); REGISTER_CONFIG_FILE_TYPE(SteppingRange32s); REGISTER_CONFIG_FILE_TYPE(SteppingRange32f); // REGISTER_CONFIG_FILE_TYPE(Color); // todo: this needs to be done in static initialization of the ICLMath library // we need to use a real singelton typemap then /* //typedef FixedMatrix Mat3x3; //typedef FixedMatrix Mat3x4; //typedef FixedMatrix Mat4x3; typedef FixedMatrix Mat4x4; REGISTER_CONFIG_FILE_TYPE(Mat3x3); REGISTER_CONFIG_FILE_TYPE(Mat3x4); REGISTER_CONFIG_FILE_TYPE(Mat4x3); REGISTER_CONFIG_FILE_TYPE(Mat4x4); typedef FixedColVector ColVec3; typedef FixedColVector ColVec4; typedef FixedRowVector RowVec3; typedef FixedRowVector RowVec4; REGISTER_CONFIG_FILE_TYPE(ColVec3); REGISTER_CONFIG_FILE_TYPE(ColVec4); REGISTER_CONFIG_FILE_TYPE(RowVec3); REGISTER_CONFIG_FILE_TYPE(RowVec4); */ } } StaticConfigFileTypeRegisteringIntance; } std::string ConfigFile::KeyRestriction::toString() const{ if(hasRange){ return str(Range32f(min,max)); }else if (hasValues){ return "["+values+"]"; }else{ return "invalid key restriction"; } } static inline bool is_text_node(const XMLNode &node){ return node && node.type() == pugi::node_pcdata; } void ConfigFile::add_to_doc(XMLDocument &doc, const std::string &name, const std::string &type, const std::string &value, const ConfigFile::KeyRestriction *restr){ std::vector t = tok(name,"."); ICLASSERT_RETURN(t.size()>1); ICLASSERT_RETURN(t[0]=="config"); XMLNode n = doc.document_element(); for(unsigned int i=1;ihasRange){ if(valuesAtt){ data.remove_attribute(valuesAtt); } if(!rangeAtt) rangeAtt = data.append_attribute("range"); rangeAtt.set_value(restr->toString().c_str()); }else if(restr->hasValues){ if(rangeAtt){ data.remove_attribute(rangeAtt); } if(!valuesAtt) valuesAtt = data.append_attribute("values"); valuesAtt.set_value(restr->toString().c_str()); }else{ WARNING_LOG("NULL KeyRestriction detected (this is ignored)"); } } } static std::string get_id_path(XMLNode n){ std::list l; while(n.parent()){ l.push_front(n.attribute("id").value()); n = n.parent(); } std::ostringstream str; str << "config"; std::copy(l.begin(),--l.end(),std::ostream_iterator(str,".")); str << l.back(); return str.str(); } void ConfigFile::load_internal(){ //m_doc->removeAllComments(); m_entries.clear(); static const XPathQuery query("//data[@id and @type]"); XPathNodeSet ds = m_doc->document_element().select_nodes(query); std::string pfx = m_sDefaultPrefix; if(pfx.length() && pfx[pfx.length()-1] != '.') pfx+='.'; for(XPathNodeSet::const_iterator it = ds.begin(); it != ds.end(); ++it){ XMLNode n = it->node(); if(!n) throw ICLException("XML node expected, but attribute found??"); const std::string key = pfx+get_id_path(n); // DEBUG_LOG("adding key " << key); if(contains(key)) throw InvalidFileFormatException("Key: '" + key + "' was found at least twice!"); Entry &e = m_entries[key]; e.parent = this; e.id = key; e.value = n.first_child().value(); Maps &maps = getMapsInstanceRef(); std::map::const_iterator jt = maps.typeMapReverse.find(n.attribute("type").value()); if(jt == maps.typeMapReverse.end()) throw UnregisteredTypeException(n.attribute("type").value()); e.rttiType = jt->second; XMLAttribute rangeAtt = n.attribute("range"); if(rangeAtt){ std::string mm = rangeAtt.value(); if(mm.size()<5 || mm[0]!='[' || mm[mm.length()-1] != ']'){ throw InvalidFileFormatException("unable to parse range attribute: syntax [min,max]"); } std::vector mmv = parseVecStr(mm.substr(1,mm.size()-2),","); if(mmv.size() != 2){ throw InvalidFileFormatException("unable to parse range attribute: syntax [min,max]"); } setRestriction(key,KeyRestriction(mmv[0],mmv[1])); continue; } XMLAttribute valuesAtt = n.attribute("values"); if(valuesAtt){ std::string vl = valuesAtt.value(); setRestriction(key,vl.substr(1,vl.size()-2)); } } } void ConfigFile::load(const std::string &filename) throw(FileNotFoundException,InvalidFileFormatException,UnregisteredTypeException){ // {{{ open m_doc = SmartPtrBase(new XMLDocument); pugi::xml_parse_result res = m_doc->load_file(filename.c_str()); if(res.status != pugi::status_ok){ if(res.status == pugi::status_file_not_found){ throw FileNotFoundException(filename); }else{ throw InvalidFileFormatException(res.description()); } } load_internal(); } // }}} ConfigFile::ConfigFile(){ // {{{ open m_doc = SmartPtrBase(new XMLDocument); m_doc->load("\n" "\n" "\n"); } // }}} ConfigFile::ConfigFile(std::istream &stream) throw(FileNotFoundException,InvalidFileFormatException,UnregisteredTypeException){ XMLDocument *doc = new XMLDocument; doc->loadNext(stream); *this = ConfigFile(doc); } void ConfigFile::setRestriction(const std::string &id, const ConfigFile::KeyRestriction &r) throw (EntryNotFoundException){ // {{{ open get_entry_internal(m_sDefaultPrefix+id).restr = SmartPtr(new KeyRestriction(r)); // Search the corresponding node ... } // }}} const ConfigFile::KeyRestriction *ConfigFile::getRestriction(const std::string &id) const throw (EntryNotFoundException){ // {{{ open return get_entry_internal(m_sDefaultPrefix+id).restr.get(); } // }}} ConfigFile::ConfigFile(const std::string &filename)throw(FileNotFoundException,InvalidFileFormatException,UnregisteredTypeException){ // {{{ open load(filename); } // }}} ConfigFile::ConfigFile(XMLDocument *handle) throw (UnregisteredTypeException): m_doc(handle){ load_internal(); } void ConfigFile::loadConfig(const std::string &filename){ // {{{ open s_oConfig.load(filename); } // }}} void ConfigFile::loadConfig(const ConfigFile &configFile){ // {{{ open s_oConfig = configFile; } // }}} void ConfigFile::save(const std::string &filename) const{ // {{{ open if (!m_doc->save_file(filename.c_str())) { throw ICLException("failed to open file"); } } // }}} void ConfigFile::clear(){ *this = ConfigFile(); } bool ConfigFile::check_type_internal(const std::string &id, const std::string &rttiTypeID) const throw (EntryNotFoundException,UnregisteredTypeException){ const Entry &e = get_entry_internal(m_sDefaultPrefix+id); return e.rttiType == rttiTypeID; /* DEBUG_LOG("rtti type is " << rttiTypeID << "#registered types:" << s_typeMap.size()); std::map::const_iterator it=s_typeMap.find(rttiTypeID); if(it == s_typeMap.end()) throw UnregisteredTypeException(rttiTypeID); return (it->second == e.rttiType); */ } ConfigFile::Entry &ConfigFile::get_entry_internal(const std::string &id) throw (EntryNotFoundException){ std::map::iterator it = m_entries.find(id); if(it == m_entries.end()) throw EntryNotFoundException(id); else return it->second; } const ConfigFile::Entry &ConfigFile::get_entry_internal(const std::string &id) const throw (EntryNotFoundException){ return const_cast(this)->get_entry_internal(id); } void ConfigFile::set_internal(const std::string &idIn, const std::string &val, const std::string &type) throw (UnregisteredTypeException){ Maps &maps = getMapsInstanceRef(); std::map::iterator it = maps.typeMap.find(type); if(it == maps.typeMap.end()) throw UnregisteredTypeException(type); std::string id=m_sDefaultPrefix + idIn; Entry &e = m_entries[id]; e.parent = this; e.id = id; e.rttiType = type; e.value = val; add_to_doc(*m_doc,id,it->second,val); } void ConfigFile::listContents() const{ std::cout << "config file entries:" << std::endl; for(std::map::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it){ std::cout << (it->second.id) << "\t" << (it->second.value) << "\t" << (it->second.rttiType) << "\t"; if(it->second.restr) std::cout << "(restriction: " << it->second.restr->toString() << ")" << std::endl; else std::cout << "(no restriction)" << std::endl; } } bool ConfigFile::contains(const std::string &id) const{ if(m_sDefaultPrefix.length()){ return (m_entries.find(m_sDefaultPrefix+id) != m_entries.end()); }else{ return (m_entries.find(id) != m_entries.end()); } } ConfigFile::Data::Data(const std::string &id, ConfigFile &cf): id(id),cf(&cf){} void ConfigFile::setPrefix(const std::string &defaultPrefix) const{ m_sDefaultPrefix = defaultPrefix; } const std::string &ConfigFile::getPrefix() const { return m_sDefaultPrefix; } ConfigFile::Data ConfigFile::operator [](const std::string &id){ return Data(id,*this); } const ConfigFile::Data ConfigFile::operator[](const std::string &id) const throw (EntryNotFoundException){ if(!contains(id)){ throw EntryNotFoundException(m_sDefaultPrefix+id); } return Data(id,const_cast(*this)); } std::ostream &operator<<(ostream &s, const ConfigFile &cf){ cf.m_doc->save(s); return s; } /// Singelton object ConfigFile ConfigFile::s_oConfig; std::vector ConfigFile::find(const std::string ®ex){ std::vector ret; for(std::map::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it){ if(match(it->first, regex)){ ret.push_back(Data(it->first,*this)); } } return ret; } } // namespace utils }