/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include "netproduce.hxx" #include "csharpfile.hxx" namespace { const std::unordered_set s_reservedKeywords{ "abstract"_ostr, "as"_ostr, "base"_ostr, "bool"_ostr, "break"_ostr, "byte"_ostr, "case"_ostr, "catch"_ostr, "char"_ostr, "checked"_ostr, "class"_ostr, "const"_ostr, "continue"_ostr, "decimal"_ostr, "default"_ostr, "delegate"_ostr, "do"_ostr, "double"_ostr, "else"_ostr, "enum"_ostr, "event"_ostr, "explicit"_ostr, "extern"_ostr, "false"_ostr, "finally"_ostr, "fixed"_ostr, "float"_ostr, "for"_ostr, "foreach"_ostr, "goto"_ostr, "if"_ostr, "implicit"_ostr, "in"_ostr, "int"_ostr, "interface"_ostr, "internal"_ostr, "is"_ostr, "lock"_ostr, "long"_ostr, "namespace"_ostr, "new"_ostr, "null"_ostr, "object"_ostr, "operator"_ostr, "out"_ostr, "override"_ostr, "params"_ostr, "private"_ostr, "protected"_ostr, "public"_ostr, "readonly"_ostr, "ref"_ostr, "return"_ostr, "sbyte"_ostr, "sealed"_ostr, "short"_ostr, "sizeof"_ostr, "stackalloc"_ostr, "static"_ostr, "string"_ostr, "struct"_ostr, "switch"_ostr, "this"_ostr, "throw"_ostr, "true"_ostr, "try"_ostr, "typeof"_ostr, "uint"_ostr, "ulong"_ostr, "unchecked"_ostr, "unsafe"_ostr, "ushort"_ostr, "using"_ostr, "virtual"_ostr, "void"_ostr, "volatile"_ostr, "while"_ostr, }; const std::unordered_map s_baseTypes{ { "boolean"_ostr, "bool"_ostr }, { "char"_ostr, "char"_ostr }, { "byte"_ostr, "sbyte"_ostr }, { "short"_ostr, "short"_ostr }, { "unsigned short"_ostr, "ushort"_ostr }, { "long"_ostr, "int"_ostr }, { "unsigned long"_ostr, "uint"_ostr }, { "hyper"_ostr, "long"_ostr }, { "unsigned hyper"_ostr, "ulong"_ostr }, { "float"_ostr, "float"_ostr }, { "double"_ostr, "double"_ostr }, { "string"_ostr, "string"_ostr }, { "void"_ostr, "void"_ostr }, { "type"_ostr, "System.Type"_ostr }, { "any"_ostr, "com.sun.star.uno.Any"_ostr }, { "com.sun.star.uno.Exception"_ostr, "com.sun.star.uno.UnoException"_ostr }, { "com.sun.star.uno.XInterface"_ostr, "com.sun.star.uno.IQueryInterface"_ostr }, }; std::tuple splitName(std::string_view name) { size_t split = name.find_last_of('.'); if (split != std::string_view::npos) return std::make_tuple(true, name.substr(0, split), name.substr(split + 1)); else return std::make_tuple(false, "", name); } OString getBaseUnoName(std::string_view name) { size_t start = name.find_first_not_of("[]"); if (start == std::string_view::npos) start = 0; size_t end = name.find_first_of('<'); if (end == std::string_view::npos) end = name.size(); return OString(name.substr(start, end - start)); } OString getBaseUnoName(std::u16string_view name) { return getBaseUnoName(u2b(name)); } OString getSafeIdentifier(std::string_view name) { OString temp(name); return s_reservedKeywords.contains(temp) ? "@"_ostr + temp : temp; } OString getSafeIdentifier(std::u16string_view name) { return getSafeIdentifier(u2b(name)); } void separatedForeach(const auto& items, auto&& sepFunc, auto&& itemFunc) { for (auto it = items.begin(); it != items.end(); ++it) { if (it != items.begin()) sepFunc(); itemFunc(*it); } } } void NetProducer::initProducer(const NetOptions& options) { m_outputDir = options.getOption("--output-dir"_ostr); m_dryRun = options.isValid("--dry-run"_ostr); m_verbose = options.isValid("--verbose"_ostr); if (options.isValid("--types"_ostr)) { const OString& names(options.getOption("--types"_ostr)); for (size_t i = 0; i != std::string_view::npos;) { std::string_view name(o3tl::getToken(names, ';', i)); if (name == "*") m_startingTypes.insert(""_ostr); else if (name.ends_with(".*")) m_startingTypes.emplace(name.substr(0, name.size() - 2)); else m_startingTypes.emplace(name); } } else { m_startingTypes.insert(""_ostr); } for (const OString& file : options.getInputFiles()) m_manager->loadProvider(convertToFileUrl(file), true); for (const OString& file : options.getExtraInputFiles()) m_manager->loadProvider(convertToFileUrl(file), false); } void NetProducer::produceAll() { for (const OString& name : m_startingTypes) produceType(name); } void NetProducer::produceType(const OString& name) { if (m_typesProduced.contains(name)) return; m_typesProduced.insert(name); if (s_baseTypes.contains(name)) return; OUString uname(b2u(name)); rtl::Reference entity; rtl::Reference cursor; if (m_manager->foundAtPrimaryProvider(uname)) { switch (m_manager->getSort(uname, &entity, &cursor)) { case codemaker::UnoType::Sort::Module: produceModule(name, cursor); break; case codemaker::UnoType::Sort::Enum: produceEnum(name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::PlainStruct: producePlainStruct(name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::PolymorphicStructTemplate: producePolyStruct( name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::Exception: produceException(name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::Interface: produceInterface(name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::Typedef: produceTypedef(name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::ConstantGroup: produceConstantGroup(name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::SingleInterfaceBasedService: produceService( name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::InterfaceBasedSingleton: produceSingleton( name, dynamic_cast(entity.get())); break; case codemaker::UnoType::Sort::AccumulationBasedService: case codemaker::UnoType::Sort::ServiceBasedSingleton: // old-style services and singletons not supported break; default: throw CannotDumpException(u"entity '"_ustr + uname + u"' has unexpected type"_ustr); } } else { // type from --extra-types switch (m_manager->getSort(uname, &entity, &cursor)) { case codemaker::UnoType::Sort::Typedef: produceTypedef(name, dynamic_cast(entity.get())); break; default: break; } } } void NetProducer::produceModule(std::string_view name, const rtl::Reference& cursor) { OUString moduleName; while (cursor->getNext(&moduleName).is()) { OString memberName = name.empty() ? u2b(moduleName) : name + "."_ostr + u2b(moduleName); produceType(memberName); } } void NetProducer::produceEnum(std::string_view name, const rtl::Reference& entity) { CSharpFile file(m_outputDir, name); if (m_verbose) std::cout << "[enum] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public enum ") .append(getSafeIdentifier(typeName)) .endLine() .beginBlock(); for (const auto& member : entity->getMembers()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); file.beginLine() .append(getSafeIdentifier(member.name)) .append(" = ") .append(OString::number(member.value)) .append(",") .endLine(); } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::producePlainStruct(std::string_view name, const rtl::Reference& entity) { // produce referenced types const auto& base = entity->getDirectBase(); if (!base.isEmpty()) produceType(getBaseUnoName(base)); for (const auto& member : entity->getDirectMembers()) produceType(getBaseUnoName(member.type)); CSharpFile file(m_outputDir, name); // verbose and dry run checks if (m_verbose) std::cout << "[struct] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); // output namespace block auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); // generate struct and base structs list file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public class ") .append(getSafeIdentifier(typeName)); if (!base.isEmpty()) file.append(" : ").append(getNetName(base)); file.endLine().beginBlock(); // generate default constructor file.beginLine() .append("public ") .append(getSafeIdentifier(typeName)) .append("()") .endLine() .beginBlock() .endBlock(); file.endLine(); // extra blank line // generate full constructor std::vector baseFields; { OUString baseTypeName = base; while (!baseTypeName.isEmpty()) { rtl::Reference baseEntity; if (m_manager->getSort(baseTypeName, &baseEntity) != codemaker::UnoType::Sort::PlainStruct) throw CannotDumpException("'" + b2u(name) + "' base type '" + baseTypeName + "' is not a plain struct"); rtl::Reference ref( dynamic_cast(baseEntity.get())); baseFields.insert(baseFields.begin(), ref->getDirectMembers().begin(), ref->getDirectMembers().end()); baseTypeName = ref->getDirectBase(); } } std::vector allFields(entity->getDirectMembers()); allFields.insert(allFields.begin(), baseFields.begin(), baseFields.end()); file.beginLine().append("public ").append(getSafeIdentifier(typeName)).append("("); separatedForeach( allFields, [&file]() { file.append(", "); }, [this, &file](const auto& member) { file.append(getNetName(member.type)).append(" ").append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginLine().extraIndent().append(": base("); separatedForeach(baseFields, [&file]() { file.append(", "); }, [&file](const auto& member) { file.append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginBlock(); for (const auto& member : entity->getDirectMembers()) { file.beginLine() .append("this.") .append(getSafeIdentifier(member.name)) .append(" = ") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); file.endLine(); // extra blank line // generate deconstructor file.beginLine().append("public void Deconstruct("); separatedForeach(allFields, [&file]() { file.append(", "); }, [this, &file](const auto& member) { file.append("out ") .append(getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginBlock(); for (const auto& member : allFields) { file.beginLine() .append(getSafeIdentifier(member.name)) .append(" = ") .append("this.") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); file.endLine(); // extra blank line // generate struct fields for (const auto& member : entity->getDirectMembers()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); file.beginLine() .append("public ") .append(getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::producePolyStruct( std::string_view name, const rtl::Reference& entity) { // produce referenced types for (const auto& member : entity->getMembers()) if (!member.parameterized) produceType(getBaseUnoName(member.type)); CSharpFile file(m_outputDir, name); // verbose and dry run checks if (m_verbose) std::cout << "[polystruct] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); // output namespace block auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); // generate struct and type parameters list file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public class ") .append(getSafeIdentifier(typeName)) .append("<"); separatedForeach(entity->getTypeParameters(), [&file]() { file.append(", "); }, [&file](const auto& param) { file.append(param); }); file.append(">").endLine().beginBlock(); // generate default constructor file.beginLine() .append("public ") .append(getSafeIdentifier(typeName)) .append("()") .endLine() .beginBlock() .endBlock(); file.endLine(); // extra blank line // generate full constructor file.beginLine().append("public ").append(getSafeIdentifier(typeName)).append("("); separatedForeach(entity->getMembers(), [&file]() { file.append(", "); }, [this, &file](const auto& member) { file.append(member.parameterized ? u2b(member.type) : getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginBlock(); for (const auto& member : entity->getMembers()) { file.beginLine() .append("this.") .append(getSafeIdentifier(member.name)) .append(" = ") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); file.endLine(); // extra blank line // generate struct fields for (const auto& member : entity->getMembers()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); file.beginLine() .append("public ") .append(member.parameterized ? u2b(member.type) : getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::produceException(std::string_view name, const rtl::Reference& entity) { // produce referenced types const auto& base = entity->getDirectBase(); if (!base.isEmpty()) produceType(getBaseUnoName(base)); for (const auto& member : entity->getDirectMembers()) produceType(getBaseUnoName(member.type)); CSharpFile file(m_outputDir, name); // verbose and dry run checks if (m_verbose) std::cout << "[exception] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); // output namespace block auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); // generate exception and base exceptions list file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public class ") .append(getSafeIdentifier(typeName)); if (!base.isEmpty()) file.append(" : ").append(getNetName(base)); file.endLine().beginBlock(); // generate default constructor file.beginLine() .append("public ") .append(getSafeIdentifier(typeName)) .append("()") .endLine() .beginBlock() .endBlock(); file.endLine(); // extra blank line // generate full constructor std::vector baseFields; { OUString baseTypeName = base; while (!baseTypeName.isEmpty()) { rtl::Reference baseEntity; if (m_manager->getSort(baseTypeName, &baseEntity) != codemaker::UnoType::Sort::Exception) throw CannotDumpException("'" + b2u(name) + "' base type '" + baseTypeName + "' is not an exception"); rtl::Reference ref( dynamic_cast(baseEntity.get())); baseFields.insert(baseFields.begin(), ref->getDirectMembers().begin(), ref->getDirectMembers().end()); baseTypeName = ref->getDirectBase(); } } std::vector allFields(entity->getDirectMembers()); allFields.insert(allFields.begin(), baseFields.begin(), baseFields.end()); file.beginLine().append("public ").append(getSafeIdentifier(typeName)).append("("); separatedForeach( allFields, [&file]() { file.append(", "); }, [this, &file](const auto& member) { file.append(getNetName(member.type)).append(" ").append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginLine().extraIndent().append(": base("); separatedForeach(baseFields, [&file]() { file.append(", "); }, [&file](const auto& member) { file.append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginBlock(); for (const auto& member : entity->getDirectMembers()) { file.beginLine() .append("this.") .append(getSafeIdentifier(member.name)) .append(" = ") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); file.endLine(); // extra blank line // generate deconstructor file.beginLine().append("public void Deconstruct("); separatedForeach(allFields, [&file]() { file.append(", "); }, [this, &file](const auto& member) { file.append("out ") .append(getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)); }); file.append(")").endLine(); file.beginBlock(); for (const auto& member : allFields) { file.beginLine() .append(getSafeIdentifier(member.name)) .append(" = ") .append("this.") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); file.endLine(); // extra blank line // generate exception fields for (const auto& member : entity->getDirectMembers()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); file.beginLine() .append("public ") .append(getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)) .append(";") .endLine(); } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::produceInterface(std::string_view name, const rtl::Reference& entity) { // produce referenced types for (const auto& base : entity->getDirectMandatoryBases()) produceType(getBaseUnoName(base.name)); for (const auto& member : entity->getDirectAttributes()) { produceType(getBaseUnoName(member.type)); for (const auto& e : member.getExceptions) produceType(getBaseUnoName(e)); for (const auto& e : member.setExceptions) produceType(getBaseUnoName(e)); } for (const auto& member : entity->getDirectMethods()) { produceType(getBaseUnoName(member.returnType)); for (const auto& e : member.exceptions) produceType(getBaseUnoName(e)); for (const auto& p : member.parameters) produceType(getBaseUnoName(p.type)); } CSharpFile file(m_outputDir, name); // verbose and dry run checks if (m_verbose) std::cout << "[interface] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); // output namespace block auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); // generate interface and base interfaces list file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public interface ") .append(getSafeIdentifier(typeName)); const auto& bases = entity->getDirectMandatoryBases(); if (!bases.empty()) { file.append(" : "); separatedForeach(bases, [&file]() { file.append(", "); }, [this, &file](const auto& b) { file.append(getNetName(b.name)); }); } file.endLine().beginBlock(); // generate interface properties for (const auto& member : entity->getDirectAttributes()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); if (member.bound) file.beginLine().append("[com.sun.star.uno.Bound]").endLine(); file.beginLine() .append(getNetName(member.type)) .append(" ") .append(getSafeIdentifier(member.name)) .endLine() .beginBlock(); if (!member.getExceptions.empty()) { file.beginLine().append("[com.sun.star.uno.Raises("); separatedForeach(member.getExceptions, [&file]() { file.append(", "); }, [this, &file](const auto& e) { file.append("typeof(").append(getNetName(e)).append(")"); }); file.append(")]").endLine(); } file.beginLine().append("get;").endLine(); if (!member.readOnly) { if (!member.setExceptions.empty()) { file.beginLine().append("[com.sun.star.uno.Raises("); separatedForeach(member.setExceptions, [&file]() { file.append(", "); }, [this, &file](const auto& e) { file.append("typeof(").append(getNetName(e)).append(")"); }); file.append(")]").endLine(); } file.beginLine().append("set;").endLine(); } file.endBlock(); } if (!entity->getDirectAttributes().empty()) file.endLine(); // extra blank line // generate interface methods for (const auto& member : entity->getDirectMethods()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); if (!member.exceptions.empty()) { file.beginLine().append("[com.sun.star.uno.Raises("); separatedForeach(member.exceptions, [&file]() { file.append(", "); }, [this, &file](const auto& e) { file.append("typeof(").append(getNetName(e)).append(")"); }); file.append(")]").endLine(); } file.beginLine() .append(getNetName(member.returnType)) .append(" ") .append(getSafeIdentifier(member.name)) .append("("); separatedForeach( member.parameters, [&file]() { file.append(", "); }, [this, &file](const auto& p) { using Dir = unoidl::InterfaceTypeEntity::Method::Parameter::Direction; switch (p.direction) { case Dir::DIRECTION_IN: break; case Dir::DIRECTION_OUT: file.append("out "); break; case Dir::DIRECTION_IN_OUT: file.append("ref "); break; } file.append(getNetName(p.type)).append(" ").append(getSafeIdentifier(p.name)); }); file.append(");").endLine(); } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::produceTypedef(std::string_view name, const rtl::Reference& entity) { OString type = u2b(entity->getType()); produceType(getBaseUnoName(type)); m_typedefs.emplace(name, type); if (m_verbose) std::cout << "[typedef] " << name << " = " << type << '\n'; } void NetProducer::produceConstantGroup(std::string_view name, const rtl::Reference& entity) { CSharpFile file(m_outputDir, name); if (m_verbose) std::cout << "[constants] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public static class ") .append(getSafeIdentifier(typeName)) .endLine() .beginBlock(); for (const auto& member : entity->getMembers()) { for (const auto& anno : member.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); OString type, value; switch (member.value.type) { case unoidl::ConstantValue::TYPE_BOOLEAN: type = "bool"_ostr; value = member.value.booleanValue ? "true"_ostr : "false"_ostr; break; case unoidl::ConstantValue::TYPE_BYTE: type = "sbyte"_ostr; value = OString::number(member.value.byteValue); break; case unoidl::ConstantValue::TYPE_SHORT: type = "short"_ostr; value = OString::number(member.value.shortValue); break; case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT: type = "ushort"_ostr; value = OString::number(member.value.unsignedShortValue); break; case unoidl::ConstantValue::TYPE_LONG: type = "int"_ostr; value = OString::number(member.value.longValue); break; case unoidl::ConstantValue::TYPE_UNSIGNED_LONG: type = "uint"_ostr; value = OString::number(member.value.unsignedLongValue); break; case unoidl::ConstantValue::TYPE_HYPER: type = "long"_ostr; value = OString::number(member.value.hyperValue); break; case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER: type = "ulong"_ostr; value = OString::number(member.value.unsignedHyperValue); break; case unoidl::ConstantValue::TYPE_FLOAT: type = "float"_ostr; value = OString::number(member.value.floatValue) + "f"; break; case unoidl::ConstantValue::TYPE_DOUBLE: type = "double"_ostr; value = OString::number(member.value.doubleValue); break; } file.beginLine() .append("public const ") .append(type) .append(" ") .append(getSafeIdentifier(member.name)) .append(" = ") .append(value) .append(";") .endLine(); } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::produceService( std::string_view name, const rtl::Reference& entity) { CSharpFile file(m_outputDir, name); if (m_verbose) std::cout << "[service] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public static class ") .append(getSafeIdentifier(typeName)) .endLine() .beginBlock(); for (const auto& ctor : entity->getConstructors()) { for (const auto& anno : ctor.annotations) if (anno == "deprecated") file.beginLine().append("[System.Obsolete]").endLine(); std::vector exceptions(ctor.exceptions); exceptions.emplace(exceptions.begin(), "com.sun.star.uno.DeploymentException"); file.beginLine().append("[com.sun.star.uno.Raises("); separatedForeach(exceptions, [&file]() { file.append(", "); }, [this, &file](const auto& e) { file.append("typeof(").append(getNetName(e)).append(")"); }); file.append(")]").endLine(); if (ctor.defaultConstructor) { const auto& returnType(getNetName(entity->getBase())); file.beginLine() .append("public static ") .append(returnType) .append(" create(com.sun.star.uno.XComponentContext ctx)") .endLine() .beginBlock(); file.beginLine() .append("try") .endLine() .beginBlock() .beginLine() .append("com.sun.star.lang.XMultiComponentFactory mcf = ") .append("ctx.getServiceManager();") .endLine() .beginLine() .append("com.sun.star.uno.Any srv = new com.sun.star.uno.Any(") .append("mcf.createInstanceWithContext(\"") .append(name) .append("\", ctx));") .endLine() .beginLine() .append("return srv.cast<") .append(returnType) .append(">();") .beginLine() .endLine() .endBlock(); for (const auto& e : ctor.exceptions) { file.beginLine() .append("catch (") .append(e) .append(")") .endLine() .beginBlock() .beginLine() .append("throw;") .endLine() .endBlock(); } file.beginLine() .append("catch") .endLine() .beginBlock() .beginLine() .append("throw new com.sun.star.uno.DeploymentException(") .append("\"Could not create service ") .append(name) .append(" from given XComponentContext\", ctx);") .endLine() .endBlock(); file.endBlock(); } else { const auto& returnType(getNetName(entity->getBase())); const auto* restParam = !ctor.parameters.empty() && ctor.parameters.front().rest ? &ctor.parameters.front() : nullptr; file.beginLine() .append("public static ") .append(returnType) .append(" ") .append(getSafeIdentifier(ctor.name)) .append("(com.sun.star.uno.XComponentContext ctx"); if (!ctor.parameters.empty()) file.append(", "); separatedForeach( ctor.parameters, [&file]() { file.append(", "); }, [this, &file](const auto& p) { file.append(getNetName(p.type)).append(" ").append(getSafeIdentifier(p.name)); }); file.append(")").endLine().beginBlock(); file.beginLine() .append("try") .endLine() .beginBlock() .beginLine() .append("com.sun.star.lang.XMultiComponentFactory mcf = ") .append("ctx.getServiceManager();") .endLine() .beginLine() .append("com.sun.star.uno.Any srv = new com.sun.star.uno.Any(") .append("mcf.createInstanceWithArgumentsAndContext(\"") .append(name) .append("\", "); if (restParam) { file.append(getSafeIdentifier(ctor.parameters.front().name)); } else if (ctor.parameters.empty()) { file.append("System.Array.Empty()"); } else { file.append("new com.sun.star.uno.Any[] { "); separatedForeach(ctor.parameters, [&file]() { file.append(", "); }, [&file](const auto& p) { file.append("new com.sun.star.uno.Any(") .append(getSafeIdentifier(p.name)) .append(")"); }); file.append(" }"); } file.append(", ctx));") .endLine() .beginLine() .append("return srv.cast<") .append(returnType) .append(">();") .endLine() .endBlock(); for (const auto& e : ctor.exceptions) { file.beginLine() .append("catch (") .append(getNetName(e)) .append(")") .endLine() .beginBlock() .beginLine() .append("throw;") .endLine() .endBlock(); } file.beginLine() .append("catch") .endLine() .beginBlock() .beginLine() .append("throw new com.sun.star.uno.DeploymentException(") .append("\"Could not create service ") .append(name) .append(" from given XComponentContext\", ctx);") .endLine() .endBlock(); file.endBlock(); } } file.endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } void NetProducer::produceSingleton( std::string_view name, const rtl::Reference& entity) { CSharpFile file(m_outputDir, name); if (m_verbose) std::cout << "[singleton] " << name << " -> " << file.getPath() << '\n'; if (m_dryRun) return; file.openFile(); auto[hasNamespace, namespaceName, typeName] = splitName(name); if (hasNamespace) file.beginLine().append("namespace ").append(namespaceName).endLine().beginBlock(); file.beginLine() .append("[com.sun.star.uno.UnoGenerated]") .endLine() .beginLine() .append("public static class ") .append(getSafeIdentifier(typeName)) .endLine() .beginBlock(); file.beginLine() .append("[com.sun.star.uno.Raises(typeof(com.sun.star.uno.DeploymentException))]") .endLine(); file.beginLine() .append("public static ") .append(getNetName(entity->getBase())) .append(" get(com.sun.star.uno.XComponentContext ctx)") .endLine() .beginBlock(); file.beginLine() .append("try") .endLine() .beginBlock() .beginLine() .append("com.sun.star.uno.Any sgtn = ctx.getValueByName(\"/singletons/") .append(name) .append("\");") .endLine() .beginLine() .append("return sgtn.cast<") .append(getNetName(entity->getBase())) .append(">();") .endLine() .endBlock(); file.beginLine() .append("catch") .endLine() .beginBlock() .beginLine() .append("throw new com.sun.star.uno.DeploymentException(\"Could not get singleton ") .append(name) .append(" from given XComponentContext\", ctx);") .endLine() .endBlock(); file.endBlock().endBlock(); if (hasNamespace) file.endBlock(); file.closeFile(); } OString NetProducer::getNetName(std::string_view name) { OString fullName(name); OStringBuffer buffer; while (true) { OString baseName = getBaseUnoName(fullName); if (m_typedefs.contains(baseName)) fullName = fullName.replaceFirst(baseName, m_typedefs.at(baseName)); else break; } std::string_view fullNameView(fullName); // if sequence, count dimensions int dimensions = 0; while (fullNameView.starts_with("[]")) { ++dimensions; fullNameView = fullNameView.substr(2); } // if polymorphic, process parameters too if (fullNameView.ends_with('>')) { const std::string_view::size_type nFirstAngle = fullNameView.find_first_of('<'); size_t start = (nFirstAngle != std::string_view::npos) ? (nFirstAngle + 1) : 0; size_t end = fullNameView.size() - 1; buffer.append(fullNameView.substr(0, start)); OString params(fullNameView.substr(start, end - start)); bool first = true; for (start = 0; start != std::string_view::npos;) { std::string_view param(o3tl::getToken(params, ',', start)); if (first) first = false; else buffer.append(", "); buffer.append(getNetName(param)); } buffer.append(">"); } else { // assumes basetypes are not polymorphic for a tiny optimization // if this is changed later, move this part out of the else block fullName = OString(fullNameView); OString baseName = getBaseUnoName(fullName); if (s_baseTypes.contains(baseName)) buffer.append(fullName.replaceFirst(baseName, s_baseTypes.at(baseName))); else buffer.append(fullName); } // if sequence, add [] to make array while (dimensions--) buffer.append("[]"); return buffer.makeStringAndClear(); } OString NetProducer::getNetName(std::u16string_view name) { return getNetName(u2b(name)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */