diff options
author | RMZeroFour <ritobroto04@gmail.com> | 2024-06-12 13:56:05 +0530 |
---|---|---|
committer | Hossein <hossein@libreoffice.org> | 2024-06-19 09:34:45 +0200 |
commit | e597e712b6e1c199a6241df3c29f35baeef9f457 (patch) | |
tree | f4517de769cd6a8e8d1b47ce87caf378122f7f83 /codemaker/source/netmaker/netproduce.cxx | |
parent | 64365dfa67d5a1d8fbc710238a4ea9c492645de4 (diff) |
.NET Bindings: Add netmaker (.NET codemaker)
This commit adds the netmaker executable to the codemaker/ module, to
generate C# code from UNOIDL specifications.
Also adds some Makefiles in the net_ure/ directory to generate code for
udkapi and offapi, to build the net_uretypes and net_oootypes assemblies.
Change-Id: Ifb61fe6a0f8f594eaa6ff95b025ba57f247b0d4b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168710
Tested-by: Jenkins
Tested-by: Hossein <hossein@libreoffice.org>
Reviewed-by: Hossein <hossein@libreoffice.org>
Diffstat (limited to 'codemaker/source/netmaker/netproduce.cxx')
-rw-r--r-- | codemaker/source/netmaker/netproduce.cxx | 1182 |
1 files changed, 1182 insertions, 0 deletions
diff --git a/codemaker/source/netmaker/netproduce.cxx b/codemaker/source/netmaker/netproduce.cxx new file mode 100644 index 000000000000..f2ff615a7803 --- /dev/null +++ b/codemaker/source/netmaker/netproduce.cxx @@ -0,0 +1,1182 @@ +/* -*- 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 <iostream> +#include <vector> + +#include <codemaker/codemaker.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/strbuf.hxx> +#include <unoidl/unoidl.hxx> + +#include "netproduce.hxx" +#include "csharpfile.hxx" + +namespace +{ +const std::unordered_set<OString> 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<OString, OString> 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.Exception"_ostr }, + { "com.sun.star.uno.XInterface"_ostr, "com.sun.star.uno.IQueryInterface"_ostr }, +}; + +std::tuple<bool, std::string_view, std::string_view> 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<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> 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<unoidl::EnumTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::PlainStruct: + producePlainStruct(name, + dynamic_cast<unoidl::PlainStructTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::PolymorphicStructTemplate: + producePolyStruct( + name, dynamic_cast<unoidl::PolymorphicStructTypeTemplateEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::Exception: + produceException(name, dynamic_cast<unoidl::ExceptionTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::Interface: + produceInterface(name, dynamic_cast<unoidl::InterfaceTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::Typedef: + produceTypedef(name, dynamic_cast<unoidl::TypedefEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::ConstantGroup: + produceConstantGroup(name, + dynamic_cast<unoidl::ConstantGroupEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::SingleInterfaceBasedService: + produceService( + name, dynamic_cast<unoidl::SingleInterfaceBasedServiceEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::InterfaceBasedSingleton: + produceSingleton( + name, dynamic_cast<unoidl::InterfaceBasedSingletonEntity*>(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<unoidl::TypedefEntity*>(entity.get())); + break; + + default: + break; + } + } +} + +void NetProducer::produceModule(std::string_view name, + const rtl::Reference<unoidl::MapCursor>& 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<unoidl::EnumTypeEntity>& 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<unoidl::PlainStructTypeEntity>& 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<unoidl::PlainStructTypeEntity::Member> baseFields; + { + OUString baseTypeName = base; + while (!baseTypeName.isEmpty()) + { + rtl::Reference<unoidl::Entity> 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<unoidl::PlainStructTypeEntity> ref( + dynamic_cast<unoidl::PlainStructTypeEntity*>(baseEntity.get())); + for (const auto& member : ref->getDirectMembers()) + baseFields.emplace_back(member); + + baseTypeName = ref->getDirectBase(); + } + } + + std::vector<unoidl::PlainStructTypeEntity::Member> allFields(entity->getDirectMembers()); + allFields.insert(allFields.end(), 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 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(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<unoidl::PolymorphicStructTypeTemplateEntity>& 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(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<unoidl::ExceptionTypeEntity>& 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<unoidl::ExceptionTypeEntity::Member> baseFields; + { + OUString baseTypeName = base; + while (!baseTypeName.isEmpty()) + { + rtl::Reference<unoidl::Entity> baseEntity; + if (m_manager->getSort(baseTypeName, &baseEntity) + != codemaker::UnoType::Sort::Exception) + throw CannotDumpException("'" + b2u(name) + "' base type '" + baseTypeName + + "' is not an exception"); + + rtl::Reference<unoidl::ExceptionTypeEntity> ref( + dynamic_cast<unoidl::ExceptionTypeEntity*>(baseEntity.get())); + for (const auto& member : ref->getDirectMembers()) + baseFields.emplace_back(member); + + baseTypeName = ref->getDirectBase(); + } + } + + std::vector<unoidl::ExceptionTypeEntity::Member> allFields(entity->getDirectMembers()); + allFields.insert(allFields.end(), 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 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(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<unoidl::InterfaceTypeEntity>& 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: + file.append("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<unoidl::TypedefEntity>& 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<unoidl::ConstantGroupEntity>& 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); + 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<unoidl::SingleInterfaceBasedServiceEntity>& 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<OUString> 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(in 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(returnType) + .append(" srv = (") + .append(returnType) + .append(")mcf.createInstanceWithContext(\"") + .append(returnType) + .append("\", ctx);") + .endLine() + .beginLine() + .append("return srv;") + .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(\"Could not create service ") + .append(returnType) + .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("(in 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(returnType) + .append(" srv = (") + .append(returnType) + .append(")mcf.createInstanceWithArgumentsAndContext(\"") + .append(returnType) + .append("\", "); + if (restParam) + { + file.append(getSafeIdentifier(ctor.parameters.front().name)); + } + else if (ctor.parameters.empty()) + { + file.append("System.Array.Empty<com.sun.star.uno.Any>()"); + } + else + { + file.append("new com.sun.star.uno.Any[] { "); + separatedForeach(ctor.parameters, [&file]() { file.append(", "); }, + [&file](const auto& p) { + file.append("com.sun.star.uno.Any.with(") + .append(getSafeIdentifier(p.name)) + .append(")"); + }); + file.append(" }"); + } + file.append(", ctx);").endLine().beginLine().append("return srv;").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(\"Could not create service ") + .append(returnType) + .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<unoidl::InterfaceBasedSingletonEntity>& 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(in com.sun.star.uno.XComponentContext ctx)") + .endLine() + .beginBlock(); + + file.beginLine() + .append("com.sun.star.uno.Any sgtn = ctx.getValueByName(\"/singletons/") + .append(name) + .append("\");") + .endLine(); + file.beginLine() + .append("if (!sgtn.hasValue())") + .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.beginLine() + .append("return (") + .append(getNetName(entity->getBase())) + .append(")sgtn.Value;") + .endLine(); + + 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('>')) + { + size_t start = fullNameView.find_first_of('<') + 1; + 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 seqeunce, 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: */ |