$search
00001 #include "import.hh" 00002 #include "xmltools.hh" 00003 #include <limits.h> 00004 00005 #include <typelib/typemodel.hh> 00006 #include <typelib/typebuilder.hh> 00007 #include <typelib/registry.hh> 00008 00009 #include <iostream> 00010 #include <fstream> 00011 00012 // Helpers 00013 namespace 00014 { 00015 using namespace std; 00016 using namespace Typelib; 00017 00018 template<typename Cat> 00019 Cat getCategoryFromNode(Cat* categories, xmlChar const* name ) 00020 { 00021 for(const Cat* cur_cat = categories; cur_cat->name; ++cur_cat) 00022 { 00023 if (!xmlStrcmp(name, cur_cat->name)) 00024 return *cur_cat; 00025 } 00026 throw std::runtime_error(string("unrecognized XML node '") + reinterpret_cast<char const*>(name) + "'"); 00027 } 00028 } 00029 00030 // Core function for loading 00031 // Loading is done in two passes: 00032 // - first, we register all type names present and the associated xml nodes and loading functions 00033 // by calling readNodes 00034 // - second, we load the Type objects in dependency order by calling load 00035 namespace 00036 { 00037 // A factory function that builds a type object from a node description 00038 struct TypeNode; 00039 class Factory; 00040 typedef Type const* (*NodeLoader) (TypeNode const& type, Factory& factory); 00041 00042 struct TypeNode 00043 { 00044 xmlNodePtr xml; 00045 string name; 00046 string file; 00047 NodeLoader loader; 00048 00049 TypeNode 00050 ( xmlNodePtr node = 0 00051 , string const& name_ = string() 00052 , string const& file_ = string() 00053 , NodeLoader loader_ = 0 ) 00054 : xml(node), name(name_), file(file_), loader(loader_) {} 00055 }; 00056 typedef std::map<string, TypeNode> TypeMap; 00057 } 00058 00059 // load loads the \c name type using the type nodes in \c remaining, which holds 00060 // types defined in the tlb file that aren't loaded yet 00061 namespace 00062 { 00063 class Factory 00064 { 00065 TypeMap m_map; 00066 Registry& m_registry; 00067 00068 public: 00069 Factory(Registry& registry) 00070 : m_registry(registry) {} 00071 00072 Registry& getRegistry() { return m_registry; } 00073 00074 void insert(TypeNode const& type, Type* object) 00075 { m_registry.add(object, type.file); } 00076 00077 void alias (TypeNode const& type, string const& new_name) 00078 { m_registry.alias(new_name, type.name); } 00079 00080 void build(TypeMap const& map) 00081 { 00082 m_map = map; 00083 while(! m_map.empty()) 00084 build(m_map.begin()->first); 00085 } 00086 00087 // Do NOT pass name by reference. It could be destroyed by the 00088 // m_map.erase(it) 00089 Type const* build(string const name) 00090 { 00091 string basename = TypeBuilder::getBaseTypename(name); 00092 00093 Type const* base = m_registry.get(basename); 00094 TypeMap::iterator it = m_map.find(basename); 00095 00096 if (! base) 00097 { 00098 if (it == m_map.end()) 00099 throw Undefined(basename); 00100 00101 TypeNode type(it->second); 00102 m_map.erase(it); 00103 00104 base = type.loader(type, *this); 00105 00106 if (base->getName() != basename && !m_registry.has(basename, false)) 00107 m_registry.alias(base->getName(), basename); 00108 } 00109 else if (it != m_map.end()) 00110 { 00111 // TODO check that the definition in the file 00112 // TODO and the definition in the registry 00113 // TODO match 00114 m_map.erase(it); 00115 } 00116 00117 if (basename == name) 00118 return base; 00119 00120 Type const* derived = m_registry.build(name); 00121 if (derived->getName() != name && !m_registry.has(name, false)) 00122 m_registry.alias(derived->getName(), name); 00123 00124 it = m_map.find(name); 00125 if (it != m_map.end()) 00126 m_map.erase(it); 00127 return derived; 00128 } 00129 }; 00130 } 00131 00132 // Loading nodes 00133 namespace 00134 { 00135 struct NumericCategory 00136 { 00137 const xmlChar* name; 00138 Numeric::NumericCategory type; 00139 }; 00140 NumericCategory numeric_categories[] = { 00141 { reinterpret_cast<const xmlChar*>("sint"), Numeric::SInt }, 00142 { reinterpret_cast<const xmlChar*>("uint"), Numeric::UInt }, 00143 { reinterpret_cast<const xmlChar*>("float"), Numeric::Float }, 00144 { 0, Numeric::SInt } 00145 }; 00146 Type const* load_numeric(TypeNode const& node, Factory& factory) 00147 { 00148 NumericCategory category = getCategoryFromNode 00149 ( numeric_categories 00150 , reinterpret_cast<xmlChar const*>(getAttribute<string>(node.xml, "category").c_str()) ); 00151 size_t size = getAttribute<size_t>(node.xml, "size"); 00152 00153 Type* type = new Numeric(node.name, size, category.type); 00154 factory.insert(node, type); 00155 return type; 00156 } 00157 Type const* load_null(TypeNode const& node, Factory& factory) 00158 { 00159 Type* type = new NullType(node.name); 00160 factory.insert(node, type); 00161 return type; 00162 } 00163 Type const* load_opaque(TypeNode const& node, Factory& factory) 00164 { 00165 size_t size = getAttribute<size_t>(node.xml, "size"); 00166 Type* type = new OpaqueType(node.name, size); 00167 factory.insert(node, type); 00168 return type; 00169 } 00170 Type const* load_container(TypeNode const& node, Factory& factory) 00171 { 00172 string indirect_name = getAttribute<string>(node.xml, "of"); 00173 Type const* indirect = factory.build(indirect_name); 00174 string kind = getAttribute<string>(node.xml, "kind"); 00175 bool has_size = false; 00176 int size = 0; 00177 try { 00178 size = getAttribute<int>(node.xml, "size"); 00179 has_size = true; 00180 } 00181 catch(Parsing::MissingAttribute) {} 00182 00183 Type const* container = &Container::createContainer(factory.getRegistry(), kind, *indirect); 00184 // We use zero size to indicate that the natural platform size should be 00185 // used 00186 if (has_size && size != 0) 00187 { 00188 // Update the size to match the one saved in the registry. This is to 00189 // allow a proper call to resize() later if needed. 00190 factory.getRegistry().get_(*container).setSize(size); 00191 } 00192 return container; 00193 } 00194 Type const* load_enum(TypeNode const& node, Factory& factory) 00195 { 00196 Enum* type = new Enum(node.name); 00197 for(xmlNodePtr xml = node.xml->xmlChildrenNode; xml; xml=xml->next) 00198 { 00199 if (!xmlStrcmp(xml->name, reinterpret_cast<const xmlChar*>("text"))) 00200 continue; 00201 00202 string symbol = getAttribute<string>(xml, "symbol"); 00203 Enum::integral_type value = getAttribute<Enum::integral_type>(xml, "value"); 00204 type->add(symbol, value); 00205 } 00206 00207 factory.insert(node, type); 00208 return type; 00209 } 00210 Type const* load_alias(TypeNode const& node, Factory& factory) 00211 { 00212 string source = getAttribute<string>(node.xml, "source"); 00213 Type const* type = factory.build(source); 00214 factory.alias(node, source); 00215 return type; 00216 } 00217 00218 00219 Type const* load_compound(TypeNode const& node, Factory& factory) 00220 { 00221 Compound* compound = new Compound(node.name); 00222 size_t size = getAttribute<size_t>(node.xml, "size"); 00223 00224 for(xmlNodePtr xml = node.xml->xmlChildrenNode; xml; xml=xml->next) 00225 { 00226 if (!xmlStrcmp(xml->name, reinterpret_cast<const xmlChar*>("text"))) 00227 continue; 00228 00229 //checkNodeName<UnexpectedElement> (xml, "field"); 00230 string name = getAttribute<string>(xml, "name"); 00231 string tname = getAttribute<string>(xml, "type"); 00232 size_t offset = getAttribute<size_t>(xml, "offset"); 00233 00234 Type const* type = factory.build(tname); 00235 compound->addField(name, *type, offset); 00236 } 00237 00238 compound->setSize(size); 00239 factory.insert(node, compound); 00240 return compound; 00241 } 00242 } 00243 00244 00245 namespace 00246 { 00247 struct NodeCategories 00248 { 00249 const xmlChar* name; 00250 NodeLoader loader; 00251 }; 00252 00253 NodeCategories node_categories[] = { 00254 { reinterpret_cast<const xmlChar*>("alias"), load_alias }, 00255 { reinterpret_cast<const xmlChar*>("numeric"), load_numeric }, 00256 { reinterpret_cast<const xmlChar*>("null"), load_null }, 00257 { reinterpret_cast<const xmlChar*>("opaque"), load_opaque }, 00258 { reinterpret_cast<const xmlChar*>("enum"), load_enum }, 00259 { reinterpret_cast<const xmlChar*>("compound"), load_compound }, 00260 { reinterpret_cast<const xmlChar*>("container"), load_container }, 00261 { 0, 0 } 00262 }; 00263 00264 void load(string const& file, TypeMap& map, xmlNodePtr type_node) 00265 { 00266 string name = getStringAttribute(type_node, "name"); 00267 NodeCategories cat = getCategoryFromNode(node_categories, type_node->name); 00268 00269 TypeMap::iterator it = map.find(name); 00270 if (it != map.end()) 00271 { 00272 string old_file = it->second.file; 00273 std::clog << "Type " << name << " has already been defined in " << old_file << endl; 00274 std::clog << "\tThe definition found in " << file << " will be ignored" << endl; 00275 } 00276 else 00277 { 00278 string type_file = file; 00279 try { type_file = getStringAttribute(type_node, "source_id"); } 00280 catch(Parsing::MissingAttribute) {} 00281 map[name] = TypeNode(type_node, name, type_file, cat.loader); 00282 } 00283 } 00284 00285 void parse(std::string const& source_id, xmlDocPtr doc, Registry& registry) 00286 { 00287 if (!doc) 00288 throw Parsing::MalformedXML(); 00289 00290 xmlNodePtr root_node = xmlDocGetRootElement(doc); 00291 if (!root_node) 00292 return; 00293 00294 checkNodeName<Parsing::BadRootElement>(root_node, "typelib"); 00295 00296 TypeMap all_types; 00297 for(xmlNodePtr node = root_node -> xmlChildrenNode; node; node=node->next) 00298 { 00299 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("comment"))) 00300 continue; 00301 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("text"))) 00302 continue; 00303 00304 ::load(source_id, all_types, node); 00305 } 00306 00307 Factory factory(registry); 00308 factory.build(all_types); 00309 } 00310 } 00311 00312 void TlbImport::load 00313 ( std::istream& stream 00314 , utilmm::config_set const& config 00315 , Typelib::Registry& registry) 00316 { 00317 // libXML is not really iostream-compatible :( 00318 // Get the whole document 00319 std::string document; 00320 00321 while (stream.good()) 00322 { 00323 std::stringbuf buffer; 00324 stream >> &buffer; 00325 document += buffer.str(); 00326 } 00327 xmlDocPtr doc = xmlParseMemory(document.c_str(), document.length()); 00328 if (!doc) 00329 throw Parsing::MalformedXML(); 00330 00331 try 00332 { 00333 parse("", doc, registry); 00334 xmlFreeDoc(doc); 00335 } 00336 catch(...) 00337 { 00338 xmlFreeDoc(doc); 00339 throw; 00340 } 00341 } 00342 00343 00344 void TlbImport::load 00345 ( std::string const& path 00346 , utilmm::config_set const& config 00347 , Typelib::Registry& registry) 00348 { 00349 // Loading from files seams to be broken on some distro's libxml2-packages, 00350 // so we load the whole file into memory before parsing 00351 std::ifstream stream(path.c_str()); 00352 load(stream, config, registry); 00353 } 00354