Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / desktop / source / deployment / misc / dp_descriptioninfoset.cxx
blob3e10663ef8db3360f3bda6ba3694e8dae1618947
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 <dp_descriptioninfoset.hxx>
22 #include <dp_resource.h>
23 #include <sal/config.h>
25 #include <comphelper/sequence.hxx>
26 #include <comphelper/seqstream.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/propertysequence.hxx>
29 #include <boost/optional.hpp>
30 #include <com/sun/star/configuration/theDefaultProvider.hpp>
31 #include <com/sun/star/container/XNameAccess.hpp>
32 #include <com/sun/star/deployment/DeploymentException.hpp>
33 #include <com/sun/star/beans/Optional.hpp>
34 #include <com/sun/star/beans/PropertyValue.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/uno/Reference.hxx>
40 #include <com/sun/star/uno/RuntimeException.hpp>
41 #include <com/sun/star/uno/Sequence.hxx>
42 #include <com/sun/star/uno/XComponentContext.hpp>
43 #include <com/sun/star/uno/XInterface.hpp>
44 #include <com/sun/star/xml/dom/DOMException.hpp>
45 #include <com/sun/star/xml/dom/XNode.hpp>
46 #include <com/sun/star/xml/dom/XNodeList.hpp>
47 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
48 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
49 #include <com/sun/star/xml/xpath/XPathException.hpp>
50 #include <com/sun/star/ucb/InteractiveIOException.hpp>
51 #include <cppuhelper/implbase.hxx>
52 #include <cppuhelper/weak.hxx>
53 #include <cppuhelper/exc_hlp.hxx>
54 #include <rtl/ustring.h>
55 #include <rtl/ustring.hxx>
56 #include <sal/types.h>
57 #include <ucbhelper/content.hxx>
59 namespace {
61 using css::uno::Reference;
63 class EmptyNodeList:
64 public cppu::WeakImplHelper<css::xml::dom::XNodeList>
66 public:
67 EmptyNodeList();
69 EmptyNodeList(const EmptyNodeList&) = delete;
70 const EmptyNodeList& operator=(const EmptyNodeList&) = delete;
72 virtual ::sal_Int32 SAL_CALL getLength() override;
74 virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
75 item(::sal_Int32 index) override;
78 EmptyNodeList::EmptyNodeList() {}
80 ::sal_Int32 EmptyNodeList::getLength() {
81 return 0;
84 css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
86 throw css::uno::RuntimeException("bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call",
87 static_cast< ::cppu::OWeakObject * >(this));
90 OUString getNodeValue(
91 css::uno::Reference< css::xml::dom::XNode > const & node)
93 OSL_ASSERT(node.is());
94 try {
95 return node->getNodeValue();
96 } catch (const css::xml::dom::DOMException & e) {
97 css::uno::Any anyEx = cppu::getCaughtException();
98 throw css::lang::WrappedTargetRuntimeException(
99 "com.sun.star.xml.dom.DOMException: " + e.Message,
100 nullptr, anyEx );
104 /**The class uses the UCB to access the description.xml file in an
105 extension. The UCB must have been initialized already. It also
106 requires that the extension has already be unzipped to a particular
107 location.
109 class ExtensionDescription
111 public:
112 /**throws an exception if the description.xml is not
113 available, cannot be read, does not contain the expected data,
114 or any other error occurred. Therefore it should only be used with
115 new extensions.
117 Throws css::uno::RuntimeException,
118 css::deployment::DeploymentException,
119 dp_registry::backend::bundle::NoDescriptionException.
121 ExtensionDescription(
122 const css::uno::Reference<css::uno::XComponentContext>& xContext,
123 const OUString& installDir,
124 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
126 const css::uno::Reference<css::xml::dom::XNode>& getRootElement() const
128 return m_xRoot;
131 private:
132 css::uno::Reference<css::xml::dom::XNode> m_xRoot;
135 class NoDescriptionException
139 class FileDoesNotExistFilter
140 : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
141 css::task::XInteractionHandler >
144 bool m_bExist;
145 css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
147 public:
148 explicit FileDoesNotExistFilter(
149 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
151 bool exist() { return m_bExist;}
152 // XCommandEnvironment
153 virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
154 getInteractionHandler() override;
155 virtual css::uno::Reference<css::ucb::XProgressHandler >
156 SAL_CALL getProgressHandler() override;
158 // XInteractionHandler
159 virtual void SAL_CALL handle(
160 css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
163 ExtensionDescription::ExtensionDescription(
164 const Reference<css::uno::XComponentContext>& xContext,
165 const OUString& installDir,
166 const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
168 try {
169 //may throw css::ucb::ContentCreationException
170 //If there is no description.xml then ucb will start an interaction which
171 //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
172 //and filter the respective exception out.
173 OUString sDescriptionUri(installDir + "/description.xml");
174 Reference<css::ucb::XCommandEnvironment> xFilter = new FileDoesNotExistFilter(xCmdEnv);
175 ::ucbhelper::Content descContent(sDescriptionUri, xFilter, xContext);
177 //throws a css::uno::Exception if the file is not available
178 Reference<css::io::XInputStream> xIn;
180 { //throws com.sun.star.ucb.InteractiveIOException
181 xIn = descContent.openStream();
183 catch ( const css::uno::Exception& )
185 if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
186 throw NoDescriptionException();
187 throw;
189 if (!xIn.is())
191 throw css::uno::Exception(
192 "Could not get XInputStream for description.xml of extension " +
193 sDescriptionUri, nullptr);
196 //get root node of description.xml
197 Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
198 css::xml::dom::DocumentBuilder::create(xContext) );
200 if (!xDocBuilder->isNamespaceAware())
202 throw css::uno::Exception(
203 "Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware.", nullptr);
206 Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
207 if (!xDoc.is())
209 throw css::uno::Exception(sDescriptionUri + " contains data which cannot be parsed. ", nullptr);
212 //check for proper root element and namespace
213 Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
214 if (!xRoot.is())
216 throw css::uno::Exception(
217 sDescriptionUri + " contains no root element.", nullptr);
220 if ( xRoot->getTagName() != "description")
222 throw css::uno::Exception(
223 sDescriptionUri + " does not contain the root element <description>.", nullptr);
226 m_xRoot.set(xRoot, css::uno::UNO_QUERY_THROW);
227 OUString nsDescription = xRoot->getNamespaceURI();
229 //check if this namespace is supported
230 if ( nsDescription != "http://openoffice.org/extensions/description/2006")
232 throw css::uno::Exception(sDescriptionUri + " contains a root element with an unsupported namespace. ", nullptr);
234 } catch (const css::uno::RuntimeException &) {
235 throw;
236 } catch (const css::deployment::DeploymentException &) {
237 throw;
238 } catch (const css::uno::Exception & e) {
239 css::uno::Any a(cppu::getCaughtException());
240 throw css::deployment::DeploymentException(
241 e.Message, Reference< css::uno::XInterface >(), a);
245 FileDoesNotExistFilter::FileDoesNotExistFilter(
246 const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
247 m_bExist(true), m_xCommandEnv(xCmdEnv)
250 // XCommandEnvironment
251 Reference<css::task::XInteractionHandler >
252 FileDoesNotExistFilter::getInteractionHandler()
254 return static_cast<css::task::XInteractionHandler*>(this);
257 Reference<css::ucb::XProgressHandler >
258 FileDoesNotExistFilter::getProgressHandler()
260 return m_xCommandEnv.is()
261 ? m_xCommandEnv->getProgressHandler()
262 : Reference<css::ucb::XProgressHandler>();
265 // XInteractionHandler
266 //If the interaction was caused by a non-existing file which is specified in the ctor
267 //of FileDoesNotExistFilter, then we do nothing
268 void FileDoesNotExistFilter::handle(
269 Reference<css::task::XInteractionRequest > const & xRequest )
271 css::uno::Any request( xRequest->getRequest() );
273 css::ucb::InteractiveIOException ioexc;
274 if ((request>>= ioexc)
275 && (ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING
276 || ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING_PATH))
278 m_bExist = false;
279 return;
281 Reference<css::task::XInteractionHandler> xInteraction;
282 if (m_xCommandEnv.is()) {
283 xInteraction = m_xCommandEnv->getInteractionHandler();
285 if (xInteraction.is()) {
286 xInteraction->handle(xRequest);
292 namespace dp_misc {
294 DescriptionInfoset getDescriptionInfoset(OUString const & sExtensionFolderURL)
296 Reference< css::xml::dom::XNode > root;
297 Reference<css::uno::XComponentContext> context(
298 comphelper::getProcessComponentContext());
299 try {
300 root =
301 ExtensionDescription(
302 context, sExtensionFolderURL,
303 Reference< css::ucb::XCommandEnvironment >()).
304 getRootElement();
305 } catch (const NoDescriptionException &) {
306 } catch (const css::deployment::DeploymentException & e) {
307 css::uno::Any anyEx = cppu::getCaughtException();
308 throw css::lang::WrappedTargetRuntimeException(
309 "com.sun.star.deployment.DeploymentException: " + e.Message,
310 nullptr, anyEx );
312 return DescriptionInfoset(context, root);
315 DescriptionInfoset::DescriptionInfoset(
316 css::uno::Reference< css::uno::XComponentContext > const & context,
317 css::uno::Reference< css::xml::dom::XNode > const & element):
318 m_context(context),
319 m_element(element)
321 if (m_element.is()) {
322 m_xpath = css::xml::xpath::XPathAPI::create(context);
323 m_xpath->registerNS("desc", element->getNamespaceURI());
324 m_xpath->registerNS("xlink", "http://www.w3.org/1999/xlink");
328 DescriptionInfoset::~DescriptionInfoset() {}
330 ::boost::optional< OUString > DescriptionInfoset::getIdentifier() const {
331 return getOptionalValue("desc:identifier/@value");
334 OUString DescriptionInfoset::getNodeValueFromExpression(OUString const & expression) const
336 css::uno::Reference< css::xml::dom::XNode > n;
337 if (m_element.is()) {
338 try {
339 n = m_xpath->selectSingleNode(m_element, expression);
340 } catch (const css::xml::xpath::XPathException &) {
341 // ignore
344 return n.is() ? getNodeValue(n) : OUString();
347 void DescriptionInfoset::checkBlacklist() const
349 if (m_element.is()) {
350 boost::optional< OUString > id(getIdentifier());
351 if (!id)
352 return; // nothing to check
353 OUString currentversion(getVersion());
354 if (currentversion.getLength() == 0)
355 return; // nothing to check
357 css::uno::Sequence<css::uno::Any> args(comphelper::InitAnyPropertySequence(
359 {"nodepath", css::uno::Any(OUString("/org.openoffice.Office.ExtensionDependencies/Extensions"))}
360 }));
361 css::uno::Reference< css::container::XNameAccess > blacklist(
362 (css::configuration::theDefaultProvider::get(m_context)
363 ->createInstanceWithArguments(
364 "com.sun.star.configuration.ConfigurationAccess", args)),
365 css::uno::UNO_QUERY_THROW);
367 // check first if a blacklist entry is available
368 if (blacklist.is() && blacklist->hasByName(*id)) {
369 css::uno::Reference< css::beans::XPropertySet > extProps(
370 blacklist->getByName(*id), css::uno::UNO_QUERY_THROW);
372 css::uno::Any anyValue = extProps->getPropertyValue("Versions");
374 css::uno::Sequence< OUString > blversions;
375 anyValue >>= blversions;
377 // check if the current version requires further dependency checks from the blacklist
378 if (checkBlacklistVersion(currentversion, blversions)) {
379 anyValue = extProps->getPropertyValue("Dependencies");
380 OUString udeps;
381 anyValue >>= udeps;
383 if (udeps.getLength() == 0)
384 return; // nothing todo
386 OString xmlDependencies = OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
388 css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
389 m_context->getServiceManager()->createInstanceWithContext("com.sun.star.xml.dom.DocumentBuilder", m_context),
390 css::uno::UNO_QUERY_THROW);
392 css::uno::Sequence< sal_Int8 > byteSeq(reinterpret_cast<const sal_Int8*>(xmlDependencies.getStr()), xmlDependencies.getLength());
394 css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
395 css::uno::UNO_QUERY_THROW);
397 css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
398 css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
399 css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
400 sal_Int32 nLen = xDeps->getLength();
402 // get the parent xml document of current description info for the import
403 css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
405 // get dependency node of current description info to merge the new dependencies from the blacklist
406 css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
407 m_xpath->selectSingleNode(m_element, "desc:dependencies"));
409 // if no dependency node exists, create a new one in the current description info
410 if (!xCurrentDeps.is()) {
411 css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
412 xCurrentDescInfo->createElementNS(
413 "http://openoffice.org/extensions/description/2006",
414 "dependencies"), css::uno::UNO_QUERY_THROW);
415 m_element->appendChild(xNewDepNode);
416 xCurrentDeps = m_xpath->selectSingleNode(m_element, "desc:dependencies");
419 for (sal_Int32 i=0; i<nLen; i++) {
420 css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
421 css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
422 if (xDep.is()) {
423 // found valid blacklist dependency, import the node first and append it to the existing dependency node
424 css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
425 xCurrentDeps->appendChild(importedNode);
433 bool DescriptionInfoset::checkBlacklistVersion(
434 const OUString& currentversion,
435 css::uno::Sequence< OUString > const & versions)
437 sal_Int32 nLen = versions.getLength();
438 for (sal_Int32 i=0; i<nLen; i++) {
439 if (currentversion == versions[i])
440 return true;
443 return false;
446 OUString DescriptionInfoset::getVersion() const
448 return getNodeValueFromExpression( "desc:version/@value" );
451 css::uno::Sequence< OUString > DescriptionInfoset::getSupportedPlatforms() const
453 //When there is no description.xml then we assume that we support all platforms
454 if (! m_element.is())
456 return { OUString("all") };
459 //Check if the <platform> element was provided. If not the default is "all" platforms
460 css::uno::Reference< css::xml::dom::XNode > nodePlatform(
461 m_xpath->selectSingleNode(m_element, "desc:platform"));
462 if (!nodePlatform.is())
464 return { OUString("all") };
467 //There is a platform element.
468 const OUString value = getNodeValueFromExpression("desc:platform/@value");
469 //parse the string, it can contained multiple strings separated by commas
470 std::vector< OUString> vec;
471 sal_Int32 nIndex = 0;
474 const OUString aToken = value.getToken( 0, ',', nIndex ).trim();
475 if (!aToken.isEmpty())
476 vec.push_back(aToken);
479 while (nIndex >= 0);
481 return comphelper::containerToSequence(vec);
484 css::uno::Reference< css::xml::dom::XNodeList >
485 DescriptionInfoset::getDependencies() const {
486 if (m_element.is()) {
487 try {
488 // check the extension blacklist first and expand the dependencies if applicable
489 checkBlacklist();
491 return m_xpath->selectNodeList(m_element, "desc:dependencies/*");
492 } catch (const css::xml::xpath::XPathException &) {
493 // ignore
496 return new EmptyNodeList;
499 css::uno::Sequence< OUString >
500 DescriptionInfoset::getUpdateInformationUrls() const {
501 return getUrls("desc:update-information/desc:src/@xlink:href");
504 css::uno::Sequence< OUString >
505 DescriptionInfoset::getUpdateDownloadUrls() const
507 return getUrls("desc:update-download/desc:src/@xlink:href");
510 OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
512 css::uno::Sequence< OUString > aStrList = getUrls( "desc:icon/desc:default/@xlink:href" );
513 css::uno::Sequence< OUString > aStrListHC = getUrls( "desc:icon/desc:high-contrast/@xlink:href" );
515 if ( bHighContrast && aStrListHC.hasElements() && !aStrListHC[0].isEmpty() )
516 return aStrListHC[0];
518 if ( aStrList.hasElements() && !aStrList[0].isEmpty() )
519 return aStrList[0];
521 return OUString();
524 ::boost::optional< OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
525 const
527 bool bParentExists = false;
528 const OUString sURL (getLocalizedHREFAttrFromChild("/desc:description/desc:update-website", &bParentExists ));
530 if (!sURL.isEmpty())
531 return ::boost::optional< OUString >(sURL);
532 else
533 return bParentExists ? ::boost::optional< OUString >(OUString()) :
534 ::boost::optional< OUString >();
537 ::boost::optional< OUString > DescriptionInfoset::getOptionalValue(
538 OUString const & expression) const
540 css::uno::Reference< css::xml::dom::XNode > n;
541 if (m_element.is()) {
542 try {
543 n = m_xpath->selectSingleNode(m_element, expression);
544 } catch (const css::xml::xpath::XPathException &) {
545 // ignore
548 return n.is()
549 ? ::boost::optional< OUString >(getNodeValue(n))
550 : ::boost::optional< OUString >();
553 css::uno::Sequence< OUString > DescriptionInfoset::getUrls(
554 OUString const & expression) const
556 css::uno::Reference< css::xml::dom::XNodeList > ns;
557 if (m_element.is()) {
558 try {
559 ns = m_xpath->selectNodeList(m_element, expression);
560 } catch (const css::xml::xpath::XPathException &) {
561 // ignore
564 css::uno::Sequence< OUString > urls(ns.is() ? ns->getLength() : 0);
565 for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
566 urls[i] = getNodeValue(ns->item(i));
568 return urls;
571 std::pair< OUString, OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
573 css::uno::Reference< css::xml::dom::XNode > node =
574 getLocalizedChild("desc:publisher");
576 OUString sPublisherName;
577 OUString sURL;
578 if (node.is())
580 const OUString exp1("text()");
581 css::uno::Reference< css::xml::dom::XNode > xPathName;
582 try {
583 xPathName = m_xpath->selectSingleNode(node, exp1);
584 } catch (const css::xml::xpath::XPathException &) {
585 // ignore
587 OSL_ASSERT(xPathName.is());
588 if (xPathName.is())
589 sPublisherName = xPathName->getNodeValue();
591 const OUString exp2("@xlink:href");
592 css::uno::Reference< css::xml::dom::XNode > xURL;
593 try {
594 xURL = m_xpath->selectSingleNode(node, exp2);
595 } catch (const css::xml::xpath::XPathException &) {
596 // ignore
598 OSL_ASSERT(xURL.is());
599 if (xURL.is())
600 sURL = xURL->getNodeValue();
602 return std::make_pair(sPublisherName, sURL);
605 OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
607 return getLocalizedHREFAttrFromChild("/desc:description/desc:release-notes", nullptr);
610 OUString DescriptionInfoset::getLocalizedDisplayName() const
612 css::uno::Reference< css::xml::dom::XNode > node =
613 getLocalizedChild("desc:display-name");
614 if (node.is())
616 const OUString exp("text()");
617 css::uno::Reference< css::xml::dom::XNode > xtext;
618 try {
619 xtext = m_xpath->selectSingleNode(node, exp);
620 } catch (const css::xml::xpath::XPathException &) {
621 // ignore
623 if (xtext.is())
624 return xtext->getNodeValue();
626 return OUString();
629 OUString DescriptionInfoset::getLocalizedLicenseURL() const
631 return getLocalizedHREFAttrFromChild("/desc:description/desc:registration/desc:simple-license", nullptr);
635 ::boost::optional<SimpleLicenseAttributes>
636 DescriptionInfoset::getSimpleLicenseAttributes() const
638 //Check if the node exist
639 css::uno::Reference< css::xml::dom::XNode > n;
640 if (m_element.is()) {
641 try {
642 n = m_xpath->selectSingleNode(m_element, "/desc:description/desc:registration/desc:simple-license/@accept-by");
643 } catch (const css::xml::xpath::XPathException &) {
644 // ignore
646 if (n.is())
648 SimpleLicenseAttributes attributes;
649 attributes.acceptBy =
650 getNodeValueFromExpression("/desc:description/desc:registration/desc:simple-license/@accept-by");
652 ::boost::optional< OUString > suppressOnUpdate = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-on-update");
653 if (suppressOnUpdate)
654 attributes.suppressOnUpdate = (*suppressOnUpdate).trim().equalsIgnoreAsciiCase("true");
655 else
656 attributes.suppressOnUpdate = false;
658 ::boost::optional< OUString > suppressIfRequired = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-if-required");
659 if (suppressIfRequired)
660 attributes.suppressIfRequired = (*suppressIfRequired).trim().equalsIgnoreAsciiCase("true");
661 else
662 attributes.suppressIfRequired = false;
664 return ::boost::optional<SimpleLicenseAttributes>(attributes);
667 return ::boost::optional<SimpleLicenseAttributes>();
670 OUString DescriptionInfoset::getLocalizedDescriptionURL() const
672 return getLocalizedHREFAttrFromChild("/desc:description/desc:extension-description", nullptr);
675 css::uno::Reference< css::xml::dom::XNode >
676 DescriptionInfoset::getLocalizedChild( const OUString & sParent) const
678 if ( ! m_element.is() || sParent.isEmpty())
679 return css::uno::Reference< css::xml::dom::XNode > ();
681 css::uno::Reference< css::xml::dom::XNode > xParent;
682 try {
683 xParent = m_xpath->selectSingleNode(m_element, sParent);
684 } catch (const css::xml::xpath::XPathException &) {
685 // ignore
687 css::uno::Reference<css::xml::dom::XNode> nodeMatch;
688 if (xParent.is())
690 nodeMatch = matchLanguageTag(xParent, getOfficeLanguageTag().getBcp47());
692 //office: en-DE, en, en-DE-altmark
693 if (! nodeMatch.is())
695 // Already tried full tag, continue with first fallback.
696 const std::vector< OUString > aFallbacks( getOfficeLanguageTag().getFallbackStrings( false));
697 for (auto const& fallback : aFallbacks)
699 nodeMatch = matchLanguageTag(xParent, fallback);
700 if (nodeMatch.is())
701 break;
703 if (! nodeMatch.is())
704 nodeMatch = getChildWithDefaultLocale(xParent);
708 return nodeMatch;
711 css::uno::Reference<css::xml::dom::XNode>
712 DescriptionInfoset::matchLanguageTag(
713 css::uno::Reference< css::xml::dom::XNode > const & xParent, OUString const & rTag) const
715 OSL_ASSERT(xParent.is());
716 css::uno::Reference<css::xml::dom::XNode> nodeMatch;
718 //first try exact match for lang
719 const OUString exp1("*[@lang=\"" + rTag + "\"]");
720 try {
721 nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
722 } catch (const css::xml::xpath::XPathException &) {
723 // ignore
726 //try to match in strings that also have a country and/or variant, for
727 //example en matches in en-US-montana, en-US, en-montana
728 if (!nodeMatch.is())
730 const OUString exp2(
731 "*[starts-with(@lang,\"" + rTag + "-\")]");
732 try {
733 nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
734 } catch (const css::xml::xpath::XPathException &) {
735 // ignore
738 return nodeMatch;
741 css::uno::Reference<css::xml::dom::XNode>
742 DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
743 const & xParent) const
745 OSL_ASSERT(xParent.is());
746 if ( xParent->getNodeName() == "simple-license" )
748 css::uno::Reference<css::xml::dom::XNode> nodeDefault;
749 try {
750 nodeDefault = m_xpath->selectSingleNode(xParent, "@default-license-id");
751 } catch (const css::xml::xpath::XPathException &) {
752 // ignore
754 if (nodeDefault.is())
756 //The old way
757 const OUString exp1("desc:license-text[@license-id = \""
758 + nodeDefault->getNodeValue()
759 + "\"]");
760 try {
761 return m_xpath->selectSingleNode(xParent, exp1);
762 } catch (const css::xml::xpath::XPathException &) {
763 // ignore
768 const OUString exp2("*[1]");
769 try {
770 return m_xpath->selectSingleNode(xParent, exp2);
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 const OUString exp("@xlink:href");
790 css::uno::Reference< css::xml::dom::XNode > xURL;
791 try {
792 xURL = m_xpath->selectSingleNode(node, exp);
793 } catch (const css::xml::xpath::XPathException &) {
794 // ignore
796 OSL_ASSERT(xURL.is());
797 if (xURL.is())
798 sURL = xURL->getNodeValue();
800 else
802 if (out_bParentExists)
803 *out_bParentExists = false;
805 return sURL;
810 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */