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
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
00031
00032
00033
00034
00035 namespace
00036 {
00037
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
00060
00061 namespace
00062 {
00063 void loadMetaData(xmlNodePtr node, MetaData& metadata)
00064 {
00065 for(xmlNodePtr xml = xmlFirstElementChild(node); xml; xml=xmlNextElementSibling(xml))
00066 {
00067 if (xmlStrcmp(xml->name, reinterpret_cast<const xmlChar*>("metadata")))
00068 continue;
00069 string key = getAttribute<string>(xml, "key");
00070 string value;
00071
00072 xmlNodePtr cdata = xml->children;
00073 for(; cdata; cdata=cdata->next)
00074 {
00075 if (cdata->type == XML_CDATA_SECTION_NODE)
00076 {
00077 value = reinterpret_cast<char const*>(cdata->content);
00078 break;
00079 }
00080 }
00081
00082 metadata.add(key, value);
00083 }
00084 }
00085
00086 void loadMetaData(TypeNode const& node, MetaData& metadata)
00087 {
00088 loadMetaData(node.xml, metadata);
00089 }
00090
00091 class Factory
00092 {
00093 TypeMap m_map;
00094 Registry& m_registry;
00095
00096 public:
00097 Factory(Registry& registry)
00098 : m_registry(registry) {}
00099
00100 Registry& getRegistry() { return m_registry; }
00101
00102 void insert(TypeNode const& type, Type* object)
00103 {
00104 loadMetaData(type, object->getMetaData());
00105 m_registry.add(object, type.file);
00106 }
00107
00108 void alias (TypeNode const& type, string const& new_name)
00109 { m_registry.alias(new_name, type.name); }
00110
00111 void build(TypeMap const& map)
00112 {
00113 m_map = map;
00114 while(! m_map.empty())
00115 build(m_map.begin()->first);
00116 }
00117
00118
00119
00120 Type const* build(string const name)
00121 {
00122 string basename = TypeBuilder::getBaseTypename(name);
00123
00124 Type const* base = m_registry.get(basename);
00125 TypeMap::iterator it = m_map.find(basename);
00126
00127 if (! base)
00128 {
00129 if (it == m_map.end())
00130 throw Undefined(basename);
00131
00132 TypeNode type(it->second);
00133 m_map.erase(it);
00134
00135 base = type.loader(type, *this);
00136
00137 if (base->getName() != basename && !m_registry.has(basename, false))
00138 m_registry.alias(base->getName(), basename);
00139 }
00140 else if (it != m_map.end())
00141 {
00142
00143
00144
00145 m_map.erase(it);
00146 }
00147
00148 if (basename == name)
00149 return base;
00150
00151 Type const* derived = m_registry.build(name);
00152 if (derived->getName() != name && !m_registry.has(name, false))
00153 m_registry.alias(derived->getName(), name);
00154
00155 it = m_map.find(name);
00156 if (it != m_map.end())
00157 m_map.erase(it);
00158 return derived;
00159 }
00160 };
00161 }
00162
00163
00164 namespace
00165 {
00166 struct NumericCategory
00167 {
00168 const xmlChar* name;
00169 Numeric::NumericCategory type;
00170 };
00171 NumericCategory numeric_categories[] = {
00172 { reinterpret_cast<const xmlChar*>("sint"), Numeric::SInt },
00173 { reinterpret_cast<const xmlChar*>("uint"), Numeric::UInt },
00174 { reinterpret_cast<const xmlChar*>("float"), Numeric::Float },
00175 { 0, Numeric::SInt }
00176 };
00177 Type const* load_numeric(TypeNode const& node, Factory& factory)
00178 {
00179 NumericCategory category = getCategoryFromNode
00180 ( numeric_categories
00181 , reinterpret_cast<xmlChar const*>(getAttribute<string>(node.xml, "category").c_str()) );
00182 size_t size = getAttribute<size_t>(node.xml, "size");
00183
00184 Type* type = new Numeric(node.name, size, category.type);
00185 factory.insert(node, type);
00186 return type;
00187 }
00188 Type const* load_null(TypeNode const& node, Factory& factory)
00189 {
00190 Type* type = new NullType(node.name);
00191 factory.insert(node, type);
00192 return type;
00193 }
00194 Type const* load_opaque(TypeNode const& node, Factory& factory)
00195 {
00196 size_t size = getAttribute<size_t>(node.xml, "size");
00197 Type* type = new OpaqueType(node.name, size);
00198 factory.insert(node, type);
00199 return type;
00200 }
00201 Type const* load_container(TypeNode const& node, Factory& factory)
00202 {
00203 string indirect_name = getAttribute<string>(node.xml, "of");
00204 Type const* indirect = factory.build(indirect_name);
00205 string kind = getAttribute<string>(node.xml, "kind");
00206 bool has_size = false;
00207 int size = 0;
00208 try {
00209 size = getAttribute<int>(node.xml, "size");
00210 has_size = true;
00211 }
00212 catch(Parsing::MissingAttribute) {}
00213
00214 Type const* container = &Container::createContainer(factory.getRegistry(), kind, *indirect);
00215
00216
00217 if (has_size && size != 0)
00218 {
00219
00220
00221 factory.getRegistry().get_(*container).setSize(size);
00222 }
00223 loadMetaData(node, container->getMetaData());
00224 return container;
00225 }
00226 Type const* load_enum(TypeNode const& node, Factory& factory)
00227 {
00228 Enum* type = new Enum(node.name);
00229 for(xmlNodePtr xml = xmlFirstElementChild(node.xml); xml; xml=xmlNextElementSibling(xml))
00230 {
00231 if (xmlStrcmp(xml->name, reinterpret_cast<const xmlChar*>("value")))
00232 continue;
00233
00234 string symbol = getAttribute<string>(xml, "symbol");
00235 Enum::integral_type value = getAttribute<Enum::integral_type>(xml, "value");
00236 type->add(symbol, value);
00237 }
00238
00239 factory.insert(node, type);
00240 return type;
00241 }
00242 Type const* load_alias(TypeNode const& node, Factory& factory)
00243 {
00244 string source = getAttribute<string>(node.xml, "source");
00245 Type const* type = factory.build(source);
00246 factory.alias(node, source);
00247 return type;
00248 }
00249
00250
00251 Type const* load_compound(TypeNode const& node, Factory& factory)
00252 {
00253 Compound* compound = new Compound(node.name);
00254 size_t size = getAttribute<size_t>(node.xml, "size");
00255
00256 for(xmlNodePtr xml = xmlFirstElementChild(node.xml); xml; xml=xmlNextElementSibling(xml))
00257 {
00258 if (xmlStrcmp(xml->name, reinterpret_cast<const xmlChar*>("field")))
00259 continue;
00260
00261
00262 string name = getAttribute<string>(xml, "name");
00263 string tname = getAttribute<string>(xml, "type");
00264 size_t offset = getAttribute<size_t>(xml, "offset");
00265
00266 Type const* type = factory.build(tname);
00267 Field const& field = compound->addField(name, *type, offset);
00268 loadMetaData(xml, field.getMetaData());
00269 }
00270
00271 compound->setSize(size);
00272 factory.insert(node, compound);
00273 return compound;
00274 }
00275 }
00276
00277
00278 namespace
00279 {
00280 struct NodeCategories
00281 {
00282 const xmlChar* name;
00283 NodeLoader loader;
00284 };
00285
00286 NodeCategories node_categories[] = {
00287 { reinterpret_cast<const xmlChar*>("alias"), load_alias },
00288 { reinterpret_cast<const xmlChar*>("numeric"), load_numeric },
00289 { reinterpret_cast<const xmlChar*>("null"), load_null },
00290 { reinterpret_cast<const xmlChar*>("opaque"), load_opaque },
00291 { reinterpret_cast<const xmlChar*>("enum"), load_enum },
00292 { reinterpret_cast<const xmlChar*>("compound"), load_compound },
00293 { reinterpret_cast<const xmlChar*>("container"), load_container },
00294 { 0, 0 }
00295 };
00296
00297 void load(string const& file, TypeMap& map, xmlNodePtr type_node)
00298 {
00299 string name = getStringAttribute(type_node, "name");
00300 NodeCategories cat = getCategoryFromNode(node_categories, type_node->name);
00301
00302 TypeMap::iterator it = map.find(name);
00303 if (it != map.end())
00304 {
00305 string old_file = it->second.file;
00306 std::clog << "Type " << name << " has already been defined in " << old_file << endl;
00307 std::clog << "\tThe definition found in " << file << " will be ignored" << endl;
00308 }
00309 else
00310 {
00311 string type_file = file;
00312 try { type_file = getStringAttribute(type_node, "source_id"); }
00313 catch(Parsing::MissingAttribute) {}
00314 map[name] = TypeNode(type_node, name, type_file, cat.loader);
00315 }
00316 }
00317
00318 void parse(std::string const& source_id, xmlDocPtr doc, Registry& registry)
00319 {
00320 if (!doc)
00321 throw Parsing::MalformedXML("error parsing '"+source_id+"'");
00322
00323 xmlNodePtr root_node = xmlDocGetRootElement(doc);
00324 if (!root_node)
00325 return;
00326
00327 checkNodeName<Parsing::BadRootElement>(root_node, "typelib");
00328
00329 TypeMap all_types;
00330 for(xmlNodePtr node = root_node -> xmlChildrenNode; node; node=node->next)
00331 {
00332 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("comment")))
00333 continue;
00334 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("text")))
00335 continue;
00336
00337 ::load(source_id, all_types, node);
00338 }
00339
00340 Factory factory(registry);
00341 factory.build(all_types);
00342 }
00343 }
00344
00345 void TlbImport::load
00346 ( std::istream& stream
00347 , utilmm::config_set const& config
00348 , Typelib::Registry& registry)
00349 {
00350
00351
00352 std::string document;
00353
00354 while (stream.good())
00355 {
00356 std::stringbuf buffer;
00357 stream >> &buffer;
00358 document += buffer.str();
00359 }
00360 xmlDocPtr doc = xmlParseMemory(document.c_str(), document.length());
00361 if (!doc)
00362 throw Parsing::MalformedXML();
00363
00364 try
00365 {
00366
00367
00368 parse("", doc, registry);
00369 xmlFreeDoc(doc);
00370 }
00371 catch(...)
00372 {
00373 xmlFreeDoc(doc);
00374 throw;
00375 }
00376 }
00377
00378
00379 void TlbImport::load
00380 ( std::string const& path
00381 , utilmm::config_set const& config
00382 , Typelib::Registry& registry)
00383 {
00384
00385
00386 std::ifstream stream(path.c_str());
00387 if (!stream.good())
00388 throw Parsing::ImportError("problem opening '"+path+"' for import");
00389 load(stream, config, registry);
00390 }
00391