00001 #include "export.hh"
00002 #include <iostream>
00003 #include <utilmm/stringtools.hh>
00004
00005 #include <typelib/typevisitor.hh>
00006 #include <typelib/plugins.hh>
00007
00008 #include <boost/algorithm/string.hpp>
00009
00010 namespace
00011 {
00012 using namespace std;
00013 using namespace Typelib;
00014
00015 static string normalizeIDLName(std::string const& name)
00016 {
00017 unsigned int template_mark;
00018 string result = name;
00019 while ((template_mark = result.find_first_of("<>/,")) < result.length())
00020 result.replace(template_mark, 1, "_");
00021 return result;
00022 }
00023
00024 static string getIDLAbsoluteNamespace(std::string const& type_ns, IDLExport const& exporter)
00025 {
00026 string ns = type_ns;
00027 string prefix = exporter.getNamespacePrefix();
00028 string suffix = exporter.getNamespaceSuffix();
00029 if (!prefix.empty())
00030 ns = prefix + ns;
00031 if (!suffix.empty())
00032 ns += suffix;
00033 return ns;
00034 }
00035
00036 class IDLTypeIdentifierVisitor : public TypeVisitor
00037 {
00038 public:
00039 IDLExport const& m_exporter;
00040 string m_front, m_back;
00041 string m_namespace;
00042
00043 bool visit_(OpaqueType const& type);
00044 bool visit_(NullType const& type);
00045 bool visit_(Container const& type);
00046 bool visit_(Compound const& type);
00047
00048 bool visit_(Numeric const& type);
00049
00050 bool visit_(Pointer const& type);
00051 bool visit_(Array const& type);
00052
00053 bool visit_(Enum const& type);
00054
00055 IDLTypeIdentifierVisitor(IDLExport const& exporter)
00056 : m_exporter(exporter) {}
00057 string getTargetNamespace() const { return m_namespace; }
00058 std::string getIDLAbsolute(Type const& type, std::string const& field_name = "");
00059 std::string getIDLRelative(Type const& type, std::string const& field_name = "");
00060 pair<string, string> getIDLBase(Type const& type, std::string const& field_name = "");
00061 void apply(Type const& type)
00062 {
00063 m_namespace = getIDLAbsoluteNamespace(type.getNamespace(), m_exporter);
00064 TypeVisitor::apply(type);
00065 m_front = normalizeIDLName(m_front);
00066 }
00067 };
00068
00069 static bool isIDLBuiltinType(Type const& type)
00070 {
00071 if (type.getCategory() == Type::Numeric || type.getName() == "/std/string")
00072 return true;
00073 else if (type.getCategory() == Type::Array)
00074 {
00075 Type const& element_type = static_cast<Array const&>(type).getIndirection();
00076 return isIDLBuiltinType(element_type);
00077 }
00078 return false;
00079 }
00080
00084 static pair<string, string> getIDLBase(Type const& type, IDLExport const& exporter, std::string const& field_name = std::string())
00085 {
00086 std::string type_name;
00087 IDLTypeIdentifierVisitor visitor(exporter);
00088 visitor.apply(type);
00089 if (field_name.empty())
00090 return make_pair(visitor.getTargetNamespace(), visitor.m_front + visitor.m_back);
00091 else
00092 return make_pair(visitor.getTargetNamespace(), visitor.m_front + " " + field_name + visitor.m_back);
00093 }
00094
00098 static string getIDLRelative(Type const& type, std::string const& relative_to, IDLExport const& exporter, std::string const& field_name = std::string())
00099 {
00100 pair<string, string> base = getIDLBase(type, exporter, field_name);
00101 if (!base.first.empty())
00102 {
00103 std::string ns = getMinimalPathTo(base.first + type.getBasename(), relative_to);
00104 boost::replace_all(ns, Typelib::NamespaceMarkString, "::");
00105 return normalizeIDLName(ns) + base.second;
00106 }
00107 else return base.second;
00108 }
00109
00113 static string getIDLAbsolute(Type const& type, IDLExport const& exporter, std::string const& field_name = std::string())
00114 {
00115 pair<string, string> base = getIDLBase(type, exporter, field_name);
00116 if (!base.first.empty())
00117 {
00118 std::string ns = base.first;
00119 boost::replace_all(ns, Typelib::NamespaceMarkString, "::");
00120 return normalizeIDLName(ns) + base.second;
00121 }
00122 else return base.second;
00123 }
00124
00125 std::string IDLTypeIdentifierVisitor::getIDLAbsolute(Type const& type, std::string const& field_name)
00126 {
00127 return ::getIDLAbsolute(type, m_exporter, field_name);
00128 }
00129 std::string IDLTypeIdentifierVisitor::getIDLRelative(Type const& type, std::string const& field_name)
00130 {
00131 return ::getIDLRelative(type, m_namespace, m_exporter, field_name);
00132 }
00133 pair<string, string> IDLTypeIdentifierVisitor::getIDLBase(Type const& type, std::string const& field_name)
00134 {
00135 return ::getIDLBase(type, m_exporter, field_name);
00136 }
00137
00138 bool IDLTypeIdentifierVisitor::visit_(OpaqueType const& type)
00139 {
00140 if (m_exporter.marshalOpaquesAsAny())
00141 {
00142 m_namespace = "";
00143 m_front = "any";
00144 }
00145 else
00146 throw UnsupportedType(type, "opaque types are not allowed in IDL");
00147
00148 return true;
00149 }
00150 bool IDLTypeIdentifierVisitor::visit_(NullType const& type)
00151 { throw UnsupportedType(type, "null types are not supported for export in IDL"); }
00152 bool IDLTypeIdentifierVisitor::visit_(Container const& type)
00153 {
00154 if (type.getName() == "/std/string")
00155 {
00156 m_namespace = "";
00157 m_front = "string";
00158 }
00159 else
00160 {
00161
00162 m_namespace = getIDLBase(type.getIndirection()).first;
00163
00164
00165 if (m_namespace.empty())
00166 m_namespace = getIDLAbsoluteNamespace("/", m_exporter);
00167
00168 std::string container_kind = Typelib::getTypename(type.kind());
00169 std::string element_name = type.getIndirection().getName();
00170 boost::replace_all(element_name, Typelib::NamespaceMarkString, "_");
00171 boost::replace_all(element_name, " ", "_");
00172 m_front = container_kind + "_" + element_name + "_";
00173 }
00174
00175 return true;
00176 }
00177 bool IDLTypeIdentifierVisitor::visit_(Compound const& type)
00178 { m_front = type.getBasename();
00179 return true; }
00180 bool IDLTypeIdentifierVisitor::visit_(Numeric const& type)
00181 {
00182 m_namespace = "";
00183 if (type.getName() == "/bool")
00184 {
00185 m_front = "boolean";
00186 }
00187 else if (type.getNumericCategory() != Numeric::Float)
00188 {
00189 if (type.getNumericCategory() == Numeric::UInt && type.getSize() != 1)
00190 m_front = "unsigned ";
00191 switch (type.getSize())
00192 {
00193 case 1: m_front += "octet"; break;
00194 case 2: m_front += "short"; break;
00195 case 4: m_front += "long"; break;
00196 case 8: m_front += "long long"; break;
00197 }
00198 }
00199 else
00200 {
00201 if (type.getSize() == 4)
00202 m_front = "float";
00203 else
00204 m_front = "double";
00205 }
00206 return true;
00207 }
00208 bool IDLTypeIdentifierVisitor::visit_(Pointer const& type)
00209 { throw UnsupportedType(type, "pointer types are not supported for export in IDL"); }
00210 bool IDLTypeIdentifierVisitor::visit_(Array const& type)
00211 {
00212 if (type.getIndirection().getCategory() == Type::Array)
00213 throw UnsupportedType(type, "multi-dimensional arrays are not supported in IDL");
00214
00215 pair<string, string> element_t = getIDLBase(type.getIndirection());
00216 m_namespace = element_t.first;
00217 m_front = element_t.second;
00218 m_back = "[" + boost::lexical_cast<string>(type.getDimension()) + "]";
00219 return true;
00220 }
00221 bool IDLTypeIdentifierVisitor::visit_(Enum const& type)
00222 { m_front = type.getBasename();
00223 return true; }
00224
00225 class IDLExportVisitor : public TypeVisitor
00226 {
00227 public:
00228 IDLExport const& m_exporter;
00229 ostringstream m_stream;
00230 string m_indent;
00231 string m_namespace;
00232 std::map<std::string, Type const*>& m_exported_typedefs;
00233
00234 bool visit_(OpaqueType const& type);
00235 bool visit_(Container const& type);
00236 bool visit_(Compound const& type);
00237 bool visit_(Compound const& type, Field const& field);
00238
00239 bool visit_(Numeric const& type);
00240
00241 bool visit_(Pointer const& type);
00242 bool visit_(Array const& type);
00243
00244 bool visit_(Enum const& type);
00245
00246 IDLExportVisitor(Registry const& registry, IDLExport const& exporter, std::map<std::string, Type const*>& exported_typedefs);
00247 std::string getResult() const { return m_stream.str(); }
00248 std::string getTargetNamespace() const { return m_namespace; }
00249 void setTargetNamespace(std::string const& target_namespace)
00250 {
00251 size_t ns_size = utilmm::split(target_namespace, "/").size();
00252 m_indent = string(ns_size * 4, ' ');
00253 m_namespace = target_namespace;
00254 }
00255 std::string getIDLAbsolute(Type const& type, std::string const& field_name = "");
00256 std::string getIDLRelative(Type const& type, std::string const& field_name = "");
00257 pair<string, string> getIDLBase(Type const& type, std::string const& field_name = "");
00258 void apply(Type const& type)
00259 {
00260 setTargetNamespace(getIDLAbsoluteNamespace(type.getNamespace(), m_exporter));
00261 TypeVisitor::apply(type);
00262 }
00263 };
00264
00265 std::string IDLExportVisitor::getIDLAbsolute(Type const& type,
00266 std::string const& field_name)
00267 {
00268 return ::getIDLAbsolute(type, m_exporter, field_name);
00269 }
00270 std::string IDLExportVisitor::getIDLRelative(Type const& type,
00271 std::string const& field_name)
00272 {
00273 return ::getIDLRelative(type, m_namespace, m_exporter, field_name);
00274 }
00275 pair<string, string> IDLExportVisitor::getIDLBase(Type const& type,
00276 std::string const& field_name)
00277 {
00278 return ::getIDLBase(type, m_exporter, field_name);
00279 }
00280
00281 struct Indent
00282 {
00283 string& m_indent;
00284 string m_save;
00285 Indent(string& current)
00286 : m_indent(current), m_save(current)
00287 { m_indent += " "; }
00288 ~Indent() { m_indent = m_save; }
00289 };
00290
00291 IDLExportVisitor::IDLExportVisitor(Registry const& registry, IDLExport const& exporter,
00292 std::map<std::string, Type const*>& exported_typedefs)
00293 : m_exporter(exporter)
00294 , m_exported_typedefs(exported_typedefs) {}
00295
00296 bool IDLExportVisitor::visit_(Compound const& type)
00297 {
00298 m_stream << m_indent << "struct " << normalizeIDLName(type.getBasename()) << " {\n";
00299
00300 { Indent indenter(m_indent);
00301 TypeVisitor::visit_(type);
00302 }
00303
00304 m_stream << m_indent
00305 << "};\n";
00306
00307 return true;
00308 }
00309 bool IDLExportVisitor::visit_(Compound const& type, Field const& field)
00310 {
00311 m_stream
00312 << m_indent
00313 << getIDLAbsolute(field.getType(), field.getName())
00314 << ";\n";
00315
00316 return true;
00317 }
00318
00319 bool IDLExportVisitor::visit_(Numeric const& type)
00320 {
00321
00322
00323 return true;
00324 }
00325
00326 bool IDLExportVisitor::visit_ (Pointer const& type)
00327 {
00328 throw UnsupportedType(type, "pointers are not allowed in IDL");
00329 return true;
00330 }
00331 bool IDLExportVisitor::visit_ (Array const& type)
00332 {
00333 throw UnsupportedType(type, "top-level arrays are not handled by the IDLExportVisitor");
00334 return true;
00335 }
00336
00337 bool IDLExportVisitor::visit_ (Enum const& type)
00338 {
00339 m_stream << m_indent << "enum " << type.getBasename() << " { ";
00340
00341 utilmm::stringlist symbols;
00342 Enum::ValueMap const& values = type.values();
00343 Enum::ValueMap::const_iterator it, end = values.end();
00344 for (it = values.begin(); it != end; ++it)
00345 symbols.push_back(it->first);
00346 m_stream << utilmm::join(symbols, ", ") << " };\n";
00347
00348 return true;
00349 }
00350
00351 bool IDLExportVisitor::visit_(Container const& type)
00352 {
00353 if (type.getName() == "/std/string")
00354 return true;
00355
00356
00357
00358
00359 string target_namespace = getIDLBase(type.getIndirection()).first;
00360
00361
00362 if (target_namespace.empty())
00363 target_namespace = getIDLAbsoluteNamespace("/", m_exporter);
00364 setTargetNamespace(target_namespace);
00365
00366 std::string element_name = getIDLAbsolute(type.getIndirection());
00367 std::string typedef_name = getIDLBase(type).second;
00368 boost::replace_all(typedef_name, "::", "_");
00369 m_stream << m_indent << "typedef sequence<" << element_name << "> " << typedef_name << ";\n";
00370 m_exported_typedefs.insert(make_pair(type.getIndirection().getNamespace() + typedef_name, &type));
00371
00372 return true;
00373 }
00374 bool IDLExportVisitor::visit_(OpaqueType const& type)
00375 {
00376 if (m_exporter.marshalOpaquesAsAny())
00377 return true;
00378
00379 throw UnsupportedType(type, "opaque types are not supported for export in IDL");
00380 }
00381 }
00382
00383 using namespace std;
00384 IDLExport::IDLExport()
00385 : m_namespace("/"), m_opaque_as_any(false) {}
00386
00387 bool IDLExport::marshalOpaquesAsAny() const { return m_opaque_as_any; }
00388
00389 void IDLExport::end
00390 ( ostream& stream
00391 , Typelib::Registry const& )
00392 {
00393 generateTypedefs(stream);
00394
00395
00396 utilmm::stringlist
00397 ns_levels = utilmm::split(m_namespace, "/");
00398 closeNamespaces(stream, ns_levels.size());
00399 }
00400
00401 void IDLExport::closeNamespaces(ostream& stream, int levels)
00402 {
00403 for (int i = 0; i < levels; ++i)
00404 {
00405 m_indent = std::string(m_indent, 0, m_indent.size() - 4);
00406 stream << "\n" << m_indent << "};\n";
00407 }
00408 }
00409
00410 void IDLExport::checkType(Type const& type)
00411 {
00412 if (type.getCategory() == Type::Pointer)
00413 throw UnsupportedType(type, "pointers are not allowed in IDL");
00414 if (type.getCategory() == Type::Array)
00415 {
00416 Type::Category pointed_to = static_cast<Indirect const&>(type).getIndirection().getCategory();
00417 if (pointed_to == Type::Array || pointed_to == Type::Pointer)
00418 throw UnsupportedType(type, "multi-dimensional arrays are not supported yet");
00419 }
00420
00421 }
00422
00423 void IDLExport::adaptNamespace(ostream& stream, string const& ns)
00424 {
00425 if (m_namespace != ns)
00426 {
00427 utilmm::stringlist
00428 old_namespace = utilmm::split(m_namespace, "/"),
00429 new_namespace = utilmm::split(ns, "/");
00430
00431 while(!old_namespace.empty() && !new_namespace.empty() && old_namespace.front() == new_namespace.front())
00432 {
00433 old_namespace.pop_front();
00434 new_namespace.pop_front();
00435 }
00436
00437 closeNamespaces(stream, old_namespace.size());
00438
00439 while (!new_namespace.empty())
00440 {
00441 stream << m_indent << "module " << normalizeIDLName(new_namespace.front()) << " {\n";
00442 m_indent += " ";
00443 new_namespace.pop_front();
00444 }
00445 }
00446 m_namespace = ns;
00447 }
00448
00449 std::string IDLExport::getIDLAbsolute(Type const& type) const
00450 {
00451 return ::getIDLAbsolute(type, *this);
00452 }
00453
00454 std::string IDLExport::getIDLRelative(Type const& type, std::string const& relative_to) const
00455 {
00456 return ::getIDLRelative(type, relative_to, *this);
00457 }
00458
00459 void IDLExport::save
00460 ( ostream& stream
00461 , utilmm::config_set const& config
00462 , Typelib::Registry const& type )
00463 {
00464 m_ns_prefix = config.get<std::string>("namespace_prefix", "");
00465 if (!m_ns_prefix.empty() && string(m_ns_prefix, 0, 1) != Typelib::NamespaceMarkString)
00466 m_ns_prefix = Typelib::NamespaceMarkString + m_ns_prefix;
00467 m_ns_suffix = config.get<std::string>("namespace_suffix", "");
00468 if (!m_ns_suffix.empty() && string(m_ns_suffix, m_ns_suffix.length() - 1, 1) != Typelib::NamespaceMarkString)
00469 m_ns_suffix += Typelib::NamespaceMarkString;
00470 m_blob_threshold = config.get<int>("blob_threshold", 0);
00471 m_opaque_as_any = config.get<bool>("opaque_as_any", false);
00472 list<string> selection = config.get< list<string> >("selected");
00473 m_selected_types = set<string>(selection.begin(), selection.end());
00474 return Exporter::save(stream, config, type);
00475 }
00476
00477 void IDLExport::generateTypedefs(ostream& stream)
00478 {
00479 for (TypedefMap::const_iterator it = m_typedefs.begin();
00480 it != m_typedefs.end(); ++it)
00481 {
00482 adaptNamespace(stream, it->first);
00483
00484 list<string> const& lines = it->second;
00485 for (list<string>::const_iterator str_it = lines.begin(); str_it != lines.end(); ++str_it)
00486 stream << m_indent << "typedef " << *str_it << std::endl;
00487 }
00488 }
00489
00490 std::string IDLExport::getNamespacePrefix() const
00491 {
00492 return m_ns_prefix;
00493 }
00494 std::string IDLExport::getNamespaceSuffix() const
00495 {
00496 return m_ns_suffix;
00497 }
00498
00499 bool IDLExport::save
00500 ( ostream& stream
00501 , Typelib::RegistryIterator const& type )
00502 {
00503 if (! m_selected_types.empty())
00504 {
00505 if (m_selected_types.count(type->getName()) == 0)
00506 {
00507 return false;
00508 }
00509 }
00510
00511 if (type.isAlias())
00512 {
00513
00514 if (type->getNamespace() != type.getNamespace()
00515 || (type->getBasename() != "struct " + type.getBasename()
00516 && type->getBasename() != "enum " + type.getBasename()
00517 && type.getBasename() != "struct " + type->getBasename()
00518 && type.getBasename() != "enum " + type->getBasename()))
00519 {
00520 IDLExport::checkType(*type);
00521 ostringstream stream;
00522
00523 std::string type_namespace = getIDLAbsoluteNamespace(type.getNamespace(), *this);
00524
00525 map<string, Type const*>::const_iterator already_exported =
00526 m_exported_typedefs.find(type.getName());
00527 if (already_exported != m_exported_typedefs.end())
00528 {
00529 if (*already_exported->second != *type)
00530 throw UnsupportedType(*type, "the typedef name " + type.getName() + " is reserved by the IDL exporter");
00531 return false;
00532 }
00533 else if (type.getBasename().find_first_of("/<>") != std::string::npos)
00534 {
00535 return false;
00536 }
00537
00538
00539
00540 if (type->getCategory() == Type::Array)
00541 {
00542 Array const& array_t = dynamic_cast<Array const&>(*type);
00543 stream
00544 << getIDLAbsolute(array_t.getIndirection())
00545 << " " << type.getBasename() << "[" << array_t.getDimension() << "];";
00546 }
00547 else if (type->getCategory() == Type::Container && type->getName() != "/std/string")
00548 {
00549
00550 Container const& container_t = dynamic_cast<Container const&>(*type);
00551 stream << "sequence<" << getIDLAbsolute(container_t.getIndirection()) << "> " << type.getBasename() << ";";
00552 }
00553 else if (type->getCategory() == Type::Opaque)
00554 {
00555 if (marshalOpaquesAsAny())
00556 stream << "any " << type.getBasename() << ";";
00557 }
00558 else if (type.getBasename().find_first_of(" ") == string::npos)
00559 stream << getIDLAbsolute(*type) << " " << type.getBasename() << ";";
00560
00561 std::string def = stream.str();
00562 if (!def.empty())
00563 m_typedefs[type_namespace].push_back(def);
00564 return true;
00565 }
00566 else return false;
00567 }
00568 else
00569 {
00570
00571
00572
00573
00574
00575
00576
00577
00578 if (m_blob_threshold && static_cast<int>(type->getSize()) > m_blob_threshold)
00579 {
00580 string target_namespace = getIDLAbsoluteNamespace(type.getNamespace(), *this);
00581 size_t ns_size = utilmm::split(target_namespace, "/").size();
00582 string indent_string = string(ns_size * 4, ' ');
00583
00584 adaptNamespace(stream, target_namespace);
00585 stream << indent_string << "typedef sequence<octet> " << type->getBasename() << ";\n";
00586 return true;
00587 }
00588 else
00589 {
00590 IDLExportVisitor exporter(type.getRegistry(), *this, m_exported_typedefs);
00591 exporter.apply(*type);
00592
00593 string result = exporter.getResult();
00594 if (result.empty())
00595 return false;
00596 else
00597 {
00598 adaptNamespace(stream, exporter.getTargetNamespace());
00599 stream << result;
00600 return true;
00601 }
00602 }
00603 }
00604 }
00605
00606 TYPELIB_REGISTER_IO1(idl, IDLExport)
00607