/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * 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 #include #include #include #include #include "legacyprovider.hxx" namespace unoidl::detail { namespace { std::vector< OUString > translateAnnotations(std::u16string_view documentation) { std::vector< OUString > ans; if (documentation.find(u"@deprecated") != std::u16string_view::npos) { //TODO: this check is somewhat crude ans.push_back("deprecated"); } return ans; } ConstantValue translateConstantValue( RegistryKey & key, RTConstValue const & value) { switch (value.m_type) { case RT_TYPE_BOOL: return ConstantValue(value.m_value.aBool); case RT_TYPE_BYTE: return ConstantValue(value.m_value.aByte); case RT_TYPE_INT16: return ConstantValue(value.m_value.aShort); case RT_TYPE_UINT16: return ConstantValue(value.m_value.aUShort); case RT_TYPE_INT32: return ConstantValue(value.m_value.aLong); case RT_TYPE_UINT32: return ConstantValue(value.m_value.aULong); case RT_TYPE_INT64: return ConstantValue(value.m_value.aHyper); case RT_TYPE_UINT64: return ConstantValue(value.m_value.aUHyper); case RT_TYPE_FLOAT: return ConstantValue(value.m_value.aFloat); case RT_TYPE_DOUBLE: return ConstantValue(value.m_value.aDouble); default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected type " + OUString::number(value.m_type) + " of value of a field of constant group with key " + key.getName())); } } rtl::Reference< Entity > readEntity( rtl::Reference< Manager > const & manager, RegistryKey & ucr, RegistryKey & key, OUString const & path, bool probe); class Cursor: public MapCursor { public: Cursor( rtl::Reference< Manager > manager, RegistryKey const & ucr, RegistryKey const & key); private: virtual ~Cursor() noexcept override {} virtual rtl::Reference< Entity > getNext(OUString * name) override; rtl::Reference< Manager > manager_; RegistryKey ucr_; RegistryKey key_; OUString prefix_; RegistryKeyNames names_; sal_uInt32 index_; }; Cursor::Cursor( rtl::Reference< Manager > manager, RegistryKey const & ucr, RegistryKey const & key): manager_(std::move(manager)), ucr_(ucr), key_(key), index_(0) { if (!ucr_.isValid()) return; prefix_ = key_.getName(); if (!prefix_.endsWith("/")) { prefix_ += "/"; } RegError e = key_.getKeyNames("", names_); if (e != RegError::NO_ERROR) { throw FileFormatException( key_.getRegistryName(), ("legacy format: cannot get sub-key names of " + key_.getName() + ": " + OUString::number(static_cast(e)))); } } rtl::Reference< Entity > Cursor::getNext(OUString * name) { assert(name != nullptr); rtl::Reference< Entity > ent; if (index_ != names_.getLength()) { OUString path(names_.getElement(index_)); assert(path.match(prefix_)); *name = path.copy(prefix_.getLength()); ent = readEntity(manager_, ucr_, key_, *name, false); assert(ent.is()); ++index_; } return ent; } class Module: public ModuleEntity { public: Module( rtl::Reference< Manager > manager, RegistryKey const & ucr, RegistryKey const & key): manager_(std::move(manager)), ucr_(ucr), key_(key) {} private: virtual ~Module() noexcept override {} virtual std::vector< OUString > getMemberNames() const override; virtual rtl::Reference< MapCursor > createCursor() const override { return new Cursor(manager_, ucr_, key_); } rtl::Reference< Manager > manager_; RegistryKey ucr_; mutable RegistryKey key_; }; std::vector< OUString > Module::getMemberNames() const { RegistryKeyNames names; RegError e = key_.getKeyNames("", names); if (e != RegError::NO_ERROR) { throw FileFormatException( key_.getRegistryName(), ("legacy format: cannot get sub-key names of " + key_.getName() + ": " + OUString::number(static_cast(e)))); } std::vector< OUString > ns; for (sal_uInt32 i = 0; i != names.getLength(); ++i) { ns.push_back(names.getElement(i)); } return ns; } typereg::Reader getReader(RegistryKey & key, std::vector< char > * buffer) { assert(buffer != nullptr); RegValueType type; sal_uInt32 size; RegError e = key.getValueInfo("", &type, &size); if (e != RegError::NO_ERROR) { throw FileFormatException( key.getRegistryName(), ("legacy format: cannot get value info about key " + key.getName() + ": " + OUString::number(static_cast(e)))); } if (type != RegValueType::BINARY) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected value type " + OUString::number(static_cast(type)) + " of key " + key.getName())); } if (size == 0 /*TODO: || size > std::numeric_limits< std::vector< char >::size_type >::max() */) { throw FileFormatException( key.getRegistryName(), ("legacy format: bad binary value size " + OUString::number(size) + " of key " + key.getName())); } buffer->resize(static_cast< std::vector< char >::size_type >(size)); e = key.getValue("", buffer->data()); if (e != RegError::NO_ERROR) { throw FileFormatException( key.getRegistryName(), ("legacy format: cannot get binary value of key " + key.getName() + ": " + OUString::number(static_cast(e)))); } typereg::Reader reader(buffer->data(), size); if (!reader.isValid()) { throw FileFormatException( key.getRegistryName(), "legacy format: malformed binary value of key " + key.getName()); } return reader; } rtl::Reference< Entity > readEntity( rtl::Reference< Manager > const & manager, RegistryKey & ucr, RegistryKey & key, OUString const & path, bool probe) { assert(manager.is()); RegistryKey sub; RegError e = key.openKey(path, sub); switch (e) { case RegError::NO_ERROR: break; case RegError::KEY_NOT_EXISTS: if (probe) { return rtl::Reference< Entity >(); } [[fallthrough]]; default: throw FileFormatException( key.getRegistryName(), ("legacy format: cannot open sub-key " + path + " of " + key.getName() + ": " + OUString::number(static_cast(e)))); } std::vector< char > buf; typereg::Reader reader(getReader(sub, &buf)); switch (reader.getTypeClass()) { case RT_TYPE_INTERFACE: { std::vector< AnnotatedReference > mandBases; sal_uInt16 n = reader.getSuperTypeCount(); for (sal_uInt16 j = 0; j != n; ++j) { mandBases.emplace_back( reader.getSuperTypeName(j).replace('/', '.'), std::vector< OUString >()); } std::vector< AnnotatedReference > optBases; n = reader.getReferenceCount(); for (sal_uInt16 j = 0; j != n; ++j) { optBases.emplace_back( reader.getReferenceTypeName(j).replace('/', '.'), translateAnnotations(reader.getReferenceDocumentation(j))); } sal_uInt16 methodCount = reader.getMethodCount(); std::vector< InterfaceTypeEntity::Attribute > attrs; n = reader.getFieldCount(); // attributes for (sal_uInt16 j = 0; j != n; ++j) { OUString attrName(reader.getFieldName(j)); std::vector< OUString > getExcs; std::vector< OUString > setExcs; for (sal_uInt16 k = 0; k != methodCount; ++k) { if (reader.getMethodName(k) == attrName) { switch (reader.getMethodFlags(k)) { case RTMethodMode::ATTRIBUTE_GET: { sal_uInt16 m = reader.getMethodExceptionCount(k); // cid#1213376 unhelpfully warns about an // untrusted loop bound here: // coverity[tainted_data] - trusted data source for (sal_uInt16 l = 0; l != m; ++l) { getExcs.push_back( reader.getMethodExceptionTypeName(k, l). replace('/', '.')); } break; } case RTMethodMode::ATTRIBUTE_SET: { sal_uInt16 m = reader.getMethodExceptionCount(k); // cid#1213376 unhelpfully warns about an // untrusted loop bound here: // coverity[tainted_data] - trusted data source for (sal_uInt16 l = 0; l != m; ++l) { setExcs.push_back( reader.getMethodExceptionTypeName(k, l). replace('/', '.')); } break; } default: throw FileFormatException( key.getRegistryName(), ("legacy format: method and attribute with same" " name " + attrName + " in interface type with key " + sub.getName())); } } } RTFieldAccess flags = reader.getFieldFlags(j); attrs.emplace_back( attrName, reader.getFieldTypeName(j).replace('/', '.'), bool(flags & RTFieldAccess::BOUND), bool(flags & RTFieldAccess::READONLY), std::move(getExcs), std::move(setExcs), translateAnnotations(reader.getFieldDocumentation(j))); } std::vector< InterfaceTypeEntity::Method > meths; meths.reserve(methodCount); for (sal_uInt16 j = 0; j != methodCount; ++j) { RTMethodMode flags = reader.getMethodFlags(j); if (flags != RTMethodMode::ATTRIBUTE_GET && flags != RTMethodMode::ATTRIBUTE_SET) { std::vector< InterfaceTypeEntity::Method::Parameter > params; sal_uInt16 m = reader.getMethodParameterCount(j); // cid#1213376 unhelpfully warns about an untrusted loop // bound here: // coverity[tainted_data] - trusted data source for (sal_uInt16 k = 0; k != m; ++k) { RTParamMode mode = reader.getMethodParameterFlags(j, k); InterfaceTypeEntity::Method::Parameter::Direction dir; switch (mode) { case RT_PARAM_IN: dir = InterfaceTypeEntity::Method::Parameter::DIRECTION_IN; break; case RT_PARAM_OUT: dir = InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT; break; case RT_PARAM_INOUT: dir = InterfaceTypeEntity::Method::Parameter::DIRECTION_IN_OUT; break; default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected mode " + OUString::number(mode) + " of parameter " + reader.getMethodParameterName(j, k) + " of method " + reader.getMethodName(j) + " in interface type with key " + sub.getName())); } params.emplace_back( reader.getMethodParameterName(j, k), (reader.getMethodParameterTypeName(j, k). replace('/', '.')), dir); } std::vector< OUString > excs; m = reader.getMethodExceptionCount(j); // cid#1213376 unhelpfully warns about an untrusted loop // bound here: // coverity[tainted_data] - trusted data source for (sal_uInt16 k = 0; k != m; ++k) { excs.push_back( reader.getMethodExceptionTypeName(j, k).replace( '/', '.')); } meths.emplace_back( reader.getMethodName(j), reader.getMethodReturnTypeName(j).replace('/', '.'), std::move(params), std::move(excs), translateAnnotations( reader.getMethodDocumentation(j))); } } return new InterfaceTypeEntity( reader.isPublished(), std::move(mandBases), std::move(optBases), std::move(attrs), std::move(meths), translateAnnotations(reader.getDocumentation())); } case RT_TYPE_MODULE: return new Module(manager, ucr, sub); case RT_TYPE_STRUCT: { sal_uInt16 n = reader.getReferenceCount(); if (n == 0) { OUString base; switch (reader.getSuperTypeCount()) { case 0: break; case 1: base = reader.getSuperTypeName(0).replace('/', '.'); break; default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected number " + OUString::number(reader.getSuperTypeCount()) + " of super-types of plain struct type with key " + sub.getName())); } std::vector< PlainStructTypeEntity::Member > mems; n = reader.getFieldCount(); for (sal_uInt16 j = 0; j != n; ++j) { mems.emplace_back( reader.getFieldName(j), reader.getFieldTypeName(j).replace('/', '.'), translateAnnotations(reader.getFieldDocumentation(j))); } return new PlainStructTypeEntity( reader.isPublished(), base, std::move(mems), translateAnnotations(reader.getDocumentation())); } else { if (reader.getSuperTypeCount() != 0) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected number " + OUString::number(reader.getSuperTypeCount()) + " of super-types of polymorphic struct type template" " with key " + sub.getName())); } std::vector< OUString > params; for (sal_uInt16 j = 0; j != n; ++j) { params.push_back( reader.getReferenceTypeName(j).replace('/', '.')); } std::vector< PolymorphicStructTypeTemplateEntity::Member > mems; n = reader.getFieldCount(); for (sal_uInt16 j = 0; j != n; ++j) { mems.emplace_back( reader.getFieldName(j), reader.getFieldTypeName(j).replace('/', '.'), bool( reader.getFieldFlags(j) & RTFieldAccess::PARAMETERIZED_TYPE), translateAnnotations(reader.getFieldDocumentation(j))); } return new PolymorphicStructTypeTemplateEntity( reader.isPublished(), std::move(params), std::move(mems), translateAnnotations(reader.getDocumentation())); } } case RT_TYPE_ENUM: { std::vector< EnumTypeEntity::Member > mems; sal_uInt16 n = reader.getFieldCount(); for (sal_uInt16 j = 0; j != n; ++j) { RTConstValue v(reader.getFieldValue(j)); if (v.m_type != RT_TYPE_INT32) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected type " + OUString::number(v.m_type) + " of value of field " + reader.getFieldName(j) + " of enum type with key " + sub.getName())); } mems.emplace_back( reader.getFieldName(j), v.m_value.aLong, translateAnnotations(reader.getFieldDocumentation(j))); } return new EnumTypeEntity( reader.isPublished(), std::move(mems), translateAnnotations(reader.getDocumentation())); } case RT_TYPE_EXCEPTION: { OUString base; switch (reader.getSuperTypeCount()) { case 0: break; case 1: base = reader.getSuperTypeName(0).replace('/', '.'); break; default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected number " + OUString::number(reader.getSuperTypeCount()) + " of super-types of exception type with key " + sub.getName())); } std::vector< ExceptionTypeEntity::Member > mems; sal_uInt16 n = reader.getFieldCount(); for (sal_uInt16 j = 0; j != n; ++j) { mems.emplace_back( reader.getFieldName(j), reader.getFieldTypeName(j).replace('/', '.'), translateAnnotations(reader.getFieldDocumentation(j))); } return new ExceptionTypeEntity( reader.isPublished(), base, std::move(mems), translateAnnotations(reader.getDocumentation())); } case RT_TYPE_TYPEDEF: if (reader.getSuperTypeCount() != 1) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected number " + OUString::number(reader.getSuperTypeCount()) + " of super-types of typedef with key " + sub.getName())); } return new TypedefEntity( reader.isPublished(), reader.getSuperTypeName(0).replace('/', '.'), translateAnnotations(reader.getDocumentation())); case RT_TYPE_SERVICE: switch (reader.getSuperTypeCount()) { case 0: { std::vector< AnnotatedReference > mandServs; std::vector< AnnotatedReference > optServs; std::vector< AnnotatedReference > mandIfcs; std::vector< AnnotatedReference > optIfcs; sal_uInt16 n = reader.getReferenceCount(); for (sal_uInt16 j = 0; j != n; ++j) { AnnotatedReference base{ reader.getReferenceTypeName(j).replace('/', '.'), translateAnnotations( reader.getReferenceDocumentation(j))}; switch (reader.getReferenceSort(j)) { case RTReferenceType::EXPORTS: if (!(reader.getReferenceFlags(j) & RTFieldAccess::OPTIONAL)) { mandServs.push_back(base); } else { optServs.push_back(base); } break; case RTReferenceType::SUPPORTS: if (!(reader.getReferenceFlags(j) & RTFieldAccess::OPTIONAL)) { mandIfcs.push_back(base); } else { optIfcs.push_back(base); } break; default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected mode " + OUString::number(static_cast(reader.getReferenceSort(j))) + " of reference " + reader.getReferenceTypeName(j) + " in service with key " + sub.getName())); } } std::vector< AccumulationBasedServiceEntity::Property > props; n = reader.getFieldCount(); for (sal_uInt16 j = 0; j != n; ++j) { RTFieldAccess acc = reader.getFieldFlags(j); int attrs = 0; if (acc & RTFieldAccess::READONLY) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_READ_ONLY; } if (acc & RTFieldAccess::OPTIONAL) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_OPTIONAL; } if (acc & RTFieldAccess::MAYBEVOID) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_MAYBE_VOID; } if (acc & RTFieldAccess::BOUND) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_BOUND; } if (acc & RTFieldAccess::CONSTRAINED) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_CONSTRAINED; } if (acc & RTFieldAccess::TRANSIENT) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_TRANSIENT; } if (acc & RTFieldAccess::MAYBEAMBIGUOUS) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_MAYBE_AMBIGUOUS; } if (acc & RTFieldAccess::MAYBEDEFAULT) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_MAYBE_DEFAULT; } if (acc & RTFieldAccess::REMOVABLE) { attrs |= AccumulationBasedServiceEntity::Property:: ATTRIBUTE_REMOVABLE; } props.emplace_back( reader.getFieldName(j), reader.getFieldTypeName(j).replace('/', '.'), static_cast< AccumulationBasedServiceEntity::Property:: Attributes >(attrs), translateAnnotations(reader.getFieldDocumentation(j))); } return new AccumulationBasedServiceEntity( reader.isPublished(), std::move(mandServs), std::move(optServs), std::move(mandIfcs), std::move(optIfcs), std::move(props), translateAnnotations(reader.getDocumentation())); } case 1: { std::vector< SingleInterfaceBasedServiceEntity::Constructor > ctors; sal_uInt16 n = reader.getMethodCount(); if (n == 1 && reader.getMethodFlags(0) == RTMethodMode::TWOWAY && reader.getMethodName(0).isEmpty() && reader.getMethodReturnTypeName(0) == "void" && reader.getMethodParameterCount(0) == 0 && reader.getMethodExceptionCount(0) == 0) { ctors.push_back( SingleInterfaceBasedServiceEntity::Constructor()); } else { for (sal_uInt16 j = 0; j != n; ++j) { if (reader.getMethodFlags(j) != RTMethodMode::TWOWAY) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected mode " + OUString::number(static_cast(reader.getMethodFlags(j))) + " of constructor " + reader.getMethodName(j) + " in service with key " + sub.getName())); } std::vector< SingleInterfaceBasedServiceEntity::Constructor:: Parameter > params; sal_uInt16 m = reader.getMethodParameterCount(j); // cid#1213376 unhelpfully warns about an untrusted // loop bound here: // coverity[tainted_data] - trusted data source for (sal_uInt16 k = 0; k != m; ++k) { RTParamMode mode = reader.getMethodParameterFlags(j, k); if ((mode & ~RT_PARAM_REST) != RT_PARAM_IN) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected mode " + OUString::number(mode) + " of parameter " + reader.getMethodParameterName(j, k) + " of constructor " + reader.getMethodName(j) + " in service with key " + sub.getName())); } if ((mode & RT_PARAM_REST) != 0 && (m != 1 || ((reader.getMethodParameterTypeName( j, 0)) != "any"))) { throw FileFormatException( key.getRegistryName(), ("legacy format: bad rest parameter " + reader.getMethodParameterName(j, k) + " of constructor " + reader.getMethodName(j) + " in service with key " + sub.getName())); } params.emplace_back( reader.getMethodParameterName(j, k), (reader.getMethodParameterTypeName(j, k). replace('/', '.')), (mode & RT_PARAM_REST) != 0); } std::vector< OUString > excs; m = reader.getMethodExceptionCount(j); // cid#1213376 unhelpfully warns about an untrusted // loop bound here: // coverity[tainted_data] - trusted data source for (sal_uInt16 k = 0; k != m; ++k) { excs.push_back( reader.getMethodExceptionTypeName(j, k).replace( '/', '.')); } ctors.push_back( SingleInterfaceBasedServiceEntity::Constructor( reader.getMethodName(j), std::move(params), std::move(excs), translateAnnotations( reader.getMethodDocumentation(j)))); } } return new SingleInterfaceBasedServiceEntity( reader.isPublished(), reader.getSuperTypeName(0).replace('/', '.'), std::move(ctors), translateAnnotations(reader.getDocumentation())); } default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected number " + OUString::number(reader.getSuperTypeCount()) + " of super-types of service with key " + sub.getName())); } case RT_TYPE_SINGLETON: { if (reader.getSuperTypeCount() != 1) { throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected number " + OUString::number(reader.getSuperTypeCount()) + " of super-types of singleton with key " + sub.getName())); } OUString basePath(reader.getSuperTypeName(0)); OUString baseName(basePath.replace('/', '.')); bool newStyle; rtl::Reference< Entity > base(manager->findEntity(baseName)); if (base.is()) { switch (base->getSort()) { case Entity::SORT_INTERFACE_TYPE: newStyle = true; break; case Entity::SORT_ACCUMULATION_BASED_SERVICE: newStyle = false; break; default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected sort " + OUString::number(base->getSort()) + " of base " + baseName + " of singleton with key " + sub.getName())); } } else { RegistryKey key2; e = ucr.openKey(basePath, key2); switch (e) { case RegError::NO_ERROR: break; case RegError::KEY_NOT_EXISTS: throw FileFormatException( key.getRegistryName(), ("legacy format: unknown super-type " + basePath + " of super-type with key " + sub.getName())); default: throw FileFormatException( key.getRegistryName(), ("legacy format: cannot open ucr sub-key " + basePath + ": " + OUString::number(static_cast(e)))); } std::vector< char > buf2; typereg::Reader reader2(getReader(key2, &buf2)); switch (reader2.getTypeClass()) { case RT_TYPE_INTERFACE: newStyle = true; break; case RT_TYPE_SERVICE: newStyle = false; break; default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected type class " + OUString::number(reader2.getTypeClass()) + " of super-type with key " + key2.getName() + " of singleton with key " + sub.getName())); } } return newStyle ? rtl::Reference< Entity >( new InterfaceBasedSingletonEntity( reader.isPublished(), baseName, translateAnnotations(reader.getDocumentation()))) : rtl::Reference< Entity >( new ServiceBasedSingletonEntity( reader.isPublished(), baseName, translateAnnotations(reader.getDocumentation()))); } case RT_TYPE_CONSTANTS: { std::vector< ConstantGroupEntity::Member > mems; sal_uInt16 n = reader.getFieldCount(); for (sal_uInt16 j = 0; j != n; ++j) { mems.emplace_back( reader.getFieldName(j), translateConstantValue(sub, reader.getFieldValue(j)), translateAnnotations(reader.getFieldDocumentation(j))); } return new ConstantGroupEntity( reader.isPublished(), std::move(mems), translateAnnotations(reader.getDocumentation())); } default: throw FileFormatException( key.getRegistryName(), ("legacy format: unexpected type class " + OUString::number(reader.getTypeClass()) + " of key " + sub.getName())); } } } LegacyProvider::LegacyProvider(Manager & manager, OUString const & uri): manager_(manager) { Registry reg; RegError e = reg.open(uri, RegAccessMode::READONLY); switch (e) { case RegError::NO_ERROR: break; case RegError::REGISTRY_NOT_EXISTS: throw NoSuchFileException(uri); default: throw FileFormatException( uri, "cannot open legacy file: " + OUString::number(static_cast(e))); } RegistryKey root; e = reg.openRootKey(root); if (e != RegError::NO_ERROR) { throw FileFormatException( uri, "legacy format: cannot open root key: " + OUString::number(static_cast(e))); } e = root.openKey("UCR", ucr_); switch (e) { case RegError::NO_ERROR: case RegError::KEY_NOT_EXISTS: // such effectively empty files exist in the wild break; default: throw FileFormatException( uri, "legacy format: cannot open UCR key: " + OUString::number(static_cast(e))); } } rtl::Reference< MapCursor > LegacyProvider::createRootCursor() const { return new Cursor(&manager_, ucr_, ucr_); } rtl::Reference< Entity > LegacyProvider::findEntity(OUString const & name) const { return ucr_.isValid() ? readEntity(&manager_, ucr_, ucr_, name.replace('.', '/'), true) : rtl::Reference< Entity >(); } LegacyProvider::~LegacyProvider() noexcept {} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */