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 if (base.first.empty())
00104 return base.second;
00105
00106 std::string ns = getMinimalPathTo(base.first + type.getBasename(), relative_to);
00107 boost::replace_all(ns, Typelib::NamespaceMarkString, "::");
00108 return normalizeIDLName(ns) + base.second;
00109 }
00110 else return base.second;
00111 }
00112
00116 static string getIDLAbsolute(Type const& type, IDLExport const& exporter, std::string const& field_name = std::string())
00117 {
00118 pair<string, string> base = getIDLBase(type, exporter, field_name);
00119 if (!base.first.empty())
00120 {
00121 std::string ns = base.first;
00122 boost::replace_all(ns, Typelib::NamespaceMarkString, "::");
00123 return normalizeIDLName(ns) + base.second;
00124 }
00125 else return base.second;
00126 }
00127
00128 std::string IDLTypeIdentifierVisitor::getIDLAbsolute(Type const& type, std::string const& field_name)
00129 {
00130 return ::getIDLAbsolute(type, m_exporter, field_name);
00131 }
00132 std::string IDLTypeIdentifierVisitor::getIDLRelative(Type const& type, std::string const& field_name)
00133 {
00134 return ::getIDLRelative(type, m_namespace, m_exporter, field_name);
00135 }
00136 pair<string, string> IDLTypeIdentifierVisitor::getIDLBase(Type const& type, std::string const& field_name)
00137 {
00138 return ::getIDLBase(type, m_exporter, field_name);
00139 }
00140
00141 bool IDLTypeIdentifierVisitor::visit_(OpaqueType const& type)
00142 {
00143 if (m_exporter.marshalOpaquesAsAny())
00144 {
00145 m_namespace = "";
00146 m_front = "any";
00147 }
00148 else
00149 throw UnsupportedType(type, "opaque types are not allowed in IDL");
00150
00151 return true;
00152 }
00153 bool IDLTypeIdentifierVisitor::visit_(NullType const& type)
00154 { throw UnsupportedType(type, "null types are not supported for export in IDL"); }
00155 bool IDLTypeIdentifierVisitor::visit_(Container const& type)
00156 {
00157 if (type.getName() == "/std/string")
00158 {
00159 m_namespace = "";
00160 m_front = "string";
00161 }
00162 else
00163 {
00164
00165 m_namespace = getIDLBase(type.getIndirection()).first;
00166
00167
00168 if (m_namespace.empty())
00169 m_namespace = getIDLAbsoluteNamespace("/", m_exporter);
00170
00171 std::string container_kind = Typelib::getTypename(type.kind());
00172 std::string element_name = type.getIndirection().getName();
00173 boost::replace_all(element_name, Typelib::NamespaceMarkString, "_");
00174 boost::replace_all(element_name, " ", "_");
00175 m_front = container_kind + "_" + element_name + "_";
00176 }
00177
00178 return true;
00179 }
00180 bool IDLTypeIdentifierVisitor::visit_(Compound const& type)
00181 { m_front = type.getBasename();
00182 return true; }
00183 bool IDLTypeIdentifierVisitor::visit_(Numeric const& type)
00184 {
00185 m_namespace = "";
00186 if (type.getName() == "/bool")
00187 {
00188 m_front = "boolean";
00189 }
00190 else if (type.getNumericCategory() != Numeric::Float)
00191 {
00192 if (type.getNumericCategory() == Numeric::UInt && type.getSize() != 1)
00193 m_front = "unsigned ";
00194 switch (type.getSize())
00195 {
00196 case 1: m_front += "octet"; break;
00197 case 2: m_front += "short"; break;
00198 case 4: m_front += "long"; break;
00199 case 8: m_front += "long long"; break;
00200 }
00201 }
00202 else
00203 {
00204 if (type.getSize() == 4)
00205 m_front = "float";
00206 else
00207 m_front = "double";
00208 }
00209 return true;
00210 }
00211 bool IDLTypeIdentifierVisitor::visit_(Pointer const& type)
00212 { throw UnsupportedType(type, "pointer types are not supported for export in IDL"); }
00213 bool IDLTypeIdentifierVisitor::visit_(Array const& type)
00214 {
00215 if (type.getIndirection().getCategory() == Type::Array)
00216 throw UnsupportedType(type, "multi-dimensional arrays are not supported in IDL");
00217
00218 pair<string, string> element_t = getIDLBase(type.getIndirection());
00219 m_namespace = element_t.first;
00220 m_front = element_t.second;
00221 m_back = "[" + boost::lexical_cast<string>(type.getDimension()) + "]";
00222 return true;
00223 }
00224 bool IDLTypeIdentifierVisitor::visit_(Enum const& type)
00225 { m_front = type.getBasename();
00226 return true; }
00227
00228 class IDLExportVisitor : public TypeVisitor
00229 {
00230 public:
00231 IDLExport const& m_exporter;
00232 ostringstream m_stream;
00233 string m_indent;
00234 string m_namespace;
00235 std::map<std::string, Type const*>& m_exported_typedefs;
00236
00237 bool visit_(OpaqueType const& type);
00238 bool visit_(Container const& type);
00239 bool visit_(Compound const& type);
00240 bool visit_(Compound const& type, Field const& field);
00241
00242 bool visit_(Numeric const& type);
00243
00244 bool visit_(Pointer const& type);
00245 bool visit_(Array const& type);
00246
00247 bool visit_(Enum const& type);
00248
00249 IDLExportVisitor(Registry const& registry, IDLExport const& exporter, std::map<std::string, Type const*>& exported_typedefs);
00250 std::string getResult() const { return m_stream.str(); }
00251 std::string getTargetNamespace() const { return m_namespace; }
00252 void setTargetNamespace(std::string const& target_namespace)
00253 {
00254 size_t ns_size = utilmm::split(target_namespace, "/").size();
00255 m_indent = string(ns_size * 4, ' ');
00256 m_namespace = target_namespace;
00257 }
00258 std::string getIDLAbsolute(Type const& type, std::string const& field_name = "");
00259 std::string getIDLRelative(Type const& type, std::string const& field_name = "");
00260 pair<string, string> getIDLBase(Type const& type, std::string const& field_name = "");
00261 void apply(Type const& type)
00262 {
00263 setTargetNamespace(getIDLAbsoluteNamespace(type.getNamespace(), m_exporter));
00264 TypeVisitor::apply(type);
00265 }
00266 };
00267
00268 std::string IDLExportVisitor::getIDLAbsolute(Type const& type,
00269 std::string const& field_name)
00270 {
00271 return ::getIDLAbsolute(type, m_exporter, field_name);
00272 }
00273 std::string IDLExportVisitor::getIDLRelative(Type const& type,
00274 std::string const& field_name)
00275 {
00276 return ::getIDLRelative(type, m_namespace, m_exporter, field_name);
00277 }
00278 pair<string, string> IDLExportVisitor::getIDLBase(Type const& type,
00279 std::string const& field_name)
00280 {
00281 return ::getIDLBase(type, m_exporter, field_name);
00282 }
00283
00284 struct Indent
00285 {
00286 string& m_indent;
00287 string m_save;
00288 Indent(string& current)
00289 : m_indent(current), m_save(current)
00290 { m_indent += " "; }
00291 ~Indent() { m_indent = m_save; }
00292 };
00293
00294 IDLExportVisitor::IDLExportVisitor(Registry const& registry, IDLExport const& exporter,
00295 std::map<std::string, Type const*>& exported_typedefs)
00296 : m_exporter(exporter)
00297 , m_exported_typedefs(exported_typedefs) {}
00298
00299 bool IDLExportVisitor::visit_(Compound const& type)
00300 {
00301 m_stream << m_indent << "struct " << normalizeIDLName(type.getBasename()) << " {\n";
00302
00303 { Indent indenter(m_indent);
00304 TypeVisitor::visit_(type);
00305 }
00306
00307 m_stream << m_indent
00308 << "};\n";
00309
00310 return true;
00311 }
00312 bool IDLExportVisitor::visit_(Compound const& type, Field const& field)
00313 {
00314 m_stream
00315 << m_indent
00316 << getIDLRelative(field.getType(), field.getName())
00317 << ";\n";
00318
00319 return true;
00320 }
00321
00322 bool IDLExportVisitor::visit_(Numeric const& type)
00323 {
00324
00325
00326 return true;
00327 }
00328
00329 bool IDLExportVisitor::visit_ (Pointer const& type)
00330 {
00331 throw UnsupportedType(type, "pointers are not allowed in IDL");
00332 return true;
00333 }
00334 bool IDLExportVisitor::visit_ (Array const& type)
00335 {
00336 throw UnsupportedType(type, "top-level arrays are not handled by the IDLExportVisitor");
00337 return true;
00338 }
00339
00340 bool IDLExportVisitor::visit_ (Enum const& type)
00341 {
00342 m_stream << m_indent << "enum " << type.getBasename() << " { ";
00343
00344 utilmm::stringlist symbols;
00345 Enum::ValueMap const& values = type.values();
00346 Enum::ValueMap::const_iterator it, end = values.end();
00347 for (it = values.begin(); it != values.end(); ++it)
00348 symbols.push_back(it->first);
00349 m_stream << utilmm::join(symbols, ", ") << " };\n";
00350
00351 return true;
00352 }
00353
00354 bool IDLExportVisitor::visit_(Container const& type)
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 = getIDLRelative(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 << getIDLRelative(array_t.getIndirection(), type_namespace)
00545 << " " << type.getBasename() << "[" << array_t.getDimension() << "];";
00546 }
00547 else if (type->getCategory() == Type::Container)
00548 {
00549
00550 Container const& container_t = dynamic_cast<Container const&>(*type);
00551 stream << "sequence<" << getIDLRelative(container_t.getIndirection(), type_namespace) << "> " << 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 << getIDLRelative(*type, type_namespace) << " " << 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