$search
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 // Get the basename for "kind" 00162 m_namespace = getIDLBase(type.getIndirection()).first; 00163 // We generate all containers in orogen::Corba, even if their 00164 // element type is a builtin 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 // no need to export Numeric types, they are already defined by IDL 00322 // itself 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 // sequence<> can be used as-is, but in order to be as cross-ORB 00354 // compatible as possible we generate sequence typedefs and use them in 00355 // the compounds. Emit the sequence right now. 00356 string target_namespace = getIDLBase(type.getIndirection()).first; 00357 // We never emit sequences into the main namespace, even if their 00358 // element is a builtin 00359 if (target_namespace.empty()) 00360 target_namespace = getIDLAbsoluteNamespace("/", m_exporter); 00361 setTargetNamespace(target_namespace); 00362 00363 std::string element_name = getIDLAbsolute(type.getIndirection()); 00364 std::string typedef_name = getIDLBase(type).second; 00365 boost::replace_all(typedef_name, "::", "_"); 00366 m_stream << m_indent << "typedef sequence<" << element_name << "> " << typedef_name << ";\n"; 00367 m_exported_typedefs.insert(make_pair(type.getIndirection().getNamespace() + typedef_name, &type)); 00368 00369 return true; 00370 } 00371 bool IDLExportVisitor::visit_(OpaqueType const& type) 00372 { 00373 if (m_exporter.marshalOpaquesAsAny()) 00374 return true; 00375 00376 throw UnsupportedType(type, "opaque types are not supported for export in IDL"); 00377 } 00378 } 00379 00380 using namespace std; 00381 IDLExport::IDLExport() 00382 : m_namespace("/"), m_opaque_as_any(false) {} 00383 00384 bool IDLExport::marshalOpaquesAsAny() const { return m_opaque_as_any; } 00385 00386 void IDLExport::end 00387 ( ostream& stream 00388 , Typelib::Registry const& /*registry*/ ) 00389 { 00390 generateTypedefs(stream); 00391 00392 // Close the remaining namespaces 00393 utilmm::stringlist 00394 ns_levels = utilmm::split(m_namespace, "/"); 00395 closeNamespaces(stream, ns_levels.size()); 00396 } 00397 00398 void IDLExport::closeNamespaces(ostream& stream, int levels) 00399 { 00400 for (int i = 0; i < levels; ++i) 00401 { 00402 m_indent = std::string(m_indent, 0, m_indent.size() - 4); 00403 stream << "\n" << m_indent << "};\n"; 00404 } 00405 } 00406 00407 void IDLExport::checkType(Type const& type) 00408 { 00409 if (type.getCategory() == Type::Pointer) 00410 throw UnsupportedType(type, "pointers are not allowed in IDL"); 00411 if (type.getCategory() == Type::Array) 00412 { 00413 Type::Category pointed_to = static_cast<Indirect const&>(type).getIndirection().getCategory(); 00414 if (pointed_to == Type::Array || pointed_to == Type::Pointer) 00415 throw UnsupportedType(type, "multi-dimensional arrays are not supported yet"); 00416 } 00417 00418 } 00419 00420 void IDLExport::adaptNamespace(ostream& stream, string const& ns) 00421 { 00422 if (m_namespace != ns) 00423 { 00424 utilmm::stringlist 00425 old_namespace = utilmm::split(m_namespace, "/"), 00426 new_namespace = utilmm::split(ns, "/"); 00427 00428 while(!old_namespace.empty() && !new_namespace.empty() && old_namespace.front() == new_namespace.front()) 00429 { 00430 old_namespace.pop_front(); 00431 new_namespace.pop_front(); 00432 } 00433 00434 closeNamespaces(stream, old_namespace.size()); 00435 00436 while (!new_namespace.empty()) 00437 { 00438 stream << m_indent << "module " << normalizeIDLName(new_namespace.front()) << " {\n"; 00439 m_indent += " "; 00440 new_namespace.pop_front(); 00441 } 00442 } 00443 m_namespace = ns; 00444 } 00445 00446 std::string IDLExport::getIDLAbsolute(Type const& type) const 00447 { 00448 return ::getIDLAbsolute(type, *this); 00449 } 00450 00451 std::string IDLExport::getIDLRelative(Type const& type, std::string const& relative_to) const 00452 { 00453 return ::getIDLRelative(type, relative_to, *this); 00454 } 00455 00456 void IDLExport::save 00457 ( ostream& stream 00458 , utilmm::config_set const& config 00459 , Typelib::Registry const& type ) 00460 { 00461 m_ns_prefix = config.get<std::string>("namespace_prefix", ""); 00462 if (!m_ns_prefix.empty() && string(m_ns_prefix, 0, 1) != Typelib::NamespaceMarkString) 00463 m_ns_prefix = Typelib::NamespaceMarkString + m_ns_prefix; 00464 m_ns_suffix = config.get<std::string>("namespace_suffix", ""); 00465 if (!m_ns_suffix.empty() && string(m_ns_suffix, m_ns_suffix.length() - 1, 1) != Typelib::NamespaceMarkString) 00466 m_ns_suffix += Typelib::NamespaceMarkString; 00467 m_blob_threshold = config.get<int>("blob_threshold", 0); 00468 m_opaque_as_any = config.get<bool>("opaque_as_any", false); 00469 list<string> selection = config.get< list<string> >("selected"); 00470 m_selected_types = set<string>(selection.begin(), selection.end()); 00471 return Exporter::save(stream, config, type); 00472 } 00473 00474 void IDLExport::generateTypedefs(ostream& stream) 00475 { 00476 for (TypedefMap::const_iterator it = m_typedefs.begin(); 00477 it != m_typedefs.end(); ++it) 00478 { 00479 adaptNamespace(stream, it->first); 00480 00481 list<string> const& lines = it->second; 00482 for (list<string>::const_iterator str_it = lines.begin(); str_it != lines.end(); ++str_it) 00483 stream << m_indent << "typedef " << *str_it << std::endl; 00484 } 00485 } 00486 00487 std::string IDLExport::getNamespacePrefix() const 00488 { 00489 return m_ns_prefix; 00490 } 00491 std::string IDLExport::getNamespaceSuffix() const 00492 { 00493 return m_ns_suffix; 00494 } 00495 00496 bool IDLExport::save 00497 ( ostream& stream 00498 , Typelib::RegistryIterator const& type ) 00499 { 00500 if (! m_selected_types.empty()) 00501 { 00502 if (m_selected_types.count(type->getName()) == 0) 00503 { 00504 return false; 00505 } 00506 } 00507 00508 if (type.isAlias()) 00509 { 00510 // IDL has C++-like rules for struct and enums. Do not alias a "struct A" to "A"; 00511 if (type->getNamespace() != type.getNamespace() 00512 || (type->getBasename() != "struct " + type.getBasename() 00513 && type->getBasename() != "enum " + type.getBasename() 00514 && type.getBasename() != "struct " + type->getBasename() 00515 && type.getBasename() != "enum " + type->getBasename())) 00516 { 00517 IDLExport::checkType(*type); 00518 ostringstream stream; 00519 00520 std::string type_namespace = getIDLAbsoluteNamespace(type.getNamespace(), *this); 00521 00522 map<string, Type const*>::const_iterator already_exported = 00523 m_exported_typedefs.find(type.getName()); 00524 if (already_exported != m_exported_typedefs.end()) 00525 { 00526 if (*already_exported->second != *type) 00527 throw UnsupportedType(*type, "the typedef name " + type.getName() + " is reserved by the IDL exporter"); 00528 return false; 00529 } 00530 else if (type.getBasename().find_first_of("/<>") != std::string::npos) 00531 { 00532 return false; 00533 } 00534 00535 // Alias types using typedef, taking into account that the aliased type 00536 // may not be in the same module than the new alias. 00537 if (type->getCategory() == Type::Array) 00538 { 00539 Array const& array_t = dynamic_cast<Array const&>(*type); 00540 stream 00541 << getIDLAbsolute(array_t.getIndirection()) 00542 << " " << type.getBasename() << "[" << array_t.getDimension() << "];"; 00543 } 00544 else if (type->getCategory() == Type::Container) 00545 { 00546 // Generate a sequence, regardless of the actual container type 00547 Container const& container_t = dynamic_cast<Container const&>(*type); 00548 stream << "sequence<" << getIDLAbsolute(container_t.getIndirection()) << "> " << type.getBasename() << ";"; 00549 } 00550 else if (type->getCategory() == Type::Opaque) 00551 { 00552 if (marshalOpaquesAsAny()) 00553 stream << "any " << type.getBasename() << ";"; 00554 } 00555 else if (type.getBasename().find_first_of(" ") == string::npos) 00556 stream << getIDLAbsolute(*type) << " " << type.getBasename() << ";"; 00557 00558 std::string def = stream.str(); 00559 if (!def.empty()) 00560 m_typedefs[type_namespace].push_back(def); 00561 return true; 00562 } 00563 else return false; 00564 } 00565 else 00566 { 00567 // Don't call adaptNamespace right now, since some types can be simply 00568 // ignored by the IDLExportVisitor -- resulting in an empty module, 00569 // which is not accepted in IDL 00570 // 00571 // Instead, act "as if" the namespace was changed, capturing the output 00572 // in a temporary ostringstream and change namespace only if some output 00573 // has actually been generated 00574 00575 if (m_blob_threshold && static_cast<int>(type->getSize()) > m_blob_threshold) 00576 { 00577 string target_namespace = getIDLAbsoluteNamespace(type.getNamespace(), *this); 00578 size_t ns_size = utilmm::split(target_namespace, "/").size(); 00579 string indent_string = string(ns_size * 4, ' '); 00580 00581 adaptNamespace(stream, target_namespace); 00582 stream << indent_string << "typedef sequence<octet> " << type->getBasename() << ";\n"; 00583 return true; 00584 } 00585 else 00586 { 00587 IDLExportVisitor exporter(type.getRegistry(), *this, m_exported_typedefs); 00588 exporter.apply(*type); 00589 00590 string result = exporter.getResult(); 00591 if (result.empty()) 00592 return false; 00593 else 00594 { 00595 adaptNamespace(stream, exporter.getTargetNamespace()); 00596 stream << result; 00597 return true; 00598 } 00599 } 00600 } 00601 } 00602 00603 TYPELIB_REGISTER_IO1(idl, IDLExport) 00604