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