1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 
10 #include "WpftLoader.hxx"
11 
12 #include <com/sun/star/beans/PropertyValue.hpp>
13 #include <com/sun/star/container/NoSuchElementException.hpp>
14 #include <com/sun/star/container/XNameAccess.hpp>
15 #include <com/sun/star/document/XExtendedFilterDetection.hpp>
16 #include <com/sun/star/document/XFilter.hpp>
17 #include <com/sun/star/document/XImporter.hpp>
18 #include <com/sun/star/frame/XController.hpp>
19 #include <com/sun/star/frame/XDesktop2.hpp>
20 #include <com/sun/star/frame/XModel.hpp>
21 #include <com/sun/star/io/XInputStream.hpp>
22 #include <com/sun/star/lang/IllegalArgumentException.hpp>
23 #include <com/sun/star/lang/XComponent.hpp>
24 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
25 #include <com/sun/star/ucb/XContent.hpp>
26 #include <com/sun/star/uno/XComponentContext.hpp>
27 #include <com/sun/star/util/XCloseable.hpp>
28 
29 #include <ucbhelper/content.hxx>
30 
31 namespace beans = com::sun::star::beans;
32 namespace container = com::sun::star::container;
33 namespace document = com::sun::star::document;
34 namespace frame = com::sun::star::frame;
35 namespace lang = com::sun::star::lang;
36 namespace ucb = com::sun::star::ucb;
37 namespace uno = com::sun::star::uno;
38 namespace util = com::sun::star::util;
39 
40 namespace writerperfect::test
41 {
WpftLoader(const OUString & rURL,const css::uno::Reference<css::document::XFilter> & rxFilter,const OUString & rFactoryURL,const css::uno::Reference<css::frame::XDesktop2> & rxDesktop,const css::uno::Reference<css::container::XNameAccess> & rxTypeMap,const css::uno::Reference<css::uno::XComponentContext> & rxContext)42 WpftLoader::WpftLoader(const OUString& rURL,
43                        const css::uno::Reference<css::document::XFilter>& rxFilter,
44                        const OUString& rFactoryURL,
45                        const css::uno::Reference<css::frame::XDesktop2>& rxDesktop,
46                        const css::uno::Reference<css::container::XNameAccess>& rxTypeMap,
47                        const css::uno::Reference<css::uno::XComponentContext>& rxContext)
48     : m_aURL(rURL)
49     , m_aFactoryURL(rFactoryURL)
50     , m_xFilter(rxFilter)
51     , m_xDesktop(rxDesktop)
52     , m_xTypeMap(rxTypeMap)
53     , m_xContext(rxContext)
54 {
55     if (!impl_load())
56         impl_dispose();
57 }
58 
WpftLoader(const css::uno::Reference<css::io::XInputStream> & rxInputStream,const css::uno::Reference<css::document::XFilter> & rxFilter,const OUString & rFactoryURL,const css::uno::Reference<css::frame::XDesktop2> & rxDesktop,const css::uno::Reference<css::uno::XComponentContext> & rxContext)59 WpftLoader::WpftLoader(const css::uno::Reference<css::io::XInputStream>& rxInputStream,
60                        const css::uno::Reference<css::document::XFilter>& rxFilter,
61                        const OUString& rFactoryURL,
62                        const css::uno::Reference<css::frame::XDesktop2>& rxDesktop,
63                        const css::uno::Reference<css::uno::XComponentContext>& rxContext)
64     : m_xInputStream(rxInputStream)
65     , m_aFactoryURL(rFactoryURL)
66     , m_xFilter(rxFilter)
67     , m_xDesktop(rxDesktop)
68     , m_xContext(rxContext)
69 {
70     if (!impl_load())
71         impl_dispose();
72 }
73 
~WpftLoader()74 WpftLoader::~WpftLoader()
75 {
76     try
77     {
78         impl_dispose();
79     }
80     catch (...)
81     {
82     }
83 }
84 
getDocument() const85 const css::uno::Reference<css::lang::XComponent>& WpftLoader::getDocument() const { return m_xDoc; }
86 
impl_load()87 bool WpftLoader::impl_load()
88 {
89     // create an empty frame
90     m_xDoc.set(m_xDesktop->loadComponentFromURL(m_aFactoryURL, "_blank", 0,
91                                                 uno::Sequence<beans::PropertyValue>()),
92                uno::UNO_SET_THROW);
93 
94     // Find the model and frame. We need them later.
95     m_xFrame.set(m_xDoc, uno::UNO_QUERY);
96     uno::Reference<frame::XModel> xModel(m_xDoc, uno::UNO_QUERY);
97     uno::Reference<frame::XController> xController(m_xDoc, uno::UNO_QUERY);
98 
99     if (m_xFrame.is())
100     {
101         xController = m_xFrame->getController();
102         xModel = xController->getModel();
103     }
104     else if (xModel.is())
105     {
106         xController = xModel->getCurrentController();
107         m_xFrame = xController->getFrame();
108     }
109     else if (xController.is())
110     {
111         m_xFrame = xController->getFrame();
112         xModel = xController->getModel();
113     }
114 
115     if (!m_xFrame.is() || !xModel.is())
116         throw uno::RuntimeException();
117 
118     // try to import the document (and load it into the prepared frame)
119     try
120     {
121         const uno::Reference<document::XImporter> xImporter(m_xFilter, uno::UNO_QUERY_THROW);
122 
123         xImporter->setTargetDocument(m_xDoc);
124 
125         uno::Sequence<beans::PropertyValue> aDescriptor(3);
126         aDescriptor[0].Name = "URL";
127         aDescriptor[0].Value <<= m_aURL;
128         if (m_xInputStream.is())
129         {
130             aDescriptor[1].Name = "InputStream";
131             aDescriptor[1].Value <<= m_xInputStream;
132         }
133         else
134         {
135             ucbhelper::Content aContent(m_aURL, uno::Reference<ucb::XCommandEnvironment>(),
136                                         m_xContext);
137             aDescriptor[1].Name = "InputStream";
138             aDescriptor[1].Value <<= aContent.openStream();
139             aDescriptor[2].Name = "UCBContent";
140             aDescriptor[2].Value <<= aContent.get();
141         }
142 
143         const uno::Reference<document::XExtendedFilterDetection> xDetector(m_xFilter,
144                                                                            uno::UNO_QUERY_THROW);
145 
146         const OUString aTypeName(xDetector->detect(aDescriptor));
147         if (aTypeName.isEmpty())
148             throw lang::IllegalArgumentException();
149 
150         if (m_xTypeMap.is())
151             impl_detectFilterName(aDescriptor, aTypeName);
152 
153         xModel->lockControllers();
154         const bool bLoaded = m_xFilter->filter(aDescriptor);
155         xModel->unlockControllers();
156         return bLoaded;
157     }
158     catch (const uno::Exception&)
159     {
160         // ignore
161     }
162 
163     return false;
164 }
165 
impl_dispose()166 void WpftLoader::impl_dispose()
167 {
168     // close the opened document
169     uno::Reference<util::XCloseable> xCloseable(m_xFrame, uno::UNO_QUERY);
170     if (xCloseable.is())
171         xCloseable->close(true);
172     else if (m_xDoc.is())
173         m_xDoc->dispose();
174     m_xDoc.clear();
175     m_xFrame.clear();
176 }
177 
impl_detectFilterName(uno::Sequence<beans::PropertyValue> & rDescriptor,const OUString & rTypeName)178 void WpftLoader::impl_detectFilterName(uno::Sequence<beans::PropertyValue>& rDescriptor,
179                                        const OUString& rTypeName)
180 {
181     bool bHasFilterName
182         = std::any_of(rDescriptor.begin(), rDescriptor.end(),
183                       [](const beans::PropertyValue& rProp) { return "FilterName" == rProp.Name; });
184     if (bHasFilterName)
185         return;
186 
187     uno::Sequence<beans::PropertyValue> aTypes;
188     if (m_xTypeMap->getByName(rTypeName) >>= aTypes)
189     {
190         for (const auto& rType : std::as_const(aTypes))
191         {
192             OUString aFilterName;
193             if (("PreferredFilter" == rType.Name) && (rType.Value >>= aFilterName))
194             {
195                 const sal_Int32 nDescriptorLen = rDescriptor.getLength();
196                 rDescriptor.realloc(nDescriptorLen + 1);
197                 rDescriptor[nDescriptorLen].Name = "FilterName";
198                 rDescriptor[nDescriptorLen].Value <<= aFilterName;
199                 return;
200             }
201         }
202     }
203 
204     throw container::NoSuchElementException();
205 }
206 }
207 
208 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
209