Quelle UriReferenceFactory.cxx
Sprache: C
/* -*- 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/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2 . 0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <string_view>
#include <utility>
#include <vector>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Exception.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/uno/XInterface.hpp>
#include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp>
#include <com/sun/star/uri/XUriReference.hpp>
#include <com/sun/star/uri/XUriReferenceFactory.hpp>
#include <com/sun/star/uri/XUriSchemeParser.hpp>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/weak.hxx>
#include <rtl/character.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/types.h>
#include "UriReference.hxx"
namespace {
bool equalIgnoreEscapeCase(std::u16string_view s1, std::u16string_view s2) {
if (s1.size() == s2.size()) {
for (size_t i = 0 ; i < s1.size();) {
if (s1[i] == '%' && s2[i] == '%' && s1.size() - i > 2
&& rtl::isAsciiHexDigit(s1[i + 1 ])
&& rtl::isAsciiHexDigit(s1[i + 2 ])
&& rtl::isAsciiHexDigit(s2[i + 1 ])
&& rtl::isAsciiHexDigit(s2[i + 2 ])
&& rtl::compareIgnoreAsciiCase(s1[i + 1 ], s2[i + 1 ]) == 0
&& rtl::compareIgnoreAsciiCase(s1[i + 2 ], s2[i + 2 ]) == 0 )
{
i += 3 ;
} else if (s1[i] != s2[i]) {
return false ;
} else {
++i;
}
}
return true ;
} else {
return false ;
}
}
sal_Int32 parseScheme(std::u16string_view uriReference) {
if (uriReference.size() >= 2 && rtl::isAsciiAlpha(uriReference[0 ])) {
for (size_t i = 0 ; i < uriReference.size(); ++i) {
sal_Unicode c = uriReference[i];
if (c == ':' ) {
return i;
} else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c)
&& c != '+' && c != '-' && c != '.' )
{
break ;
}
}
}
return -1 ;
}
class UriReference:
public cppu::WeakImplHelper<css::uri::XUriReference>
{
public :
UriReference(
OUString const & scheme, bool bHasAuthority,
OUString const & authority, OUString const & path,
bool bHasQuery, OUString const & query):
m_base(
scheme, bHasAuthority, authority, path, bHasQuery,
query)
{}
UriReference(const UriReference&) = delete ;
UriReference& operator =(const UriReference&) = delete ;
virtual OUString SAL_CALL getUriReference() override
{ return m_base.getUriReference(); }
virtual sal_Bool SAL_CALL isAbsolute() override
{ return m_base.isAbsolute(); }
virtual OUString SAL_CALL getScheme() override
{ return m_base.getScheme(); }
virtual OUString SAL_CALL getSchemeSpecificPart() override
{ return m_base.getSchemeSpecificPart(); }
virtual sal_Bool SAL_CALL isHierarchical() override
{ return m_base.isHierarchical(); }
virtual sal_Bool SAL_CALL hasAuthority() override
{ return m_base.hasAuthority(); }
virtual OUString SAL_CALL getAuthority() override
{ return m_base.getAuthority(); }
virtual OUString SAL_CALL getPath() override
{ return m_base.getPath(); }
virtual sal_Bool SAL_CALL hasRelativePath() override
{ return m_base.hasRelativePath(); }
virtual sal_Int32 SAL_CALL getPathSegmentCount() override
{ return m_base.getPathSegmentCount(); }
virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override
{ return m_base.getPathSegment(index); }
virtual sal_Bool SAL_CALL hasQuery() override
{ return m_base.hasQuery(); }
virtual OUString SAL_CALL getQuery() override
{ return m_base.getQuery(); }
virtual sal_Bool SAL_CALL hasFragment() override
{ return m_base.hasFragment(); }
virtual OUString SAL_CALL getFragment() override
{ return m_base.getFragment(); }
virtual void SAL_CALL setFragment(OUString const & fragment) override
{ m_base.setFragment(fragment); }
virtual void SAL_CALL clearFragment() override
{ m_base.clearFragment(); }
private :
virtual ~UriReference() override {}
stoc::uriproc::UriReference m_base;
};
css::uno::Reference< css::uri::XUriReference > parseGeneric(
OUString const & scheme, std::u16string_view schemeSpecificPart)
{
size_t len = schemeSpecificPart.size();
size_t i = 0 ;
bool hasAuthority = false ;
OUString authority;
if (len - i >= 2 && schemeSpecificPart[i] == '/'
&& schemeSpecificPart[i + 1 ] == '/' )
{
i += 2 ;
sal_Int32 n = i;
while (i < len && schemeSpecificPart[i] != '/'
&& schemeSpecificPart[i] != '?' ) {
++i;
}
hasAuthority = true ;
authority = schemeSpecificPart.substr(n, i - n);
}
sal_Int32 n = i;
i = schemeSpecificPart.find('?' , i);
if (i == std::u16string_view::npos) {
i = len;
}
OUString path( schemeSpecificPart.substr(n, i - n) );
bool hasQuery = false ;
OUString query;
if (i != len) {
hasQuery = true ;
query = schemeSpecificPart.substr(i + 1 );
}
return new UriReference(
scheme, hasAuthority, authority, path, hasQuery, query);
}
struct Segment {
bool leadingSlash;
bool excessParent;
std::u16string_view segment;
Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment):
leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {}
};
std::pair<std::vector<Segment>, bool > processSegments(
std::u16string_view first, std::u16string_view second, bool processSpecialSegments)
{
std::vector<Segment> segments;
bool processed = false ;
std::u16string_view const * half = &first;
// later checks for `half == &first` and `half == &second` rely on the fact that `first` and
// `second` are passed by value, in case a caller passes the same object for both arguments
std::size_t index = 0 ;
bool slash = false ;
if (index == half->length()) {
half = &second;
index = 0 ;
}
if (index != half->length()) {
if ((*half)[index] == u'/' ) {
slash = true ;
++index;
}
for (;;) {
if (index == half->length() && half == &first) {
half = &second;
index = 0 ;
}
if (index == half->length()) {
if (slash) {
segments.emplace_back(true , false , std::u16string_view());
}
break ;
}
auto const n = std::min(half->find(u'/' , index), half->length());
auto const leadingSlash = slash;
auto const segment = half->substr(index, n - index);
auto const process = processSpecialSegments || half == &second;
index = n;
slash = false ;
if (index == half->length() && half == &first) {
half = &second;
index = 0 ;
}
if (index != half->length() && (*half)[index] == u'/' ) {
slash = true ;
++index;
}
if (process) {
if (segment == u"." ) {
slash = leadingSlash;
processed = true ;
continue ;
} else if (segment == u".." ) {
if (segments.empty() || segments.back().excessParent) {
segments.emplace_back(leadingSlash, true , segment);
} else {
if (leadingSlash) {
segments.pop_back();
}
slash = leadingSlash;
}
processed = true ;
continue ;
}
}
segments.emplace_back(leadingSlash, false , segment);
}
}
return {segments, processed};
}
class Factory:
public cppu::WeakImplHelper<
css::lang::XServiceInfo, css::uri::XUriReferenceFactory>
{
public :
explicit Factory(
css::uno::Reference< css::uno::XComponentContext > context):
m_context(std::move(context)) {}
Factory(const Factory&) = delete ;
Factory& operator =(const Factory&) = delete ;
virtual OUString SAL_CALL getImplementationName() override;
virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
virtual css::uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames() override;
virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
parse(OUString const & uriReference) override;
virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
makeAbsolute(
css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
css::uno::Reference< css::uri::XUriReference > const & uriReference,
sal_Bool processAdditionalSpecialSegments,
css::uri::RelativeUriExcessParentSegments excessParentSegments) override;
virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
makeRelative(
css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
css::uno::Reference< css::uri::XUriReference > const & uriReference,
sal_Bool preferAuthorityOverRelativePath,
sal_Bool preferAbsoluteOverRelativePath,
sal_Bool encodeRetainedSpecialSegments) override;
private :
virtual ~Factory() override {}
css::uno::Reference< css::uri::XUriReference > clone(
css::uno::Reference< css::uri::XUriReference > const & uriReference)
{ return parse(uriReference->getUriReference()); }
css::uno::Reference< css::uno::XComponentContext > m_context;
};
OUString Factory::getImplementationName()
{
return u"com.sun.star.comp.uri.UriReferenceFactory" _ustr;
}
sal_Bool Factory::supportsService(OUString const & serviceName)
{
return cppu::supportsService(this , serviceName);
}
css::uno::Sequence< OUString > Factory::getSupportedServiceNames()
{
css::uno::Sequence< OUString > s { u"com.sun.star.uri.UriReferenceFactory" _ustr };
return s;
}
css::uno::Reference< css::uri::XUriReference > Factory::parse(
OUString const & uriReference)
{
sal_Int32 fragment = uriReference.indexOf('#' );
if (fragment == -1 ) {
fragment = uriReference.getLength();
}
OUString scheme;
OUString schemeSpecificPart;
OUString serviceName;
sal_Int32 n = parseScheme(uriReference);
assert(n < fragment);
if (n >= 0 ) {
scheme = uriReference.copy(0 , n);
schemeSpecificPart = uriReference.copy(n + 1 , fragment - (n + 1 ));
OUStringBuffer buf(128 );
buf.append("com.sun.star.uri.UriSchemeParser_" );
for (sal_Int32 i = 0 ; i < scheme.getLength(); ++i) {
sal_Unicode c = scheme[i];
if (rtl::isAsciiUpperCase(c)) {
buf.append(static_cast <sal_Unicode>(rtl::toAsciiLowerCase(c)));
} else if (c == '+' ) {
buf.append("PLUS" );
} else if (c == '-' ) {
buf.append("HYPHEN" );
} else if (c == '.' ) {
buf.append("DOT" );
} else {
assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c));
buf.append(c);
}
}
serviceName = buf.makeStringAndClear();
} else {
schemeSpecificPart = uriReference.copy(0 , fragment);
}
css::uno::Reference< css::uri::XUriSchemeParser > parser;
if (!serviceName.isEmpty()) {
css::uno::Reference< css::lang::XMultiComponentFactory > factory(
m_context->getServiceManager());
if (factory.is()) {
css::uno::Reference< css::uno::XInterface > service;
try {
service = factory->createInstanceWithContext(
serviceName, m_context);
} catch (css::uno::RuntimeException &) {
throw ;
} catch (const css::uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"creating service " + serviceName,
getXWeak(),
anyEx);
}
if (service.is()) {
parser.set( service, css::uno::UNO_QUERY_THROW);
}
}
}
css::uno::Reference< css::uri::XUriReference > uriRef(
parser.is()
? parser->parse(scheme, schemeSpecificPart)
: parseGeneric(scheme, schemeSpecificPart));
if (uriRef.is() && fragment != uriReference.getLength()) {
uriRef->setFragment(uriReference.copy(fragment + 1 ));
}
return uriRef;
}
css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute(
css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
css::uno::Reference< css::uri::XUriReference > const & uriReference,
sal_Bool processAdditionalSpecialSegments,
css::uri::RelativeUriExcessParentSegments excessParentSegments)
{
if (!baseUriReference.is() || !baseUriReference->isAbsolute()
|| !uriReference.is()) {
return nullptr;
} else if (uriReference->isAbsolute()) {
if (processAdditionalSpecialSegments) {
auto const path = uriReference->getPath();
auto [segments, proc] = processSegments(path, {}, true );
if (proc) {
OUStringBuffer abs(uriReference->getScheme() + ":" );
if (uriReference->hasAuthority()) {
abs.append("//" + uriReference->getAuthority());
}
for (auto const & i : segments)
{
if (i.excessParent) {
switch (excessParentSegments) {
case css::uri::RelativeUriExcessParentSegments_ERROR:
return nullptr;
case css::uri::RelativeUriExcessParentSegments_RETAIN:
assert(i.segment == u".." );
break ;
case css::uri::RelativeUriExcessParentSegments_REMOVE:
continue ;
default :
assert(false );
break ;
}
}
if (i.leadingSlash) {
abs.append('/' );
}
abs.append(i.segment);
}
if (uriReference->hasQuery()) {
abs.append("?" + uriReference->getQuery());
}
if (uriReference->hasFragment()) {
abs.append("#" + uriReference->getFragment());
}
return parse(abs.makeStringAndClear());
}
}
return clone(uriReference);
} else if (!uriReference->hasAuthority()
&& uriReference->getPath().isEmpty()) {
OUStringBuffer abs(baseUriReference->getScheme() + ":" );
if (baseUriReference->hasAuthority()) {
abs.append("//" + baseUriReference->getAuthority());
}
abs.append(baseUriReference->getPath());
if (uriReference->hasQuery()) {
abs.append("?" + uriReference->getQuery());
} else if (baseUriReference->hasQuery()) {
abs.append("?" + baseUriReference->getQuery());
}
if (uriReference->hasFragment()) {
abs.append("#" + uriReference->getFragment());
}
return parse(abs.makeStringAndClear());
} else {
OUStringBuffer abs(128 );
abs.append(baseUriReference->getScheme() + ":" );
if (uriReference->hasAuthority()) {
abs.append("//" + uriReference->getAuthority());
} else if (baseUriReference->hasAuthority()) {
abs.append("//" + baseUriReference->getAuthority());
}
if (uriReference->hasRelativePath()) {
auto path1 = baseUriReference->getPath();
if (path1.isEmpty()) {
if (baseUriReference->hasAuthority()) {
path1 = "/" ;
}
} else {
path1 = path1.copy(0 , path1.lastIndexOf('/' ) + 1 );
}
auto const path2 = uriReference->getPath();
auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments);
(void )_;
for (auto const & i : segments)
{
if (i.excessParent) {
switch (excessParentSegments) {
case css::uri::RelativeUriExcessParentSegments_ERROR:
return nullptr;
case css::uri::RelativeUriExcessParentSegments_RETAIN:
assert(i.segment == u".." );
break ;
case css::uri::RelativeUriExcessParentSegments_REMOVE:
continue ;
default :
assert(false );
break ;
}
}
if (i.leadingSlash) {
abs.append('/' );
}
abs.append(i.segment);
}
} else {
bool processed = false ;
if (processAdditionalSpecialSegments) {
auto const path = uriReference->getPath();
auto [segments, proc] = processSegments(path, {}, true );
if (proc) {
for (auto const & i : segments)
{
if (i.excessParent) {
switch (excessParentSegments) {
case css::uri::RelativeUriExcessParentSegments_ERROR:
return nullptr;
case css::uri::RelativeUriExcessParentSegments_RETAIN:
assert(i.segment == u".." );
break ;
case css::uri::RelativeUriExcessParentSegments_REMOVE:
continue ;
default :
assert(false );
break ;
}
}
if (i.leadingSlash) {
abs.append('/' );
}
abs.append(i.segment);
}
processed = true ;
}
}
if (!processed) {
abs.append(uriReference->getPath());
}
}
if (uriReference->hasQuery()) {
abs.append("?" + uriReference->getQuery());
}
if (uriReference->hasFragment()) {
abs.append("#" + uriReference->getFragment());
}
return parse(abs.makeStringAndClear());
}
}
css::uno::Reference< css::uri::XUriReference > Factory::makeRelative(
css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
css::uno::Reference< css::uri::XUriReference > const & uriReference,
sal_Bool preferAuthorityOverRelativePath,
sal_Bool preferAbsoluteOverRelativePath,
sal_Bool encodeRetainedSpecialSegments)
{
if (!baseUriReference.is() || !baseUriReference->isAbsolute()
|| !uriReference.is()) {
return nullptr;
} else if (!uriReference->isAbsolute() || uriReference->hasRelativePath()
|| !baseUriReference->getScheme().equalsIgnoreAsciiCase(
uriReference->getScheme())) {
return clone(uriReference);
} else {
OUStringBuffer rel(128 );
bool omitQuery = false ;
if ((baseUriReference->hasAuthority() != uriReference->hasAuthority())
|| !equalIgnoreEscapeCase(
baseUriReference->getAuthority(),
uriReference->getAuthority()))
{
if (uriReference->hasAuthority()) {
rel.append("//" + uriReference->getAuthority());
}
rel.append(uriReference->getPath());
} else if ((equalIgnoreEscapeCase(
baseUriReference->getPath(), uriReference->getPath())
|| (baseUriReference->getPath() == "/"
&& uriReference->getPath().isEmpty()))
&& baseUriReference->hasQuery() == uriReference->hasQuery()
&& equalIgnoreEscapeCase(
baseUriReference->getQuery(), uriReference->getQuery()))
{
omitQuery = true ;
} else {
sal_Int32 count1 = std::max< sal_Int32 >(
baseUriReference->getPathSegmentCount(), 1 );
sal_Int32 count2 = std::max< sal_Int32 >(
uriReference->getPathSegmentCount(), 1 );
sal_Int32 i = 0 ;
for (; i < std::min(count1, count2) - 1 ; ++i) {
if (!equalIgnoreEscapeCase(
baseUriReference->getPathSegment(i),
uriReference->getPathSegment(i)))
{
break ;
}
}
if (i == 0
&& (preferAbsoluteOverRelativePath || uriReference->hasQuery())
&& (preferAuthorityOverRelativePath
|| !uriReference->getPath().startsWith("//")))
{
if (uriReference->getPath().isEmpty()) {
if (!baseUriReference->getPath().isEmpty()
&& baseUriReference->getPath() != "/" )
{
rel.append('/' );
}
} else if (uriReference->getPath() == "/" ) {
if (baseUriReference->getPath().isEmpty()
|| baseUriReference->getPath() != "/" )
{
rel.append('/' );
}
} else {
if (uriReference->getPath().startsWith("//")) {
assert(uriReference->hasAuthority());
rel.append("//" + uriReference->getAuthority());
}
rel.append(uriReference->getPath());
}
} else {
bool segments = false ;
for (sal_Int32 j = i; j < count1 - 1 ; ++j) {
if (segments) {
rel.append('/' );
}
rel.append(".." );
segments = true ;
}
if (i < count2 - 1
|| (!uriReference->getPathSegment(count2 - 1 ).isEmpty()))
{
if (!segments
&& (uriReference->getPathSegment(i).isEmpty()
|| (parseScheme(uriReference->getPathSegment(i))
>= 0 )))
{
rel.append('.' );
segments = true ;
}
for (; i < count2; ++i) {
if (segments) {
rel.append('/' );
}
OUString s(uriReference->getPathSegment(i));
if (encodeRetainedSpecialSegments && s == "." ) {
rel.append("%2E" );
} else if (encodeRetainedSpecialSegments && s == ".." ) {
rel.append("%2E%2E" );
} else {
rel.append(s);
}
segments = true ;
}
}
}
}
if (!omitQuery && uriReference->hasQuery()) {
rel.append("?" + uriReference->getQuery());
}
if (uriReference->hasFragment()) {
rel.append("#" + uriReference->getFragment());
}
return parse(rel.makeStringAndClear());
}
}
}
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext,
css::uno::Sequence<css::uno::Any> const &)
{
return ::cppu::acquire(new Factory(rxContext));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=98 H=94 G=95
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298