1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "precompiled_sd.hxx"
29 
30 #include "framework/ResourceId.hxx"
31 #include "framework/FrameworkHelper.hxx"
32 #include "tools/SdGlobalResourceContainer.hxx"
33 #include <com/sun/star/lang/IllegalArgumentException.hpp>
34 #include <com/sun/star/uno/XComponentContext.hpp>
35 #include <comphelper/processfactory.hxx>
36 #include <rtl/ref.hxx>
37 
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::uno;
40 using namespace ::com::sun::star::lang;
41 using namespace ::com::sun::star::drawing::framework;
42 using ::rtl::OUString;
43 
44 /** When the USE_OPTIMIZATIONS symbol is defined then at some optimizations
45     are activated that work only together with XResourceId objects that are
46     implemented by the ResourceId class.  For other implementations of when
47     the USE_OPTIMIZATIONS symbol is not defined then alternative code is
48     used instead.
49 */
50 #define USE_OPTIMIZATIONS
51 
52 namespace sd { namespace framework {
53 
54 Reference<XInterface> SAL_CALL ResourceId_createInstance (
55     const Reference<XComponentContext>& rxContext)
56 {
57     (void)rxContext;
58     return Reference<XInterface>(static_cast<XWeak*>(new ::sd::framework::ResourceId()));
59 }
60 
61 
62 
63 
64 ::rtl::OUString ResourceId_getImplementationName (void) throw(RuntimeException)
65 {
66     return ::rtl::OUString(
67         RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.Draw.framework.ResourceId"));
68 }
69 
70 
71 
72 
73 Sequence<rtl::OUString> SAL_CALL ResourceId_getSupportedServiceNames (void)
74     throw (RuntimeException)
75 {
76 	static const ::rtl::OUString sServiceName(
77         ::rtl::OUString::createFromAscii("com.sun.star.drawing.framework.ResourceId"));
78 	return Sequence<rtl::OUString>(&sServiceName, 1);
79 }
80 
81 
82 
83 
84 //===== ResourceId ============================================================
85 
86 WeakReference<util::XURLTransformer> ResourceId::mxURLTransformerWeak;
87 
88 ResourceId::ResourceId (void)
89     : ResourceIdInterfaceBase(),
90       maResourceURLs(0),
91       mpURL()
92 {
93 }
94 
95 
96 
97 
98 ResourceId::ResourceId (
99     const std::vector<OUString>& rResourceURLs)
100     : ResourceIdInterfaceBase(),
101       maResourceURLs(rResourceURLs),
102       mpURL()
103 {
104     ParseResourceURL();
105 }
106 
107 
108 
109 
110 ResourceId::ResourceId (
111     const OUString& rsResourceURL)
112     : ResourceIdInterfaceBase(),
113       maResourceURLs(1, rsResourceURL),
114       mpURL()
115 {
116     // Handle the special case of an empty resource URL.
117     if (rsResourceURL.getLength() == 0)
118         maResourceURLs.clear();
119     ParseResourceURL();
120 }
121 
122 
123 
124 
125 ResourceId::ResourceId (
126     const OUString& rsResourceURL,
127     const OUString& rsAnchorURL)
128     : ResourceIdInterfaceBase(),
129       maResourceURLs(2),
130       mpURL()
131 {
132     maResourceURLs[0] = rsResourceURL;
133     maResourceURLs[1] = rsAnchorURL;
134     ParseResourceURL();
135 }
136 
137 
138 
139 
140 ResourceId::ResourceId (
141     const OUString& rsResourceURL,
142     const ::std::vector<OUString>& rAnchorURLs)
143     : ResourceIdInterfaceBase(),
144       maResourceURLs(1+rAnchorURLs.size()),
145       mpURL()
146 {
147     maResourceURLs[0] = rsResourceURL;
148     for (sal_uInt32 nIndex=0; nIndex<rAnchorURLs.size(); ++nIndex)
149         maResourceURLs[nIndex+1] = rAnchorURLs[nIndex];
150     ParseResourceURL();
151 }
152 
153 
154 
155 
156 ResourceId::ResourceId (
157     const OUString& rsResourceURL,
158     const OUString& rsFirstAnchorURL,
159     const Sequence<OUString>& rAnchorURLs)
160     : ResourceIdInterfaceBase(),
161       maResourceURLs(2+rAnchorURLs.getLength()),
162       mpURL()
163 {
164     maResourceURLs[0] = rsResourceURL;
165     maResourceURLs[1] = rsFirstAnchorURL;
166     for (sal_Int32 nIndex=0; nIndex<rAnchorURLs.getLength(); ++nIndex)
167         maResourceURLs[nIndex+2] = rAnchorURLs[nIndex];
168     ParseResourceURL();
169 }
170 
171 
172 
173 
174 ResourceId::~ResourceId (void)
175 {
176     mpURL.reset();
177 }
178 
179 
180 
181 
182 OUString SAL_CALL
183     ResourceId::getResourceURL (void)
184     throw(com::sun::star::uno::RuntimeException)
185 {
186     if (!maResourceURLs.empty())
187         return maResourceURLs[0];
188     else
189         return OUString();
190 }
191 
192 
193 
194 
195 util::URL SAL_CALL
196     ResourceId::getFullResourceURL (void)
197  throw(com::sun::star::uno::RuntimeException)
198 {
199     if (mpURL.get() != NULL)
200         return *mpURL;
201 
202     Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak);
203     if (xURLTransformer.is() && !maResourceURLs.empty() )
204     {
205         mpURL.reset(new util::URL);
206         mpURL->Complete = maResourceURLs[0];
207         xURLTransformer->parseStrict(*mpURL);
208         return *mpURL;
209     }
210 
211     util::URL aURL;
212     if (!maResourceURLs.empty())
213         aURL.Complete = maResourceURLs[0];
214     return aURL;
215 }
216 
217 
218 
219 
220 sal_Bool SAL_CALL
221     ResourceId::hasAnchor (void)
222     throw (RuntimeException)
223 {
224     return maResourceURLs.size()>1;
225 }
226 
227 
228 
229 
230 Reference<XResourceId> SAL_CALL
231     ResourceId::getAnchor (void)
232     throw (RuntimeException)
233 {
234     ::rtl::Reference<ResourceId> rResourceId (new ResourceId());
235     const sal_Int32 nAnchorCount (maResourceURLs.size()-1);
236     if (nAnchorCount > 0)
237     {
238         rResourceId->maResourceURLs.resize(nAnchorCount);
239         for (sal_Int32 nIndex=0; nIndex<nAnchorCount; ++nIndex)
240             rResourceId->maResourceURLs[nIndex] = maResourceURLs[nIndex+1];
241     }
242     return Reference<XResourceId>(rResourceId.get());
243 }
244 
245 
246 
247 
248 Sequence<OUString> SAL_CALL
249     ResourceId::getAnchorURLs (void)
250     throw (RuntimeException)
251 {
252     const sal_Int32 nAnchorCount (maResourceURLs.size() - 1);
253     if (nAnchorCount > 0)
254     {
255         Sequence<OUString> aAnchorURLs (nAnchorCount);
256         for (sal_Int32 nIndex=0; nIndex<nAnchorCount; ++nIndex)
257             aAnchorURLs[nIndex] = maResourceURLs[nIndex+1];
258         return aAnchorURLs;
259     }
260     else
261         return Sequence<OUString>();
262 }
263 
264 
265 
266 
267 OUString SAL_CALL
268     ResourceId::getResourceTypePrefix (void)
269     throw (RuntimeException)
270 {
271     if (!maResourceURLs.empty() )
272     {
273         // Return the "private:resource/<type>/" prefix.
274 
275         // Get the the prefix that ends with the second "/".
276         const OUString& rsResourceURL (maResourceURLs[0]);
277         sal_Int32 nPrefixEnd (rsResourceURL.indexOf(sal_Unicode('/'), 0));
278         if (nPrefixEnd >= 0)
279             nPrefixEnd = rsResourceURL.indexOf(sal_Unicode('/'), nPrefixEnd+1) + 1;
280         else
281             nPrefixEnd = 0;
282 
283         return rsResourceURL.copy(0,nPrefixEnd);
284     }
285     else
286         return OUString();
287 }
288 
289 
290 
291 
292 sal_Int16 SAL_CALL
293     ResourceId::compareTo (const Reference<XResourceId>& rxResourceId)
294     throw (RuntimeException)
295 {
296     sal_Int16 nResult (0);
297 
298     if ( ! rxResourceId.is())
299     {
300         // The empty reference is interpreted as empty resource id object.
301         if (!maResourceURLs.empty())
302             nResult = +1;
303         else
304             nResult = 0;
305     }
306     else
307     {
308         ResourceId* pId = NULL;
309 #ifdef USE_OPTIMIZATIONS
310         pId = dynamic_cast<ResourceId*>(rxResourceId.get());
311 #endif
312         if (pId != NULL)
313         {
314             // We have direct access to the implementation of the given
315             // resource id object.
316             nResult = CompareToLocalImplementation(*pId);
317         }
318         else
319         {
320             // We have to do the comparison via the UNO interface of the
321             // given resource id object.
322             nResult = CompareToExternalImplementation(rxResourceId);
323         }
324     }
325 
326     return nResult;
327 }
328 
329 
330 
331 
332 sal_Int16 ResourceId::CompareToLocalImplementation (const ResourceId& rId) const
333 {
334     sal_Int16 nResult (0);
335 
336     const sal_uInt32 nLocalURLCount (maResourceURLs.size());
337     const sal_uInt32 nURLCount(rId.maResourceURLs.size());
338 
339     // Start comparison with the top most anchors.
340     for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1;
341          nIndex>=0 && nLocalIndex>=0;
342          --nIndex,--nLocalIndex)
343     {
344         const OUString sLocalURL (maResourceURLs[nLocalIndex]);
345         const OUString sURL (rId.maResourceURLs[nIndex]);
346         const sal_Int32 nLocalResult (sURL.compareTo(sLocalURL));
347         if (nLocalResult != 0)
348         {
349             if (nLocalResult < 0)
350                 nResult = -1;
351             else
352                 nResult = +1;
353             break;
354         }
355     }
356 
357     if (nResult == 0)
358     {
359         // No difference found yet.  When the lengths are the same then the
360         // two resource ids are equivalent.  Otherwise the shorter comes
361         // first.
362         if (nLocalURLCount != nURLCount)
363         {
364             if (nLocalURLCount < nURLCount)
365                 nResult = -1;
366             else
367                 nResult = +1;
368         }
369     }
370 
371     return nResult;
372 }
373 
374 
375 
376 
377 sal_Int16 ResourceId::CompareToExternalImplementation (const Reference<XResourceId>& rxId) const
378 {
379     sal_Int16 nResult (0);
380 
381     const Sequence<OUString> aAnchorURLs (rxId->getAnchorURLs());
382     const sal_uInt32 nLocalURLCount (maResourceURLs.size());
383     const sal_uInt32 nURLCount(1+aAnchorURLs.getLength());
384 
385     // Start comparison with the top most anchors.
386     sal_Int32 nLocalResult (0);
387     for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1;
388          nIndex>=0&&nLocalIndex>=0;
389          --nIndex,--nLocalIndex)
390     {
391         if (nIndex == 0 )
392             nLocalResult = maResourceURLs[nIndex].compareTo(rxId->getResourceURL());
393         else
394             nLocalResult = maResourceURLs[nIndex].compareTo(aAnchorURLs[nIndex-1]);
395         if (nLocalResult != 0)
396         {
397             if (nLocalResult < 0)
398                 nResult = -1;
399             else
400                 nResult = +1;
401             break;
402         }
403     }
404 
405     if (nResult == 0)
406     {
407         // No difference found yet.  When the lengths are the same then the
408         // two resource ids are equivalent.  Otherwise the shorter comes
409         // first.
410         if (nLocalURLCount != nURLCount)
411         {
412             if (nLocalURLCount < nURLCount)
413                 nResult = -1;
414             else
415                 nResult = +1;
416         }
417     }
418 
419     return nResult;
420 }
421 
422 
423 
424 
425 sal_Bool SAL_CALL
426     ResourceId::isBoundTo (
427         const Reference<XResourceId>& rxResourceId,
428         AnchorBindingMode eMode)
429     throw (RuntimeException)
430 {
431     if ( ! rxResourceId.is())
432     {
433         // An empty reference is interpreted as empty resource id.
434         return IsBoundToAnchor(NULL, NULL, eMode);
435     }
436 
437     ResourceId* pId = NULL;
438 #ifdef USE_OPTIMIZATIONS
439     pId = dynamic_cast<ResourceId*>(rxResourceId.get());
440 #endif
441     if (pId != NULL)
442     {
443         return IsBoundToAnchor(pId->maResourceURLs, eMode);
444     }
445     else
446     {
447         const OUString sResourceURL (rxResourceId->getResourceURL());
448         const Sequence<OUString> aAnchorURLs (rxResourceId->getAnchorURLs());
449         return IsBoundToAnchor(&sResourceURL, &aAnchorURLs, eMode);
450     }
451 }
452 
453 
454 
455 
456 sal_Bool SAL_CALL
457     ResourceId::isBoundToURL (
458         const OUString& rsAnchorURL,
459         AnchorBindingMode eMode)
460     throw (RuntimeException)
461 {
462     return IsBoundToAnchor(&rsAnchorURL, NULL, eMode);
463 }
464 
465 
466 
467 
468 Reference<XResourceId> SAL_CALL
469     ResourceId::clone (void)
470     throw(RuntimeException)
471 {
472     return new ResourceId(maResourceURLs);
473 }
474 
475 
476 
477 
478 //----- XInitialization -------------------------------------------------------
479 
480 void SAL_CALL ResourceId::initialize (const Sequence<Any>& aArguments)
481     throw (RuntimeException)
482 {
483     sal_uInt32 nCount (aArguments.getLength());
484     for (sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex)
485     {
486         OUString sResourceURL;
487         if (aArguments[nIndex] >>= sResourceURL)
488             maResourceURLs.push_back(sResourceURL);
489         else
490         {
491             Reference<XResourceId> xAnchor;
492             if (aArguments[nIndex] >>= xAnchor)
493             {
494                 if (xAnchor.is())
495                 {
496                     maResourceURLs.push_back(xAnchor->getResourceURL());
497                     Sequence<OUString> aAnchorURLs (xAnchor->getAnchorURLs());
498                     for (sal_Int32 nURLIndex=0; nURLIndex<aAnchorURLs.getLength(); ++nURLIndex)
499                     {
500                         maResourceURLs.push_back(aAnchorURLs[nURLIndex]);
501                     }
502                 }
503             }
504         }
505     }
506     ParseResourceURL();
507 }
508 
509 
510 
511 
512 //-----------------------------------------------------------------------------
513 
514 /** When eMode is DIRECTLY then the anchor of the called object and the
515     anchor represented by the given sequence of anchor URLs have to be
516     identical.   When eMode is RECURSIVE then the anchor of the called
517     object has to start with the given anchor URLs.
518 */
519 bool ResourceId::IsBoundToAnchor (
520     const OUString* psFirstAnchorURL,
521     const Sequence<OUString>* paAnchorURLs,
522     AnchorBindingMode eMode) const
523 {
524     const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1);
525     const bool bHasFirstAnchorURL (psFirstAnchorURL!=NULL);
526     const sal_uInt32 nAnchorURLCount ((bHasFirstAnchorURL?1:0)
527         + (paAnchorURLs!=NULL ? paAnchorURLs->getLength() : 0));
528 
529     // Check the lengths.
530     if (nLocalAnchorURLCount<nAnchorURLCount ||
531         (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount))
532     {
533         return false;
534     }
535 
536     // Compare the nAnchorURLCount bottom-most anchor URLs of this resource
537     // id and the given anchor.
538     sal_uInt32 nOffset = 0;
539     if (paAnchorURLs != NULL)
540     {
541         sal_uInt32 nCount = paAnchorURLs->getLength();
542         while (nOffset < nCount)
543         {
544             if ( ! maResourceURLs[nLocalAnchorURLCount - nOffset].equals(
545                 (*paAnchorURLs)[nCount - 1 - nOffset]))
546             {
547                 return false;
548             }
549             ++nOffset;
550         }
551     }
552     if (bHasFirstAnchorURL)
553     {
554         if ( ! psFirstAnchorURL->equals(maResourceURLs[nLocalAnchorURLCount - nOffset]))
555             return false;
556     }
557 
558     return true;
559 }
560 
561 
562 
563 
564 bool ResourceId::IsBoundToAnchor (
565     const ::std::vector<OUString>& rAnchorURLs,
566     AnchorBindingMode eMode) const
567 {
568     const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1);
569     const sal_uInt32 nAnchorURLCount (rAnchorURLs.size());
570 
571     // Check the lengths.
572     if (nLocalAnchorURLCount<nAnchorURLCount ||
573         (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount))
574     {
575         return false;
576     }
577 
578     // Compare the nAnchorURLCount bottom-most anchor URLs of this resource
579     // id and the given anchor.
580     for (sal_uInt32 nOffset=0; nOffset<nAnchorURLCount; ++nOffset)
581     {
582         if ( ! maResourceURLs[nLocalAnchorURLCount - nOffset].equals(
583             rAnchorURLs[nAnchorURLCount - 1 - nOffset]))
584         {
585             return false;
586         }
587     }
588 
589     return true;
590 }
591 
592 
593 
594 
595 void ResourceId::ParseResourceURL (void)
596 {
597     ::osl::Guard< ::osl::Mutex > aGuard (::osl::Mutex::getGlobalMutex());
598     Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak);
599     if ( ! xURLTransformer.is())
600     {
601         // Create the URL transformer.
602         Reference<lang::XMultiServiceFactory> xServiceManager (
603             ::comphelper::getProcessServiceFactory());
604         xURLTransformer = Reference<util::XURLTransformer>(
605             xServiceManager->createInstance(
606                 OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer"))),
607             UNO_QUERY);
608         mxURLTransformerWeak = xURLTransformer;
609         SdGlobalResourceContainer::Instance().AddResource(
610             Reference<XInterface>(xURLTransformer,UNO_QUERY));
611     }
612 
613     if (xURLTransformer.is() && !maResourceURLs.empty() )
614     {
615         mpURL.reset(new util::URL);
616         mpURL->Complete = maResourceURLs[0];
617         xURLTransformer->parseStrict(*mpURL);
618         if (mpURL->Main == maResourceURLs[0])
619             mpURL.reset();
620         else
621             maResourceURLs[0] = mpURL->Main;
622     }
623 }
624 
625 
626 } } // end of namespace sd::framework
627