tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / stoc / source / uriproc / UriReferenceFactory.cxx
blob2320194d205fbb9bc783d1199f35b29e83c3c239
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <cassert>
24 #include <cstddef>
25 #include <string_view>
26 #include <utility>
27 #include <vector>
29 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
30 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
31 #include <com/sun/star/lang/XServiceInfo.hpp>
32 #include <com/sun/star/uno/Any.hxx>
33 #include <com/sun/star/uno/Exception.hpp>
34 #include <com/sun/star/uno/Reference.hxx>
35 #include <com/sun/star/uno/RuntimeException.hpp>
36 #include <com/sun/star/uno/Sequence.hxx>
37 #include <com/sun/star/uno/XComponentContext.hpp>
38 #include <com/sun/star/uno/XInterface.hpp>
39 #include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp>
40 #include <com/sun/star/uri/XUriReference.hpp>
41 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
42 #include <com/sun/star/uri/XUriSchemeParser.hpp>
43 #include <cppuhelper/exc_hlp.hxx>
44 #include <cppuhelper/implbase.hxx>
45 #include <cppuhelper/supportsservice.hxx>
46 #include <cppuhelper/weak.hxx>
47 #include <rtl/character.hxx>
48 #include <rtl/ustrbuf.hxx>
49 #include <rtl/ustring.hxx>
50 #include <sal/types.h>
52 #include "UriReference.hxx"
54 namespace {
56 bool equalIgnoreEscapeCase(std::u16string_view s1, std::u16string_view s2) {
57 if (s1.size() == s2.size()) {
58 for (size_t i = 0; i < s1.size();) {
59 if (s1[i] == '%' && s2[i] == '%' && s1.size() - i > 2
60 && rtl::isAsciiHexDigit(s1[i + 1])
61 && rtl::isAsciiHexDigit(s1[i + 2])
62 && rtl::isAsciiHexDigit(s2[i + 1])
63 && rtl::isAsciiHexDigit(s2[i + 2])
64 && rtl::compareIgnoreAsciiCase(s1[i + 1], s2[i + 1]) == 0
65 && rtl::compareIgnoreAsciiCase(s1[i + 2], s2[i + 2]) == 0)
67 i += 3;
68 } else if (s1[i] != s2[i]) {
69 return false;
70 } else {
71 ++i;
74 return true;
75 } else {
76 return false;
80 sal_Int32 parseScheme(std::u16string_view uriReference) {
81 if (uriReference.size() >= 2 && rtl::isAsciiAlpha(uriReference[0])) {
82 for (size_t i = 0; i < uriReference.size(); ++i) {
83 sal_Unicode c = uriReference[i];
84 if (c == ':') {
85 return i;
86 } else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c)
87 && c != '+' && c != '-' && c != '.')
89 break;
93 return -1;
96 class UriReference:
97 public cppu::WeakImplHelper<css::uri::XUriReference>
99 public:
100 UriReference(
101 OUString const & scheme, bool bHasAuthority,
102 OUString const & authority, OUString const & path,
103 bool bHasQuery, OUString const & query):
104 m_base(
105 scheme, bHasAuthority, authority, path, bHasQuery,
106 query)
109 UriReference(const UriReference&) = delete;
110 UriReference& operator=(const UriReference&) = delete;
112 virtual OUString SAL_CALL getUriReference() override
113 { return m_base.getUriReference(); }
115 virtual sal_Bool SAL_CALL isAbsolute() override
116 { return m_base.isAbsolute(); }
118 virtual OUString SAL_CALL getScheme() override
119 { return m_base.getScheme(); }
121 virtual OUString SAL_CALL getSchemeSpecificPart() override
122 { return m_base.getSchemeSpecificPart(); }
124 virtual sal_Bool SAL_CALL isHierarchical() override
125 { return m_base.isHierarchical(); }
127 virtual sal_Bool SAL_CALL hasAuthority() override
128 { return m_base.hasAuthority(); }
130 virtual OUString SAL_CALL getAuthority() override
131 { return m_base.getAuthority(); }
133 virtual OUString SAL_CALL getPath() override
134 { return m_base.getPath(); }
136 virtual sal_Bool SAL_CALL hasRelativePath() override
137 { return m_base.hasRelativePath(); }
139 virtual sal_Int32 SAL_CALL getPathSegmentCount() override
140 { return m_base.getPathSegmentCount(); }
142 virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override
143 { return m_base.getPathSegment(index); }
145 virtual sal_Bool SAL_CALL hasQuery() override
146 { return m_base.hasQuery(); }
148 virtual OUString SAL_CALL getQuery() override
149 { return m_base.getQuery(); }
151 virtual sal_Bool SAL_CALL hasFragment() override
152 { return m_base.hasFragment(); }
154 virtual OUString SAL_CALL getFragment() override
155 { return m_base.getFragment(); }
157 virtual void SAL_CALL setFragment(OUString const & fragment) override
158 { m_base.setFragment(fragment); }
160 virtual void SAL_CALL clearFragment() override
161 { m_base.clearFragment(); }
163 private:
164 virtual ~UriReference() override {}
166 stoc::uriproc::UriReference m_base;
169 css::uno::Reference< css::uri::XUriReference > parseGeneric(
170 OUString const & scheme, std::u16string_view schemeSpecificPart)
172 size_t len = schemeSpecificPart.size();
173 size_t i = 0;
174 bool hasAuthority = false;
175 OUString authority;
176 if (len - i >= 2 && schemeSpecificPart[i] == '/'
177 && schemeSpecificPart[i + 1] == '/')
179 i += 2;
180 sal_Int32 n = i;
181 while (i < len && schemeSpecificPart[i] != '/'
182 && schemeSpecificPart[i] != '?') {
183 ++i;
185 hasAuthority = true;
186 authority = schemeSpecificPart.substr(n, i - n);
188 sal_Int32 n = i;
189 i = schemeSpecificPart.find('?', i);
190 if (i == std::u16string_view::npos) {
191 i = len;
193 OUString path( schemeSpecificPart.substr(n, i - n) );
194 bool hasQuery = false;
195 OUString query;
196 if (i != len) {
197 hasQuery = true;
198 query = schemeSpecificPart.substr(i + 1);
200 return new UriReference(
201 scheme, hasAuthority, authority, path, hasQuery, query);
204 struct Segment {
205 bool leadingSlash;
206 bool excessParent;
207 std::u16string_view segment;
209 Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment):
210 leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {}
213 std::pair<std::vector<Segment>, bool> processSegments(
214 std::u16string_view first, std::u16string_view second, bool processSpecialSegments)
216 std::vector<Segment> segments;
217 bool processed = false;
218 std::u16string_view const * half = &first;
219 // later checks for `half == &first` and `half == &second` rely on the fact that `first` and
220 // `second` are passed by value, in case a caller passes the same object for both arguments
221 std::size_t index = 0;
222 bool slash = false;
223 if (index == half->length()) {
224 half = &second;
225 index = 0;
227 if (index != half->length()) {
228 if ((*half)[index] == u'/') {
229 slash = true;
230 ++index;
232 for (;;) {
233 if (index == half->length() && half == &first) {
234 half = &second;
235 index = 0;
237 if (index == half->length()) {
238 if (slash) {
239 segments.emplace_back(true, false, std::u16string_view());
241 break;
243 auto const n = std::min(half->find(u'/', index), half->length());
244 auto const leadingSlash = slash;
245 auto const segment = half->substr(index, n - index);
246 auto const process = processSpecialSegments || half == &second;
247 index = n;
248 slash = false;
249 if (index == half->length() && half == &first) {
250 half = &second;
251 index = 0;
253 if (index != half->length() && (*half)[index] == u'/') {
254 slash = true;
255 ++index;
257 if (process) {
258 if (segment == u".") {
259 slash = leadingSlash;
260 processed = true;
261 continue;
262 } else if (segment == u"..") {
263 if (segments.empty() || segments.back().excessParent) {
264 segments.emplace_back(leadingSlash, true, segment);
265 } else {
266 if (leadingSlash) {
267 segments.pop_back();
269 slash = leadingSlash;
271 processed = true;
272 continue;
275 segments.emplace_back(leadingSlash, false, segment);
278 return {segments, processed};
281 class Factory:
282 public cppu::WeakImplHelper<
283 css::lang::XServiceInfo, css::uri::XUriReferenceFactory>
285 public:
286 explicit Factory(
287 css::uno::Reference< css::uno::XComponentContext > context):
288 m_context(std::move(context)) {}
290 Factory(const Factory&) = delete;
291 Factory& operator=(const Factory&) = delete;
293 virtual OUString SAL_CALL getImplementationName() override;
295 virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
297 virtual css::uno::Sequence< OUString > SAL_CALL
298 getSupportedServiceNames() override;
300 virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
301 parse(OUString const & uriReference) override;
303 virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
304 makeAbsolute(
305 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
306 css::uno::Reference< css::uri::XUriReference > const & uriReference,
307 sal_Bool processAdditionalSpecialSegments,
308 css::uri::RelativeUriExcessParentSegments excessParentSegments) override;
310 virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
311 makeRelative(
312 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
313 css::uno::Reference< css::uri::XUriReference > const & uriReference,
314 sal_Bool preferAuthorityOverRelativePath,
315 sal_Bool preferAbsoluteOverRelativePath,
316 sal_Bool encodeRetainedSpecialSegments) override;
318 private:
319 virtual ~Factory() override {}
321 css::uno::Reference< css::uri::XUriReference > clone(
322 css::uno::Reference< css::uri::XUriReference > const & uriReference)
323 { return parse(uriReference->getUriReference()); }
325 css::uno::Reference< css::uno::XComponentContext > m_context;
328 OUString Factory::getImplementationName()
330 return u"com.sun.star.comp.uri.UriReferenceFactory"_ustr;
333 sal_Bool Factory::supportsService(OUString const & serviceName)
335 return cppu::supportsService(this, serviceName);
338 css::uno::Sequence< OUString > Factory::getSupportedServiceNames()
340 css::uno::Sequence< OUString > s { u"com.sun.star.uri.UriReferenceFactory"_ustr };
341 return s;
344 css::uno::Reference< css::uri::XUriReference > Factory::parse(
345 OUString const & uriReference)
347 sal_Int32 fragment = uriReference.indexOf('#');
348 if (fragment == -1) {
349 fragment = uriReference.getLength();
351 OUString scheme;
352 OUString schemeSpecificPart;
353 OUString serviceName;
354 sal_Int32 n = parseScheme(uriReference);
355 assert(n < fragment);
356 if (n >= 0) {
357 scheme = uriReference.copy(0, n);
358 schemeSpecificPart = uriReference.copy(n + 1, fragment - (n + 1));
359 OUStringBuffer buf(128);
360 buf.append("com.sun.star.uri.UriSchemeParser_");
361 for (sal_Int32 i = 0; i < scheme.getLength(); ++i) {
362 sal_Unicode c = scheme[i];
363 if (rtl::isAsciiUpperCase(c)) {
364 buf.append(static_cast<sal_Unicode>(rtl::toAsciiLowerCase(c)));
365 } else if (c == '+') {
366 buf.append("PLUS");
367 } else if (c == '-') {
368 buf.append("HYPHEN");
369 } else if (c == '.') {
370 buf.append("DOT");
371 } else {
372 assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c));
373 buf.append(c);
376 serviceName = buf.makeStringAndClear();
377 } else {
378 schemeSpecificPart = uriReference.copy(0, fragment);
380 css::uno::Reference< css::uri::XUriSchemeParser > parser;
381 if (!serviceName.isEmpty()) {
382 css::uno::Reference< css::lang::XMultiComponentFactory > factory(
383 m_context->getServiceManager());
384 if (factory.is()) {
385 css::uno::Reference< css::uno::XInterface > service;
386 try {
387 service = factory->createInstanceWithContext(
388 serviceName, m_context);
389 } catch (css::uno::RuntimeException &) {
390 throw;
391 } catch (const css::uno::Exception &) {
392 css::uno::Any anyEx = cppu::getCaughtException();
393 throw css::lang::WrappedTargetRuntimeException(
394 "creating service " + serviceName,
395 getXWeak(),
396 anyEx);
398 if (service.is()) {
399 parser.set( service, css::uno::UNO_QUERY_THROW);
403 css::uno::Reference< css::uri::XUriReference > uriRef(
404 parser.is()
405 ? parser->parse(scheme, schemeSpecificPart)
406 : parseGeneric(scheme, schemeSpecificPart));
407 if (uriRef.is() && fragment != uriReference.getLength()) {
408 uriRef->setFragment(uriReference.copy(fragment + 1));
410 return uriRef;
413 css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute(
414 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
415 css::uno::Reference< css::uri::XUriReference > const & uriReference,
416 sal_Bool processAdditionalSpecialSegments,
417 css::uri::RelativeUriExcessParentSegments excessParentSegments)
419 if (!baseUriReference.is() || !baseUriReference->isAbsolute()
420 || !uriReference.is()) {
421 return nullptr;
422 } else if (uriReference->isAbsolute()) {
423 if (processAdditionalSpecialSegments) {
424 auto const path = uriReference->getPath();
425 auto [segments, proc] = processSegments(path, {}, true);
426 if (proc) {
427 OUStringBuffer abs(uriReference->getScheme() + ":");
428 if (uriReference->hasAuthority()) {
429 abs.append("//" + uriReference->getAuthority());
431 for (auto const & i : segments)
433 if (i.excessParent) {
434 switch (excessParentSegments) {
435 case css::uri::RelativeUriExcessParentSegments_ERROR:
436 return nullptr;
438 case css::uri::RelativeUriExcessParentSegments_RETAIN:
439 assert(i.segment == u"..");
440 break;
442 case css::uri::RelativeUriExcessParentSegments_REMOVE:
443 continue;
445 default:
446 assert(false);
447 break;
450 if (i.leadingSlash) {
451 abs.append('/');
453 abs.append(i.segment);
455 if (uriReference->hasQuery()) {
456 abs.append("?" + uriReference->getQuery());
458 if (uriReference->hasFragment()) {
459 abs.append("#" + uriReference->getFragment());
461 return parse(abs.makeStringAndClear());
464 return clone(uriReference);
465 } else if (!uriReference->hasAuthority()
466 && uriReference->getPath().isEmpty()) {
467 OUStringBuffer abs(baseUriReference->getScheme() + ":");
468 if (baseUriReference->hasAuthority()) {
469 abs.append("//" + baseUriReference->getAuthority());
471 abs.append(baseUriReference->getPath());
472 if (uriReference->hasQuery()) {
473 abs.append("?" + uriReference->getQuery());
474 } else if (baseUriReference->hasQuery()) {
475 abs.append("?" + baseUriReference->getQuery());
477 if (uriReference->hasFragment()) {
478 abs.append("#" + uriReference->getFragment());
480 return parse(abs.makeStringAndClear());
481 } else {
482 OUStringBuffer abs(128);
483 abs.append(baseUriReference->getScheme() + ":");
484 if (uriReference->hasAuthority()) {
485 abs.append("//" + uriReference->getAuthority());
486 } else if (baseUriReference->hasAuthority()) {
487 abs.append("//" + baseUriReference->getAuthority());
489 if (uriReference->hasRelativePath()) {
490 auto path1 = baseUriReference->getPath();
491 if (path1.isEmpty()) {
492 if (baseUriReference->hasAuthority()) {
493 path1 = "/";
495 } else {
496 path1 = path1.copy(0, path1.lastIndexOf('/') + 1);
498 auto const path2 = uriReference->getPath();
499 auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments);
500 (void)_;
501 for (auto const & i : segments)
503 if (i.excessParent) {
504 switch (excessParentSegments) {
505 case css::uri::RelativeUriExcessParentSegments_ERROR:
506 return nullptr;
508 case css::uri::RelativeUriExcessParentSegments_RETAIN:
509 assert(i.segment == u"..");
510 break;
512 case css::uri::RelativeUriExcessParentSegments_REMOVE:
513 continue;
515 default:
516 assert(false);
517 break;
520 if (i.leadingSlash) {
521 abs.append('/');
523 abs.append(i.segment);
525 } else {
526 bool processed = false;
527 if (processAdditionalSpecialSegments) {
528 auto const path = uriReference->getPath();
529 auto [segments, proc] = processSegments(path, {}, true);
530 if (proc) {
531 for (auto const & i : segments)
533 if (i.excessParent) {
534 switch (excessParentSegments) {
535 case css::uri::RelativeUriExcessParentSegments_ERROR:
536 return nullptr;
538 case css::uri::RelativeUriExcessParentSegments_RETAIN:
539 assert(i.segment == u"..");
540 break;
542 case css::uri::RelativeUriExcessParentSegments_REMOVE:
543 continue;
545 default:
546 assert(false);
547 break;
550 if (i.leadingSlash) {
551 abs.append('/');
553 abs.append(i.segment);
555 processed = true;
558 if (!processed) {
559 abs.append(uriReference->getPath());
562 if (uriReference->hasQuery()) {
563 abs.append("?" + uriReference->getQuery());
565 if (uriReference->hasFragment()) {
566 abs.append("#" + uriReference->getFragment());
568 return parse(abs.makeStringAndClear());
572 css::uno::Reference< css::uri::XUriReference > Factory::makeRelative(
573 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
574 css::uno::Reference< css::uri::XUriReference > const & uriReference,
575 sal_Bool preferAuthorityOverRelativePath,
576 sal_Bool preferAbsoluteOverRelativePath,
577 sal_Bool encodeRetainedSpecialSegments)
579 if (!baseUriReference.is() || !baseUriReference->isAbsolute()
580 || !uriReference.is()) {
581 return nullptr;
582 } else if (!uriReference->isAbsolute() || uriReference->hasRelativePath()
583 || !baseUriReference->getScheme().equalsIgnoreAsciiCase(
584 uriReference->getScheme())) {
585 return clone(uriReference);
586 } else {
587 OUStringBuffer rel(128);
588 bool omitQuery = false;
589 if ((baseUriReference->hasAuthority() != uriReference->hasAuthority())
590 || !equalIgnoreEscapeCase(
591 baseUriReference->getAuthority(),
592 uriReference->getAuthority()))
594 if (uriReference->hasAuthority()) {
595 rel.append("//" + uriReference->getAuthority());
597 rel.append(uriReference->getPath());
598 } else if ((equalIgnoreEscapeCase(
599 baseUriReference->getPath(), uriReference->getPath())
600 || (baseUriReference->getPath() == "/"
601 && uriReference->getPath().isEmpty()))
602 && baseUriReference->hasQuery() == uriReference->hasQuery()
603 && equalIgnoreEscapeCase(
604 baseUriReference->getQuery(), uriReference->getQuery()))
606 omitQuery = true;
607 } else {
608 sal_Int32 count1 = std::max< sal_Int32 >(
609 baseUriReference->getPathSegmentCount(), 1);
610 sal_Int32 count2 = std::max< sal_Int32 >(
611 uriReference->getPathSegmentCount(), 1);
612 sal_Int32 i = 0;
613 for (; i < std::min(count1, count2) - 1; ++i) {
614 if (!equalIgnoreEscapeCase(
615 baseUriReference->getPathSegment(i),
616 uriReference->getPathSegment(i)))
618 break;
621 if (i == 0
622 && (preferAbsoluteOverRelativePath || uriReference->hasQuery())
623 && (preferAuthorityOverRelativePath
624 || !uriReference->getPath().startsWith("//")))
626 if (uriReference->getPath().isEmpty()) {
627 if (!baseUriReference->getPath().isEmpty()
628 && baseUriReference->getPath() != "/")
630 rel.append('/');
632 } else if (uriReference->getPath() == "/") {
633 if (baseUriReference->getPath().isEmpty()
634 || baseUriReference->getPath() != "/")
636 rel.append('/');
638 } else {
639 if (uriReference->getPath().startsWith("//")) {
640 assert(uriReference->hasAuthority());
641 rel.append("//" + uriReference->getAuthority());
643 rel.append(uriReference->getPath());
645 } else {
646 bool segments = false;
647 for (sal_Int32 j = i; j < count1 - 1; ++j) {
648 if (segments) {
649 rel.append('/');
651 rel.append("..");
652 segments = true;
654 if (i < count2 - 1
655 || (!uriReference->getPathSegment(count2 - 1).isEmpty()))
657 if (!segments
658 && (uriReference->getPathSegment(i).isEmpty()
659 || (parseScheme(uriReference->getPathSegment(i))
660 >= 0)))
662 rel.append('.');
663 segments = true;
665 for (; i < count2; ++i) {
666 if (segments) {
667 rel.append('/');
669 OUString s(uriReference->getPathSegment(i));
670 if (encodeRetainedSpecialSegments && s == ".") {
671 rel.append("%2E");
672 } else if (encodeRetainedSpecialSegments && s == "..") {
673 rel.append("%2E%2E");
674 } else {
675 rel.append(s);
677 segments = true;
682 if (!omitQuery && uriReference->hasQuery()) {
683 rel.append("?" + uriReference->getQuery());
685 if (uriReference->hasFragment()) {
686 rel.append("#" + uriReference->getFragment());
688 return parse(rel.makeStringAndClear());
694 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
695 com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext,
696 css::uno::Sequence<css::uno::Any> const &)
698 return ::cppu::acquire(new Factory(rxContext));
701 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */