Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / desktop / source / deployment / misc / dp_descriptioninfoset.cxx
blob00b32c04f2c4bf9f58a2b58736d1fa346cf8c11f
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 <string_view>
24 #include <dp_descriptioninfoset.hxx>
26 #include <dp_resource.h>
28 #include <comphelper/sequence.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/propertysequence.hxx>
31 #include <optional>
32 #include <com/sun/star/configuration/theDefaultProvider.hpp>
33 #include <com/sun/star/container/XNameAccess.hpp>
34 #include <com/sun/star/deployment/DeploymentException.hpp>
35 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/io/SequenceInputStream.hpp>
37 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
38 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
39 #include <com/sun/star/task/XInteractionHandler.hpp>
40 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
41 #include <com/sun/star/ucb/XProgressHandler.hpp>
42 #include <com/sun/star/uno/Reference.hxx>
43 #include <com/sun/star/uno/RuntimeException.hpp>
44 #include <com/sun/star/uno/Sequence.hxx>
45 #include <com/sun/star/uno/XInterface.hpp>
46 #include <com/sun/star/xml/dom/DOMException.hpp>
47 #include <com/sun/star/xml/dom/XNode.hpp>
48 #include <com/sun/star/xml/dom/XNodeList.hpp>
49 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
50 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
51 #include <com/sun/star/xml/xpath/XPathException.hpp>
52 #include <com/sun/star/ucb/InteractiveIOException.hpp>
53 #include <cppuhelper/implbase.hxx>
54 #include <cppuhelper/weak.hxx>
55 #include <cppuhelper/exc_hlp.hxx>
56 #include <rtl/ustring.hxx>
57 #include <sal/types.h>
58 #include <ucbhelper/content.hxx>
59 #include <o3tl/string_view.hxx>
61 namespace {
63 using css::uno::Reference;
65 class EmptyNodeList:
66 public cppu::WeakImplHelper<css::xml::dom::XNodeList>
68 public:
69 EmptyNodeList();
71 EmptyNodeList(const EmptyNodeList&) = delete;
72 const EmptyNodeList& operator=(const EmptyNodeList&) = delete;
74 virtual ::sal_Int32 SAL_CALL getLength() override;
76 virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
77 item(::sal_Int32 index) override;
80 EmptyNodeList::EmptyNodeList() {}
82 ::sal_Int32 EmptyNodeList::getLength() {
83 return 0;
86 css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
88 throw css::uno::RuntimeException("bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call",
89 static_cast< ::cppu::OWeakObject * >(this));
92 OUString getNodeValue(
93 css::uno::Reference< css::xml::dom::XNode > const & node)
95 OSL_ASSERT(node.is());
96 try {
97 return node->getNodeValue();
98 } catch (const css::xml::dom::DOMException & e) {
99 css::uno::Any anyEx = cppu::getCaughtException();
100 throw css::lang::WrappedTargetRuntimeException(
101 "com.sun.star.xml.dom.DOMException: " + e.Message,
102 nullptr, anyEx );
106 /**The class uses the UCB to access the description.xml file in an
107 extension. The UCB must have been initialized already. It also
108 requires that the extension has already be unzipped to a particular
109 location.
111 class ExtensionDescription
113 public:
114 /**throws an exception if the description.xml is not
115 available, cannot be read, does not contain the expected data,
116 or any other error occurred. Therefore it should only be used with
117 new extensions.
119 Throws css::uno::RuntimeException,
120 css::deployment::DeploymentException,
121 dp_registry::backend::bundle::NoDescriptionException.
123 ExtensionDescription(
124 const css::uno::Reference<css::uno::XComponentContext>& xContext,
125 std::u16string_view installDir,
126 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
128 const css::uno::Reference<css::xml::dom::XNode>& getRootElement() const
130 return m_xRoot;
133 private:
134 css::uno::Reference<css::xml::dom::XNode> m_xRoot;
137 class NoDescriptionException
141 class FileDoesNotExistFilter
142 : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
143 css::task::XInteractionHandler >
146 bool m_bExist;
147 css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
149 public:
150 explicit FileDoesNotExistFilter(
151 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
153 bool exist() { return m_bExist;}
154 // XCommandEnvironment
155 virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
156 getInteractionHandler() override;
157 virtual css::uno::Reference<css::ucb::XProgressHandler >
158 SAL_CALL getProgressHandler() override;
160 // XInteractionHandler
161 virtual void SAL_CALL handle(
162 css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
165 ExtensionDescription::ExtensionDescription(
166 const Reference<css::uno::XComponentContext>& xContext,
167 std::u16string_view installDir,
168 const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
170 try {
171 //may throw css::ucb::ContentCreationException
172 //If there is no description.xml then ucb will start an interaction which
173 //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
174 //and filter the respective exception out.
175 OUString sDescriptionUri(OUString::Concat(installDir) + "/description.xml");
176 Reference<css::ucb::XCommandEnvironment> xFilter = new FileDoesNotExistFilter(xCmdEnv);
177 ::ucbhelper::Content descContent(sDescriptionUri, xFilter, xContext);
179 //throws a css::uno::Exception if the file is not available
180 Reference<css::io::XInputStream> xIn;
182 { //throws com.sun.star.ucb.InteractiveIOException
183 xIn = descContent.openStream();
185 catch ( const css::uno::Exception& )
187 if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
188 throw NoDescriptionException();
189 throw;
191 if (!xIn.is())
193 throw css::uno::Exception(
194 "Could not get XInputStream for description.xml of extension " +
195 sDescriptionUri, nullptr);
198 //get root node of description.xml
199 Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
200 css::xml::dom::DocumentBuilder::create(xContext) );
202 if (!xDocBuilder->isNamespaceAware())
204 throw css::uno::Exception(
205 "Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware.", nullptr);
208 Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
209 if (!xDoc.is())
211 throw css::uno::Exception(sDescriptionUri + " contains data which cannot be parsed. ", nullptr);
214 //check for proper root element and namespace
215 Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
216 if (!xRoot.is())
218 throw css::uno::Exception(
219 sDescriptionUri + " contains no root element.", nullptr);
222 if ( xRoot->getTagName() != "description")
224 throw css::uno::Exception(
225 sDescriptionUri + " does not contain the root element <description>.", nullptr);
228 m_xRoot.set(xRoot, css::uno::UNO_QUERY_THROW);
229 OUString nsDescription = xRoot->getNamespaceURI();
231 //check if this namespace is supported
232 if ( nsDescription != "http://openoffice.org/extensions/description/2006")
234 throw css::uno::Exception(sDescriptionUri + " contains a root element with an unsupported namespace. ", nullptr);
236 } catch (const css::uno::RuntimeException &) {
237 throw;
238 } catch (const css::deployment::DeploymentException &) {
239 throw;
240 } catch (const css::uno::Exception & e) {
241 css::uno::Any a(cppu::getCaughtException());
242 throw css::deployment::DeploymentException(
243 e.Message, Reference< css::uno::XInterface >(), a);
247 FileDoesNotExistFilter::FileDoesNotExistFilter(
248 const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
249 m_bExist(true), m_xCommandEnv(xCmdEnv)
252 // XCommandEnvironment
253 Reference<css::task::XInteractionHandler >
254 FileDoesNotExistFilter::getInteractionHandler()
256 return static_cast<css::task::XInteractionHandler*>(this);
259 Reference<css::ucb::XProgressHandler >
260 FileDoesNotExistFilter::getProgressHandler()
262 return m_xCommandEnv.is()
263 ? m_xCommandEnv->getProgressHandler()
264 : Reference<css::ucb::XProgressHandler>();
267 // XInteractionHandler
268 //If the interaction was caused by a non-existing file which is specified in the ctor
269 //of FileDoesNotExistFilter, then we do nothing
270 void FileDoesNotExistFilter::handle(
271 Reference<css::task::XInteractionRequest > const & xRequest )
273 css::uno::Any request( xRequest->getRequest() );
275 css::ucb::InteractiveIOException ioexc;
276 if ((request>>= ioexc)
277 && (ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING
278 || ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING_PATH))
280 m_bExist = false;
281 return;
283 Reference<css::task::XInteractionHandler> xInteraction;
284 if (m_xCommandEnv.is()) {
285 xInteraction = m_xCommandEnv->getInteractionHandler();
287 if (xInteraction.is()) {
288 xInteraction->handle(xRequest);
294 namespace dp_misc {
296 DescriptionInfoset getDescriptionInfoset(std::u16string_view sExtensionFolderURL)
298 Reference< css::xml::dom::XNode > root;
299 Reference<css::uno::XComponentContext> context(
300 comphelper::getProcessComponentContext());
301 try {
302 root =
303 ExtensionDescription(
304 context, sExtensionFolderURL,
305 Reference< css::ucb::XCommandEnvironment >()).
306 getRootElement();
307 } catch (const NoDescriptionException &) {
308 } catch (const css::deployment::DeploymentException & e) {
309 css::uno::Any anyEx = cppu::getCaughtException();
310 throw css::lang::WrappedTargetRuntimeException(
311 "com.sun.star.deployment.DeploymentException: " + e.Message,
312 nullptr, anyEx );
314 return DescriptionInfoset(context, root);
317 DescriptionInfoset::DescriptionInfoset(
318 css::uno::Reference< css::uno::XComponentContext > const & context,
319 css::uno::Reference< css::xml::dom::XNode > const & element):
320 m_context(context),
321 m_element(element)
323 if (m_element.is()) {
324 m_xpath = css::xml::xpath::XPathAPI::create(context);
325 m_xpath->registerNS("desc", element->getNamespaceURI());
326 m_xpath->registerNS("xlink", "http://www.w3.org/1999/xlink");
330 DescriptionInfoset::~DescriptionInfoset() {}
332 ::std::optional< OUString > DescriptionInfoset::getIdentifier() const {
333 return getOptionalValue("desc:identifier/@value");
336 OUString DescriptionInfoset::getNodeValueFromExpression(OUString const & expression) const
338 css::uno::Reference< css::xml::dom::XNode > n;
339 if (m_element.is()) {
340 try {
341 n = m_xpath->selectSingleNode(m_element, expression);
342 } catch (const css::xml::xpath::XPathException &) {
343 // ignore
346 return n.is() ? getNodeValue(n) : OUString();
349 void DescriptionInfoset::checkDenylist() const
351 if (!m_element.is())
352 return;
354 std::optional< OUString > id(getIdentifier());
355 if (!id)
356 return; // nothing to check
357 OUString currentversion(getVersion());
358 if (currentversion.getLength() == 0)
359 return; // nothing to check
361 css::uno::Sequence<css::uno::Any> args(comphelper::InitAnyPropertySequence(
363 {"nodepath", css::uno::Any(OUString("/org.openoffice.Office.ExtensionDependencies/Extensions"))}
364 }));
365 css::uno::Reference< css::container::XNameAccess > denylist(
366 (css::configuration::theDefaultProvider::get(m_context)
367 ->createInstanceWithArguments(
368 "com.sun.star.configuration.ConfigurationAccess", args)),
369 css::uno::UNO_QUERY_THROW);
371 // check first if a denylist entry is available
372 if (!(denylist.is() && denylist->hasByName(*id))) return;
374 css::uno::Reference< css::beans::XPropertySet > extProps(
375 denylist->getByName(*id), css::uno::UNO_QUERY_THROW);
377 css::uno::Any anyValue = extProps->getPropertyValue("Versions");
379 css::uno::Sequence< OUString > blversions;
380 anyValue >>= blversions;
382 // check if the current version requires further dependency checks from the denylist
383 if (!checkDenylistVersion(currentversion, blversions)) return;
385 anyValue = extProps->getPropertyValue("Dependencies");
386 OUString udeps;
387 anyValue >>= udeps;
389 if (udeps.getLength() == 0)
390 return; // nothing todo
392 OString xmlDependencies = OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
394 css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
395 m_context->getServiceManager()->createInstanceWithContext("com.sun.star.xml.dom.DocumentBuilder", m_context),
396 css::uno::UNO_QUERY_THROW);
398 css::uno::Sequence< sal_Int8 > byteSeq(reinterpret_cast<const sal_Int8*>(xmlDependencies.getStr()), xmlDependencies.getLength());
400 css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
401 css::uno::UNO_QUERY_THROW);
403 css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
404 css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
405 css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
406 sal_Int32 nLen = xDeps->getLength();
408 // get the parent xml document of current description info for the import
409 css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
411 // get dependency node of current description info to merge the new dependencies from the denylist
412 css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
413 m_xpath->selectSingleNode(m_element, "desc:dependencies"));
415 // if no dependency node exists, create a new one in the current description info
416 if (!xCurrentDeps.is()) {
417 css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
418 xCurrentDescInfo->createElementNS(
419 "http://openoffice.org/extensions/description/2006",
420 "dependencies"), css::uno::UNO_QUERY_THROW);
421 m_element->appendChild(xNewDepNode);
422 xCurrentDeps = m_xpath->selectSingleNode(m_element, "desc:dependencies");
425 for (sal_Int32 i=0; i<nLen; i++) {
426 css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
427 css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
428 if (xDep.is()) {
429 // found valid denylist dependency, import the node first and append it to the existing dependency node
430 css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
431 xCurrentDeps->appendChild(importedNode);
436 bool DescriptionInfoset::checkDenylistVersion(
437 std::u16string_view currentversion,
438 css::uno::Sequence< OUString > const & versions)
440 sal_Int32 nLen = versions.getLength();
441 for (sal_Int32 i=0; i<nLen; i++) {
442 if (currentversion == versions[i])
443 return true;
446 return false;
449 OUString DescriptionInfoset::getVersion() const
451 return getNodeValueFromExpression( "desc:version/@value" );
454 css::uno::Sequence< OUString > DescriptionInfoset::getSupportedPlatforms() const
456 //When there is no description.xml then we assume that we support all platforms
457 if (! m_element.is())
459 return { OUString("all") };
462 //Check if the <platform> element was provided. If not the default is "all" platforms
463 css::uno::Reference< css::xml::dom::XNode > nodePlatform(
464 m_xpath->selectSingleNode(m_element, "desc:platform"));
465 if (!nodePlatform.is())
467 return { OUString("all") };
470 //There is a platform element.
471 const OUString value = getNodeValueFromExpression("desc:platform/@value");
472 //parse the string, it can contained multiple strings separated by commas
473 std::vector< OUString> vec;
474 sal_Int32 nIndex = 0;
477 const OUString aToken( o3tl::trim(o3tl::getToken(value, 0, ',', nIndex )) );
478 if (!aToken.isEmpty())
479 vec.push_back(aToken);
482 while (nIndex >= 0);
484 return comphelper::containerToSequence(vec);
487 css::uno::Reference< css::xml::dom::XNodeList >
488 DescriptionInfoset::getDependencies() const {
489 if (m_element.is()) {
490 try {
491 // check the extension denylist first and expand the dependencies if applicable
492 checkDenylist();
494 return m_xpath->selectNodeList(m_element, "desc:dependencies/*");
495 } catch (const css::xml::xpath::XPathException &) {
496 // ignore
499 return new EmptyNodeList;
502 css::uno::Sequence< OUString >
503 DescriptionInfoset::getUpdateInformationUrls() const {
504 return getUrls("desc:update-information/desc:src/@xlink:href");
507 css::uno::Sequence< OUString >
508 DescriptionInfoset::getUpdateDownloadUrls() const
510 return getUrls("desc:update-download/desc:src/@xlink:href");
513 OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
515 css::uno::Sequence< OUString > aStrList = getUrls( "desc:icon/desc:default/@xlink:href" );
516 css::uno::Sequence< OUString > aStrListHC = getUrls( "desc:icon/desc:high-contrast/@xlink:href" );
518 if ( bHighContrast && aStrListHC.hasElements() && !aStrListHC[0].isEmpty() )
519 return aStrListHC[0];
521 if ( aStrList.hasElements() && !aStrList[0].isEmpty() )
522 return aStrList[0];
524 return OUString();
527 ::std::optional< OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
528 const
530 bool bParentExists = false;
531 const OUString sURL (getLocalizedHREFAttrFromChild("/desc:description/desc:update-website", &bParentExists ));
533 if (!sURL.isEmpty())
534 return ::std::optional< OUString >(sURL);
535 else
536 return bParentExists ? ::std::optional< OUString >(OUString()) :
537 ::std::optional< OUString >();
540 ::std::optional< OUString > DescriptionInfoset::getOptionalValue(
541 OUString const & expression) const
543 css::uno::Reference< css::xml::dom::XNode > n;
544 if (m_element.is()) {
545 try {
546 n = m_xpath->selectSingleNode(m_element, expression);
547 } catch (const css::xml::xpath::XPathException &) {
548 // ignore
551 return n.is()
552 ? ::std::optional< OUString >(getNodeValue(n))
553 : ::std::optional< OUString >();
556 css::uno::Sequence< OUString > DescriptionInfoset::getUrls(
557 OUString const & expression) const
559 css::uno::Reference< css::xml::dom::XNodeList > ns;
560 if (m_element.is()) {
561 try {
562 ns = m_xpath->selectNodeList(m_element, expression);
563 } catch (const css::xml::xpath::XPathException &) {
564 // ignore
567 css::uno::Sequence< OUString > urls(ns.is() ? ns->getLength() : 0);
568 auto urlsRange = asNonConstRange(urls);
569 for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
570 urlsRange[i] = getNodeValue(ns->item(i));
572 return urls;
575 std::pair< OUString, OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
577 css::uno::Reference< css::xml::dom::XNode > node =
578 getLocalizedChild("desc:publisher");
580 OUString sPublisherName;
581 OUString sURL;
582 if (node.is())
584 css::uno::Reference< css::xml::dom::XNode > xPathName;
585 try {
586 xPathName = m_xpath->selectSingleNode(node, "text()");
587 } catch (const css::xml::xpath::XPathException &) {
588 // ignore
590 OSL_ASSERT(xPathName.is());
591 if (xPathName.is())
592 sPublisherName = xPathName->getNodeValue();
594 css::uno::Reference< css::xml::dom::XNode > xURL;
595 try {
596 xURL = m_xpath->selectSingleNode(node, "@xlink:href");
597 } catch (const css::xml::xpath::XPathException &) {
598 // ignore
600 OSL_ASSERT(xURL.is());
601 if (xURL.is())
602 sURL = xURL->getNodeValue();
604 return std::make_pair(sPublisherName, sURL);
607 OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
609 return getLocalizedHREFAttrFromChild("/desc:description/desc:release-notes", nullptr);
612 OUString DescriptionInfoset::getLocalizedDisplayName() const
614 css::uno::Reference< css::xml::dom::XNode > node =
615 getLocalizedChild("desc:display-name");
616 if (node.is())
618 css::uno::Reference< css::xml::dom::XNode > xtext;
619 try {
620 xtext = m_xpath->selectSingleNode(node, "text()");
621 } catch (const css::xml::xpath::XPathException &) {
622 // ignore
624 if (xtext.is())
625 return xtext->getNodeValue();
627 return OUString();
630 OUString DescriptionInfoset::getLocalizedLicenseURL() const
632 return getLocalizedHREFAttrFromChild("/desc:description/desc:registration/desc:simple-license", nullptr);
636 ::std::optional<SimpleLicenseAttributes>
637 DescriptionInfoset::getSimpleLicenseAttributes() const
639 //Check if the node exist
640 css::uno::Reference< css::xml::dom::XNode > n;
641 if (m_element.is()) {
642 try {
643 n = m_xpath->selectSingleNode(m_element, "/desc:description/desc:registration/desc:simple-license/@accept-by");
644 } catch (const css::xml::xpath::XPathException &) {
645 // ignore
647 if (n.is())
649 SimpleLicenseAttributes attributes;
650 attributes.acceptBy =
651 getNodeValueFromExpression("/desc:description/desc:registration/desc:simple-license/@accept-by");
653 ::std::optional< OUString > suppressOnUpdate = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-on-update");
654 if (suppressOnUpdate)
655 attributes.suppressOnUpdate = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressOnUpdate), u"true");
656 else
657 attributes.suppressOnUpdate = false;
659 ::std::optional< OUString > suppressIfRequired = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-if-required");
660 if (suppressIfRequired)
661 attributes.suppressIfRequired = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressIfRequired), u"true");
662 else
663 attributes.suppressIfRequired = false;
665 return ::std::optional<SimpleLicenseAttributes>(attributes);
668 return ::std::optional<SimpleLicenseAttributes>();
671 OUString DescriptionInfoset::getLocalizedDescriptionURL() const
673 return getLocalizedHREFAttrFromChild("/desc:description/desc:extension-description", nullptr);
676 css::uno::Reference< css::xml::dom::XNode >
677 DescriptionInfoset::getLocalizedChild( const OUString & sParent) const
679 if ( ! m_element.is() || sParent.isEmpty())
680 return css::uno::Reference< css::xml::dom::XNode > ();
682 css::uno::Reference< css::xml::dom::XNode > xParent;
683 try {
684 xParent = m_xpath->selectSingleNode(m_element, sParent);
685 } catch (const css::xml::xpath::XPathException &) {
686 // ignore
688 css::uno::Reference<css::xml::dom::XNode> nodeMatch;
689 if (xParent.is())
691 nodeMatch = matchLanguageTag(xParent, getOfficeLanguageTag().getBcp47());
693 //office: en-DE, en, en-DE-altmark
694 if (! nodeMatch.is())
696 // Already tried full tag, continue with first fallback.
697 const std::vector< OUString > aFallbacks( getOfficeLanguageTag().getFallbackStrings( false));
698 for (auto const& fallback : aFallbacks)
700 nodeMatch = matchLanguageTag(xParent, fallback);
701 if (nodeMatch.is())
702 break;
704 if (! nodeMatch.is())
705 nodeMatch = getChildWithDefaultLocale(xParent);
709 return nodeMatch;
712 css::uno::Reference<css::xml::dom::XNode>
713 DescriptionInfoset::matchLanguageTag(
714 css::uno::Reference< css::xml::dom::XNode > const & xParent, std::u16string_view rTag) const
716 OSL_ASSERT(xParent.is());
717 css::uno::Reference<css::xml::dom::XNode> nodeMatch;
719 //first try exact match for lang
720 const OUString exp1(OUString::Concat("*[@lang=\"") + rTag + "\"]");
721 try {
722 nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
723 } catch (const css::xml::xpath::XPathException &) {
724 // ignore
727 //try to match in strings that also have a country and/or variant, for
728 //example en matches in en-US-montana, en-US, en-montana
729 if (!nodeMatch.is())
731 const OUString exp2(
732 OUString::Concat("*[starts-with(@lang,\"") + rTag + "-\")]");
733 try {
734 nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
735 } catch (const css::xml::xpath::XPathException &) {
736 // ignore
739 return nodeMatch;
742 css::uno::Reference<css::xml::dom::XNode>
743 DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
744 const & xParent) const
746 OSL_ASSERT(xParent.is());
747 if ( xParent->getNodeName() == "simple-license" )
749 css::uno::Reference<css::xml::dom::XNode> nodeDefault;
750 try {
751 nodeDefault = m_xpath->selectSingleNode(xParent, "@default-license-id");
752 } catch (const css::xml::xpath::XPathException &) {
753 // ignore
755 if (nodeDefault.is())
757 //The old way
758 const OUString exp1("desc:license-text[@license-id = \""
759 + nodeDefault->getNodeValue()
760 + "\"]");
761 try {
762 return m_xpath->selectSingleNode(xParent, exp1);
763 } catch (const css::xml::xpath::XPathException &) {
764 // ignore
769 try {
770 return m_xpath->selectSingleNode(xParent, "*[1]");
771 } catch (const css::xml::xpath::XPathException &) {
772 // ignore
773 return nullptr;
777 OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
778 OUString const & sXPathParent, bool * out_bParentExists)
779 const
781 css::uno::Reference< css::xml::dom::XNode > node =
782 getLocalizedChild(sXPathParent);
784 OUString sURL;
785 if (node.is())
787 if (out_bParentExists)
788 *out_bParentExists = true;
789 css::uno::Reference< css::xml::dom::XNode > xURL;
790 try {
791 xURL = m_xpath->selectSingleNode(node, "@xlink:href");
792 } catch (const css::xml::xpath::XPathException &) {
793 // ignore
795 OSL_ASSERT(xURL.is());
796 if (xURL.is())
797 sURL = xURL->getNodeValue();
799 else
801 if (out_bParentExists)
802 *out_bParentExists = false;
804 return sURL;
809 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */