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  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <framework/ResourceId.hxx>
21 #include <tools/SdGlobalResourceContainer.hxx>
22 #include <com/sun/star/util/URLTransformer.hpp>
23 #include <comphelper/processfactory.hxx>
24 #include <cppuhelper/supportsservice.hxx>
25 #include <cppuhelper/weakref.hxx>
26 #include <rtl/ref.hxx>
27 
28 namespace com::sun::star::uno { class XComponentContext; }
29 
30 using namespace ::com::sun::star;
31 using namespace ::com::sun::star::uno;
32 using namespace ::com::sun::star::lang;
33 using namespace ::com::sun::star::drawing::framework;
34 
35 /** When the USE_OPTIMIZATIONS symbol is defined then at some optimizations
36     are activated that work only together with XResourceId objects that are
37     implemented by the ResourceId class.  For other implementations of when
38     the USE_OPTIMIZATIONS symbol is not defined then alternative code is
39     used instead.
40 */
41 #define USE_OPTIMIZATIONS
42 
43 namespace sd::framework {
44 
45 //===== ResourceId ============================================================
46 
47 WeakReference<util::XURLTransformer> ResourceId::mxURLTransformerWeak;
48 
ResourceId()49 ResourceId::ResourceId()
50     : ResourceIdInterfaceBase(),
51       maResourceURLs(0),
52       mpURL()
53 {
54 }
55 
ResourceId(const std::vector<OUString> & rResourceURLs)56 ResourceId::ResourceId (
57     const std::vector<OUString>& rResourceURLs)
58     : ResourceIdInterfaceBase(),
59       maResourceURLs(rResourceURLs),
60       mpURL()
61 {
62     ParseResourceURL();
63 }
64 
ResourceId(const OUString & rsResourceURL)65 ResourceId::ResourceId (
66     const OUString& rsResourceURL)
67     : ResourceIdInterfaceBase(),
68       maResourceURLs(1, rsResourceURL),
69       mpURL()
70 {
71     // Handle the special case of an empty resource URL.
72     if (rsResourceURL.isEmpty())
73         maResourceURLs.clear();
74     ParseResourceURL();
75 }
76 
ResourceId(const OUString & rsResourceURL,const OUString & rsAnchorURL)77 ResourceId::ResourceId (
78     const OUString& rsResourceURL,
79     const OUString& rsAnchorURL)
80     : ResourceIdInterfaceBase(),
81       maResourceURLs(2),
82       mpURL()
83 {
84     maResourceURLs[0] = rsResourceURL;
85     maResourceURLs[1] = rsAnchorURL;
86     ParseResourceURL();
87 }
88 
ResourceId(const OUString & rsResourceURL,const OUString & rsFirstAnchorURL,const Sequence<OUString> & rAnchorURLs)89 ResourceId::ResourceId (
90     const OUString& rsResourceURL,
91     const OUString& rsFirstAnchorURL,
92     const Sequence<OUString>& rAnchorURLs)
93     : ResourceIdInterfaceBase(),
94       maResourceURLs(2+rAnchorURLs.getLength()),
95       mpURL()
96 {
97     maResourceURLs[0] = rsResourceURL;
98     maResourceURLs[1] = rsFirstAnchorURL;
99     std::copy(rAnchorURLs.begin(), rAnchorURLs.end(), std::next(maResourceURLs.begin(), 2));
100     ParseResourceURL();
101 }
102 
~ResourceId()103 ResourceId::~ResourceId()
104 {
105     mpURL.reset();
106 }
107 
108 OUString SAL_CALL
getResourceURL()109     ResourceId::getResourceURL()
110 {
111     if (!maResourceURLs.empty())
112         return maResourceURLs[0];
113     else
114         return OUString();
115 }
116 
117 util::URL SAL_CALL
getFullResourceURL()118     ResourceId::getFullResourceURL()
119 {
120     if (mpURL != nullptr)
121         return *mpURL;
122 
123     Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak);
124     if (xURLTransformer.is() && !maResourceURLs.empty() )
125     {
126         mpURL.reset(new util::URL);
127         mpURL->Complete = maResourceURLs[0];
128         xURLTransformer->parseStrict(*mpURL);
129         return *mpURL;
130     }
131 
132     util::URL aURL;
133     if (!maResourceURLs.empty())
134         aURL.Complete = maResourceURLs[0];
135     return aURL;
136 }
137 
138 sal_Bool SAL_CALL
hasAnchor()139     ResourceId::hasAnchor()
140 {
141     return maResourceURLs.size()>1;
142 }
143 
144 Reference<XResourceId> SAL_CALL
getAnchor()145     ResourceId::getAnchor()
146 {
147     ::rtl::Reference<ResourceId> rResourceId (new ResourceId());
148     const sal_Int32 nAnchorCount (maResourceURLs.size()-1);
149     if (nAnchorCount > 0)
150     {
151         rResourceId->maResourceURLs.resize(nAnchorCount);
152         for (sal_Int32 nIndex=0; nIndex<nAnchorCount; ++nIndex)
153             rResourceId->maResourceURLs[nIndex] = maResourceURLs[nIndex+1];
154     }
155     return rResourceId;
156 }
157 
158 Sequence<OUString> SAL_CALL
getAnchorURLs()159     ResourceId::getAnchorURLs()
160 {
161     const sal_Int32 nAnchorCount (maResourceURLs.size() - 1);
162     if (nAnchorCount > 0)
163     {
164         Sequence<OUString> aAnchorURLs (nAnchorCount);
165         for (sal_Int32 nIndex=0; nIndex<nAnchorCount; ++nIndex)
166             aAnchorURLs[nIndex] = maResourceURLs[nIndex+1];
167         return aAnchorURLs;
168     }
169     else
170         return Sequence<OUString>();
171 }
172 
173 OUString SAL_CALL
getResourceTypePrefix()174     ResourceId::getResourceTypePrefix()
175 {
176     if (!maResourceURLs.empty() )
177     {
178         // Return the "private:resource/<type>/" prefix.
179 
180         // Get the prefix that ends with the second "/".
181         const OUString& rsResourceURL (maResourceURLs[0]);
182         sal_Int32 nPrefixEnd (rsResourceURL.indexOf('/'));
183         if (nPrefixEnd >= 0)
184             nPrefixEnd = rsResourceURL.indexOf('/', nPrefixEnd+1) + 1;
185         else
186             nPrefixEnd = 0;
187 
188         return rsResourceURL.copy(0,nPrefixEnd);
189     }
190     else
191         return OUString();
192 }
193 
194 sal_Int16 SAL_CALL
compareTo(const Reference<XResourceId> & rxResourceId)195     ResourceId::compareTo (const Reference<XResourceId>& rxResourceId)
196 {
197     sal_Int16 nResult (0);
198 
199     if ( ! rxResourceId.is())
200     {
201         // The empty reference is interpreted as empty resource id object.
202         if (!maResourceURLs.empty())
203             nResult = +1;
204         else
205             nResult = 0;
206     }
207     else
208     {
209         ResourceId* pId = nullptr;
210 #ifdef USE_OPTIMIZATIONS
211         pId = dynamic_cast<ResourceId*>(rxResourceId.get());
212 #endif
213         if (pId != nullptr)
214         {
215             // We have direct access to the implementation of the given
216             // resource id object.
217             nResult = CompareToLocalImplementation(*pId);
218         }
219         else
220         {
221             // We have to do the comparison via the UNO interface of the
222             // given resource id object.
223             nResult = CompareToExternalImplementation(rxResourceId);
224         }
225     }
226 
227     return nResult;
228 }
229 
CompareToLocalImplementation(const ResourceId & rId) const230 sal_Int16 ResourceId::CompareToLocalImplementation (const ResourceId& rId) const
231 {
232     sal_Int16 nResult (0);
233 
234     const sal_uInt32 nLocalURLCount (maResourceURLs.size());
235     const sal_uInt32 nURLCount(rId.maResourceURLs.size());
236 
237     // Start comparison with the top most anchors.
238     for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1;
239          nIndex>=0 && nLocalIndex>=0;
240          --nIndex,--nLocalIndex)
241     {
242         const OUString sLocalURL (maResourceURLs[nLocalIndex]);
243         const OUString sURL (rId.maResourceURLs[nIndex]);
244         const sal_Int32 nLocalResult (sURL.compareTo(sLocalURL));
245         if (nLocalResult != 0)
246         {
247             if (nLocalResult < 0)
248                 nResult = -1;
249             else
250                 nResult = +1;
251             break;
252         }
253     }
254 
255     if (nResult == 0)
256     {
257         // No difference found yet.  When the lengths are the same then the
258         // two resource ids are equivalent.  Otherwise the shorter comes
259         // first.
260         if (nLocalURLCount != nURLCount)
261         {
262             if (nLocalURLCount < nURLCount)
263                 nResult = -1;
264             else
265                 nResult = +1;
266         }
267     }
268 
269     return nResult;
270 }
271 
CompareToExternalImplementation(const Reference<XResourceId> & rxId) const272 sal_Int16 ResourceId::CompareToExternalImplementation (const Reference<XResourceId>& rxId) const
273 {
274     sal_Int16 nResult (0);
275 
276     const Sequence<OUString> aAnchorURLs (rxId->getAnchorURLs());
277     const sal_uInt32 nLocalURLCount (maResourceURLs.size());
278     const sal_uInt32 nURLCount(1+aAnchorURLs.getLength());
279 
280     // Start comparison with the top most anchors.
281     sal_Int32 nLocalResult (0);
282     for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1;
283          nIndex>=0&&nLocalIndex>=0;
284          --nIndex,--nLocalIndex)
285     {
286         if (nIndex == 0 )
287             nLocalResult = maResourceURLs[nIndex].compareTo(rxId->getResourceURL());
288         else
289             nLocalResult = maResourceURLs[nIndex].compareTo(aAnchorURLs[nIndex-1]);
290         if (nLocalResult != 0)
291         {
292             if (nLocalResult < 0)
293                 nResult = -1;
294             else
295                 nResult = +1;
296             break;
297         }
298     }
299 
300     if (nResult == 0)
301     {
302         // No difference found yet.  When the lengths are the same then the
303         // two resource ids are equivalent.  Otherwise the shorter comes
304         // first.
305         if (nLocalURLCount != nURLCount)
306         {
307             if (nLocalURLCount < nURLCount)
308                 nResult = -1;
309             else
310                 nResult = +1;
311         }
312     }
313 
314     return nResult;
315 }
316 
317 sal_Bool SAL_CALL
isBoundTo(const Reference<XResourceId> & rxResourceId,AnchorBindingMode eMode)318     ResourceId::isBoundTo (
319         const Reference<XResourceId>& rxResourceId,
320         AnchorBindingMode eMode)
321 {
322     if ( ! rxResourceId.is())
323     {
324         // An empty reference is interpreted as empty resource id.
325         return IsBoundToAnchor(nullptr, nullptr, eMode);
326     }
327 
328     ResourceId* pId = nullptr;
329 #ifdef USE_OPTIMIZATIONS
330     pId = dynamic_cast<ResourceId*>(rxResourceId.get());
331 #endif
332     if (pId != nullptr)
333     {
334         return IsBoundToAnchor(pId->maResourceURLs, eMode);
335     }
336     else
337     {
338         const OUString sResourceURL (rxResourceId->getResourceURL());
339         const Sequence<OUString> aAnchorURLs (rxResourceId->getAnchorURLs());
340         return IsBoundToAnchor(&sResourceURL, &aAnchorURLs, eMode);
341     }
342 }
343 
344 sal_Bool SAL_CALL
isBoundToURL(const OUString & rsAnchorURL,AnchorBindingMode eMode)345     ResourceId::isBoundToURL (
346         const OUString& rsAnchorURL,
347         AnchorBindingMode eMode)
348 {
349     return IsBoundToAnchor(&rsAnchorURL, nullptr, eMode);
350 }
351 
352 Reference<XResourceId> SAL_CALL
clone()353     ResourceId::clone()
354 {
355     return new ResourceId(maResourceURLs);
356 }
357 
358 //----- XInitialization -------------------------------------------------------
359 
initialize(const Sequence<Any> & aArguments)360 void SAL_CALL ResourceId::initialize (const Sequence<Any>& aArguments)
361 {
362     for (const auto& rArgument : aArguments)
363     {
364         OUString sResourceURL;
365         if (rArgument >>= sResourceURL)
366             maResourceURLs.push_back(sResourceURL);
367         else
368         {
369             Reference<XResourceId> xAnchor;
370             if (rArgument >>= xAnchor)
371             {
372                 if (xAnchor.is())
373                 {
374                     maResourceURLs.push_back(xAnchor->getResourceURL());
375                     Sequence<OUString> aAnchorURLs (xAnchor->getAnchorURLs());
376                     maResourceURLs.insert( maResourceURLs.end(), aAnchorURLs.begin(), aAnchorURLs.end() );
377                 }
378             }
379         }
380     }
381     ParseResourceURL();
382 }
383 
getImplementationName()384 OUString ResourceId::getImplementationName()
385 {
386     return "com.sun.star.comp.Draw.framework.ResourceId";
387 }
388 
supportsService(OUString const & ServiceName)389 sal_Bool ResourceId::supportsService(OUString const & ServiceName)
390 {
391     return cppu::supportsService(this, ServiceName);
392 }
393 
getSupportedServiceNames()394 css::uno::Sequence<OUString> ResourceId::getSupportedServiceNames()
395 {
396     return css::uno::Sequence<OUString>{
397         "com.sun.star.drawing.framework.ResourceId"};
398 }
399 
400 /** When eMode is DIRECTLY then the anchor of the called object and the
401     anchor represented by the given sequence of anchor URLs have to be
402     identical.   When eMode is RECURSIVE then the anchor of the called
403     object has to start with the given anchor URLs.
404 */
IsBoundToAnchor(const OUString * psFirstAnchorURL,const Sequence<OUString> * paAnchorURLs,AnchorBindingMode eMode) const405 bool ResourceId::IsBoundToAnchor (
406     const OUString* psFirstAnchorURL,
407     const Sequence<OUString>* paAnchorURLs,
408     AnchorBindingMode eMode) const
409 {
410     const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1);
411     const bool bHasFirstAnchorURL (psFirstAnchorURL!=nullptr);
412     const sal_uInt32 nAnchorURLCount ((bHasFirstAnchorURL?1:0)
413         + (paAnchorURLs!=nullptr ? paAnchorURLs->getLength() : 0));
414 
415     // Check the lengths.
416     if (nLocalAnchorURLCount<nAnchorURLCount ||
417         (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount))
418     {
419         return false;
420     }
421 
422     // Compare the nAnchorURLCount bottom-most anchor URLs of this resource
423     // id and the given anchor.
424     sal_uInt32 nOffset = 0;
425     if (paAnchorURLs != nullptr)
426     {
427         sal_uInt32 nCount = paAnchorURLs->getLength();
428         while (nOffset < nCount)
429         {
430             if ( maResourceURLs[nLocalAnchorURLCount - nOffset] !=
431                 (*paAnchorURLs)[nCount - 1 - nOffset] )
432             {
433                 return false;
434             }
435             ++nOffset;
436         }
437     }
438     if (bHasFirstAnchorURL)
439     {
440         if ( *psFirstAnchorURL != maResourceURLs[nLocalAnchorURLCount - nOffset] )
441             return false;
442     }
443 
444     return true;
445 }
446 
IsBoundToAnchor(const::std::vector<OUString> & rAnchorURLs,AnchorBindingMode eMode) const447 bool ResourceId::IsBoundToAnchor (
448     const ::std::vector<OUString>& rAnchorURLs,
449     AnchorBindingMode eMode) const
450 {
451     const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1);
452     const sal_uInt32 nAnchorURLCount (rAnchorURLs.size());
453 
454     // Check the lengths.
455     if (nLocalAnchorURLCount<nAnchorURLCount ||
456         (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount))
457     {
458         return false;
459     }
460 
461     // Compare the nAnchorURLCount bottom-most anchor URLs of this resource
462     // id and the given anchor.
463     for (sal_uInt32 nOffset=0; nOffset<nAnchorURLCount; ++nOffset)
464     {
465         if ( maResourceURLs[nLocalAnchorURLCount - nOffset] !=
466             rAnchorURLs[nAnchorURLCount - 1 - nOffset] )
467         {
468             return false;
469         }
470     }
471 
472     return true;
473 }
474 
ParseResourceURL()475 void ResourceId::ParseResourceURL()
476 {
477     ::osl::Guard< ::osl::Mutex > aGuard (::osl::Mutex::getGlobalMutex());
478     Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak);
479     if ( ! xURLTransformer.is())
480     {
481         // Create the URL transformer.
482         Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
483         xURLTransformer.set(util::URLTransformer::create(xContext));
484         mxURLTransformerWeak = xURLTransformer;
485         SdGlobalResourceContainer::Instance().AddResource(
486             Reference<XInterface>(xURLTransformer,UNO_QUERY));
487     }
488 
489     if (xURLTransformer.is() && !maResourceURLs.empty() )
490     {
491         mpURL.reset(new util::URL);
492         mpURL->Complete = maResourceURLs[0];
493         xURLTransformer->parseStrict(*mpURL);
494         if (mpURL->Main == maResourceURLs[0])
495             mpURL.reset();
496         else
497             maResourceURLs[0] = mpURL->Main;
498     }
499 }
500 
501 } // end of namespace sd::framework
502 
503 
504 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_Draw_framework_ResourceID_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)505 com_sun_star_comp_Draw_framework_ResourceID_get_implementation(css::uno::XComponentContext*,
506                                                                css::uno::Sequence<css::uno::Any> const &)
507 {
508     return cppu::acquire(new sd::framework::ResourceId());
509 }
510 
511 
512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
513