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