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 // SMARTTAGS
25 
26 // MARKER(update_precomp.py): autogen include statement, do not remove
27 #include "precompiled_svx.hxx"
28 #include <svx/SmartTagMgr.hxx>
29 
30 #include <vos/mutex.hxx>
31 #include <vcl/svapp.hxx>
32 #include <com/sun/star/smarttags/XSmartTagRecognizer.hpp>
33 #include <com/sun/star/smarttags/XRangeBasedSmartTagRecognizer.hpp>
34 #include <com/sun/star/smarttags/XSmartTagAction.hpp>
35 #include <com/sun/star/deployment/ExtensionManager.hpp>
36 #include <com/sun/star/text/XTextMarkup.hpp>
37 #include <com/sun/star/smarttags/SmartTagRecognizerMode.hpp>
38 #include <com/sun/star/i18n/XBreakIterator.hpp>
39 #include <com/sun/star/lang/Locale.hpp>
40 #include <com/sun/star/lang/EventObject.hpp>
41 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
42 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
43 #include <com/sun/star/lang/XServiceInfo.hpp>
44 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
45 #include <com/sun/star/container/XNameContainer.hpp>
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 #include <com/sun/star/beans/PropertyValue.hpp>
48 #include <com/sun/star/util/XChangesBatch.hpp>
49 #include <com/sun/star/util/XChangesNotifier.hpp>
50 #include <comphelper/processfactory.hxx>
51 #include <rtl/ustring.hxx>
52 #include <tools/string.hxx>
53 
54 #include <com/sun/star/text/XTextRange.hpp>
55 
56 using namespace com::sun::star;
57 using namespace com::sun::star::uno;
58 
59 #define C2U(cChar) rtl::OUString::createFromAscii(cChar)
60 
61 
SmartTagMgr(const rtl::OUString & rApplicationName)62 SmartTagMgr::SmartTagMgr( const rtl::OUString& rApplicationName )
63     : maApplicationName( rApplicationName ),
64       maRecognizerList(),
65       maActionList(),
66       maDisabledSmartTagTypes(),
67       maSmartTagMap(),
68       mxMSF( ::comphelper::getProcessServiceFactory() ),
69       mbLabelTextWithSmartTags(true)
70 {
71 }
72 
~SmartTagMgr()73 SmartTagMgr::~SmartTagMgr()
74 {
75 }
76 
Init(const rtl::OUString & rConfigurationGroupName)77 void SmartTagMgr::Init( const rtl::OUString& rConfigurationGroupName )
78 {
79     // get component context to pass to components:
80     if ( mxMSF.is() )
81     {
82         Reference< beans::XPropertySet > xPropSet = Reference< beans::XPropertySet>( mxMSF, UNO_QUERY);
83         const Any aAny = xPropSet->getPropertyValue( C2U("DefaultContext"));
84         aAny >>= mxContext;
85 
86         if ( mxContext.is() )
87         {
88             PrepareConfiguration( rConfigurationGroupName );
89             ReadConfiguration( true, true );
90             RegisterListener();
91             LoadLibraries();
92         }
93     }
94 }
CreateBreakIterator() const95 void SmartTagMgr::CreateBreakIterator() const
96 {
97     if ( !mxBreakIter.is() && mxMSF.is() && mxContext.is() )
98     {
99         // get the break iterator
100         mxBreakIter.set(mxMSF->createInstance( C2U( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY);
101     }
102 }
103 
104 /** Dispatches the recognize call to all installed smart tag recognizers
105 */
RecognizeString(const rtl::OUString & rText,const Reference<text::XTextMarkup> xMarkup,const Reference<frame::XController> xController,const lang::Locale & rLocale,sal_uInt32 nStart,sal_uInt32 nLen) const106 void SmartTagMgr::RecognizeString( const rtl::OUString& rText,
107                              const Reference< text::XTextMarkup > xMarkup,
108                              const Reference< frame::XController > xController,
109                              const lang::Locale& rLocale,
110                              sal_uInt32 nStart, sal_uInt32 nLen ) const
111 {
112     for ( sal_uInt32 i = 0; i < maRecognizerList.size(); i++ )
113     {
114         Reference < smarttags::XSmartTagRecognizer > xRecognizer = maRecognizerList[i];
115 
116         // if all smart tag types supported by this recognizer have been
117         // disabled, we do not have to call the recognizer:
118         bool bCallRecognizer = false;
119         const sal_uInt32 nSmartTagCount = xRecognizer->getSmartTagCount();
120         for ( sal_uInt32 j = 0; j < nSmartTagCount && !bCallRecognizer; ++j )
121         {
122             const rtl::OUString aSmartTagName = xRecognizer->getSmartTagName(j);
123             if ( IsSmartTagTypeEnabled( aSmartTagName ) )
124                 bCallRecognizer = true;
125         }
126 
127         if ( bCallRecognizer )
128         {
129             CreateBreakIterator();
130             maRecognizerList[i]->recognize( rText, nStart, nLen,
131                                             smarttags::SmartTagRecognizerMode_PARAGRAPH,
132                                             rLocale, xMarkup, maApplicationName, xController,
133                                             mxBreakIter );
134         }
135     }
136 }
137 
RecognizeTextRange(const Reference<text::XTextRange> xRange,const Reference<text::XTextMarkup> xMarkup,const Reference<frame::XController> xController) const138 void SmartTagMgr::RecognizeTextRange(const Reference< text::XTextRange> xRange,
139                              const Reference< text::XTextMarkup > xMarkup,
140                              const Reference< frame::XController > xController) const
141 {
142     for ( sal_uInt32 i = 0; i < maRecognizerList.size(); i++ )
143     {
144         Reference < smarttags::XSmartTagRecognizer > xRecognizer = maRecognizerList[i];
145 
146         Reference< smarttags::XRangeBasedSmartTagRecognizer > xRangeBasedRecognizer = Reference< smarttags::XRangeBasedSmartTagRecognizer >( xRecognizer, UNO_QUERY);
147 
148         if (!xRangeBasedRecognizer.is()) continue;
149 
150         // if all smart tag types supported by this recognizer have been
151         // disabled, we do not have to call the recognizer:
152         bool bCallRecognizer = false;
153         const sal_uInt32 nSmartTagCount = xRecognizer->getSmartTagCount();
154         for ( sal_uInt32 j = 0; j < nSmartTagCount && !bCallRecognizer; ++j )
155         {
156             const rtl::OUString aSmartTagName = xRecognizer->getSmartTagName(j);
157             if ( IsSmartTagTypeEnabled( aSmartTagName ) )
158                 bCallRecognizer = true;
159         }
160 
161         if ( bCallRecognizer )
162         {
163             xRangeBasedRecognizer->recognizeTextRange( xRange,
164                                                        smarttags::SmartTagRecognizerMode_PARAGRAPH,
165                                                        xMarkup, maApplicationName, xController);
166         }
167     }
168 
169 }
170 
171 
172 typedef std::multimap < rtl::OUString, ActionReference >::const_iterator SmartTagMapIter;
173 
GetActionSequences(Sequence<rtl::OUString> & rSmartTagTypes,Sequence<Sequence<Reference<smarttags::XSmartTagAction>>> & rActionComponentsSequence,Sequence<Sequence<sal_Int32>> & rActionIndicesSequence) const174 void SmartTagMgr::GetActionSequences( Sequence < rtl::OUString >& rSmartTagTypes,
175                                       Sequence < Sequence< Reference< smarttags::XSmartTagAction > > >& rActionComponentsSequence,
176                                       Sequence < Sequence< sal_Int32 > >& rActionIndicesSequence ) const
177 {
178     rActionComponentsSequence.realloc( rSmartTagTypes.getLength() );
179     rActionIndicesSequence.realloc( rSmartTagTypes.getLength() );
180 
181     for ( sal_uInt16 j = 0; j < rSmartTagTypes.getLength(); ++j )
182     {
183         const rtl::OUString& rSmartTagType = rSmartTagTypes[j];
184 
185         const sal_Int32 nNumberOfActionRefs = maSmartTagMap.count( rSmartTagType );
186 
187         Sequence< Reference< smarttags::XSmartTagAction > > aActions( nNumberOfActionRefs );
188         Sequence< sal_Int32 > aIndices( nNumberOfActionRefs );
189 
190         sal_uInt16 i = 0;
191         SmartTagMapIter aActionsIter;
192         SmartTagMapIter aEnd = maSmartTagMap.upper_bound( rSmartTagType );
193 
194         for ( aActionsIter = maSmartTagMap.lower_bound( rSmartTagType ); aActionsIter != aEnd; ++aActionsIter )
195         {
196             aActions[ i ] = (*aActionsIter).second.mxSmartTagAction;
197             aIndices[ i++ ] = (*aActionsIter).second.mnSmartTagIndex;
198         }
199 
200         rActionComponentsSequence[ j ] = aActions;
201         rActionIndicesSequence[ j ]  = aIndices;
202     }
203 }
204 
205 /** Returns the caption for a smart tag type.
206 */
GetSmartTagCaption(const rtl::OUString & rSmartTagType,const com::sun::star::lang::Locale & rLocale) const207 rtl::OUString SmartTagMgr::GetSmartTagCaption( const rtl::OUString& rSmartTagType, const com::sun::star::lang::Locale& rLocale ) const
208 {
209     rtl::OUString aRet;
210 
211     SmartTagMapIter aLower = maSmartTagMap.lower_bound( rSmartTagType );
212 
213     if ( aLower != maSmartTagMap.end() )
214     {
215         const ActionReference& rActionRef = (*aLower).second;
216         Reference< smarttags::XSmartTagAction > xAction = rActionRef.mxSmartTagAction;
217 
218         if ( xAction.is() )
219         {
220             const sal_Int32 nSmartTagIndex = rActionRef.mnSmartTagIndex;
221             aRet = xAction->getSmartTagCaption( nSmartTagIndex, rLocale );
222         }
223     }
224 
225     return aRet;
226 }
227 
228 
229 /** Returns true if the given smart tag type is enabled.
230 */
IsSmartTagTypeEnabled(const rtl::OUString & rSmartTagType) const231 bool SmartTagMgr::IsSmartTagTypeEnabled( const rtl::OUString& rSmartTagType ) const
232 {
233     return maDisabledSmartTagTypes.end() == maDisabledSmartTagTypes.find( rSmartTagType );
234 }
235 
236 /** Writes currently disabled smart tag types to configuration.
237 */
WriteConfiguration(const bool * pIsLabelTextWithSmartTags,const std::vector<rtl::OUString> * pDisabledTypes) const238 void SmartTagMgr::WriteConfiguration( const bool* pIsLabelTextWithSmartTags,
239                                       const std::vector< rtl::OUString >* pDisabledTypes ) const
240 {
241     if ( mxConfigurationSettings.is() )
242     {
243         bool bCommit = false;
244 
245         if ( pIsLabelTextWithSmartTags )
246         {
247             const Any aEnabled = makeAny( *pIsLabelTextWithSmartTags );
248 
249             try
250             {
251                 mxConfigurationSettings->setPropertyValue( C2U("RecognizeSmartTags"), aEnabled );
252                 bCommit = true;
253             }
254             catch ( ::com::sun::star::uno::Exception& )
255             {
256             }
257         }
258 
259         if ( pDisabledTypes )
260         {
261             const sal_Int32 nNumberOfDisabledSmartTagTypes = pDisabledTypes->size();
262             Sequence< rtl::OUString > aTypes( nNumberOfDisabledSmartTagTypes );
263 
264             std::vector< rtl::OUString >::const_iterator aIter;
265             sal_Int32 nCount = 0;
266             for ( aIter = pDisabledTypes->begin(); aIter != pDisabledTypes->end(); ++aIter )
267                 aTypes[ nCount++ ] = *aIter;
268 
269             const Any aNewTypes = makeAny( aTypes );
270 
271             try
272             {
273                 mxConfigurationSettings->setPropertyValue( C2U("ExcludedSmartTagTypes"), aNewTypes );
274                 bCommit = true;
275             }
276             catch ( ::com::sun::star::uno::Exception& )
277             {
278             }
279         }
280 
281         if ( bCommit )
282         {
283             try
284             {
285                 Reference< util::XChangesBatch >( mxConfigurationSettings, UNO_QUERY_THROW )->commitChanges();
286             }
287             catch ( ::com::sun::star::uno::Exception& )
288             {
289             }
290         }
291     }
292 }
293 
294 // ::com::sun::star::util::XModifyListener
modified(const lang::EventObject &)295 void SmartTagMgr::modified( const lang::EventObject& )	throw( RuntimeException )
296 {
297 	vos::OGuard aGuard(Application::GetSolarMutex());
298 
299     maRecognizerList.clear();
300     maActionList.clear();
301     maSmartTagMap.clear();
302 
303     LoadLibraries();
304 }
305 
306 // ::com::sun::star::lang::XEventListener
disposing(const lang::EventObject & rEvent)307 void SmartTagMgr::disposing( const lang::EventObject& rEvent ) throw( RuntimeException )
308 {
309 	vos::OGuard aGuard(Application::GetSolarMutex());
310 
311     uno::Reference< frame::XModel >  xModel( rEvent.Source, uno::UNO_QUERY );
312     uno::Reference< util::XModifyBroadcaster >  xMB(xModel, uno::UNO_QUERY);
313     uno::Reference< util::XChangesNotifier >  xCN(xModel, uno::UNO_QUERY);
314 
315     try
316     {
317         if( xMB.is() )
318         {
319         	uno::Reference< util::XModifyListener >  xListener( this );
320 			xMB->removeModifyListener( xListener );
321         }
322         else if ( xCN.is() )
323         {
324         	uno::Reference< util::XChangesListener >  xListener( this );
325             xCN->removeChangesListener( xListener );
326         }
327     }
328     catch(Exception& )
329     {
330     }
331 }
332 
333 // ::com::sun::star::util::XChangesListener
changesOccurred(const util::ChangesEvent & rEvent)334 void SmartTagMgr::changesOccurred( const util::ChangesEvent& rEvent ) throw( RuntimeException)
335 {
336 	vos::OGuard aGuard(Application::GetSolarMutex());
337 
338     const util::ElementChange* pElementChanges = rEvent.Changes.getConstArray();
339     const sal_Int32 nNumberOfChanges = rEvent.Changes.getLength();
340     bool bExcludedTypes = false;
341     bool bRecognize = false;
342 
343 	for( sal_Int32 i = 0; i < nNumberOfChanges; ++i)
344 	{
345 		rtl::OUString sTemp;
346 		pElementChanges[i].Accessor >>= sTemp;
347 
348         if ( sTemp == C2U( "ExcludedSmartTagTypes" ) )
349             bExcludedTypes = true;
350         else if ( sTemp == C2U( "RecognizeSmartTags" ) )
351             bRecognize = true;
352 	}
353 
354     ReadConfiguration( bExcludedTypes, bRecognize );
355 }
356 
357 //------------- PRIVATE -----------------------------------------------
358 
359 /**
360 */
LoadLibraries()361 void SmartTagMgr::LoadLibraries()
362 {
363     Reference< container::XContentEnumerationAccess > rContent( mxMSF , UNO_QUERY );
364     if ( !rContent.is() )
365         return;
366 
367     // load recognizers: No recognizers -> nothing to do.
368     Reference < container::XEnumeration > rEnum = rContent->createContentEnumeration( C2U("com.sun.star.smarttags.SmartTagRecognizer"));
369     if ( !rEnum.is() || !rEnum->hasMoreElements() )
370         return;
371 
372     // iterate over all implementations of the smart tag recognizer service:
373     while( rEnum->hasMoreElements())
374     {
375         const Any a = rEnum->nextElement();
376         Reference< lang::XSingleComponentFactory > xSCF;
377         Reference< lang::XServiceInfo > xsInfo;
378 
379         if (a >>= xsInfo)
380             xSCF = Reference< lang::XSingleComponentFactory >(xsInfo, UNO_QUERY);
381         else
382             continue;
383 
384         Reference< smarttags::XSmartTagRecognizer > xLib ( xSCF->
385                    createInstanceWithContext(mxContext), UNO_QUERY );
386 
387         if (!xLib.is())
388             continue;
389 
390         xLib->initialize( Sequence< Any >() );
391         maRecognizerList.push_back(xLib);
392     }
393 
394     // load actions: No actions -> nothing to do.
395     rEnum = rContent->createContentEnumeration( C2U("com.sun.star.smarttags.SmartTagAction"));
396     if ( !rEnum.is() )
397         return;
398 
399     // iterate over all implementations of the smart tag action service:
400     while( rEnum->hasMoreElements())
401     {
402         const Any a = rEnum->nextElement();
403         Reference< lang::XServiceInfo > xsInfo;
404         Reference< lang::XSingleComponentFactory > xSCF;
405 
406         if (a >>= xsInfo)
407             xSCF = Reference< lang::XSingleComponentFactory >(xsInfo, UNO_QUERY);
408         else
409             continue;
410 
411         Reference< smarttags::XSmartTagAction > xLib ( xSCF->
412                     createInstanceWithContext(mxContext), UNO_QUERY );
413 
414         if (!xLib.is())
415             continue;
416 
417         xLib->initialize( Sequence< Any >() );
418         maActionList.push_back(xLib);
419     }
420 
421     AssociateActionsWithRecognizers();
422 
423 }
424 
425 /**
426 */
PrepareConfiguration(const rtl::OUString & rConfigurationGroupName)427 void SmartTagMgr::PrepareConfiguration( const rtl::OUString& rConfigurationGroupName )
428 {
429     Any aAny = makeAny( C2U( "/org.openoffice.Office.Common/SmartTags/" ) + rConfigurationGroupName );
430 	beans::PropertyValue aPathArgument;
431 	aPathArgument.Name = C2U( "nodepath" );
432 	aPathArgument.Value = aAny;
433 	Sequence< Any > aArguments( 1 );
434 	aArguments[ 0 ] <<= aPathArgument;
435     Reference< lang::XMultiServiceFactory > xConfProv( mxMSF->createInstance(C2U ("com.sun.star.configuration.ConfigurationProvider")), UNO_QUERY );
436 
437     if ( !xConfProv.is() )
438         return;
439 
440     // try to get read-write access to configuration:
441     Reference< XInterface > xConfigurationAccess;
442     try
443 	{
444         xConfigurationAccess = xConfProv->createInstanceWithArguments(
445             C2U("com.sun.star.configuration.ConfigurationUpdateAccess" ), aArguments );
446 	}
447 	catch ( uno::Exception& )
448 	{
449 	}
450 
451     // fallback: try read-only access to configuration:
452 	if ( !xConfigurationAccess.is() )
453     {
454         try
455         {
456             xConfigurationAccess = xConfProv->createInstanceWithArguments(
457                 C2U("com.sun.star.configuration.ConfigurationAccess" ), aArguments );
458         }
459         catch ( uno::Exception& )
460         {
461         }
462     }
463 
464 	if ( xConfigurationAccess.is() )
465 	{
466         mxConfigurationSettings = Reference< beans::XPropertySet >( xConfigurationAccess, UNO_QUERY );
467     }
468 }
469 
470 
ReadConfiguration(bool bExcludedTypes,bool bRecognize)471 void SmartTagMgr::ReadConfiguration( bool bExcludedTypes, bool bRecognize )
472 {
473     if ( mxConfigurationSettings.is() )
474     {
475         if ( bExcludedTypes )
476         {
477             maDisabledSmartTagTypes.clear();
478 
479             Any aAny = mxConfigurationSettings->getPropertyValue( C2U("ExcludedSmartTagTypes") );
480             Sequence< rtl::OUString > aValues;
481             aAny >>= aValues;
482 
483             const sal_Int32 nValues = aValues.getLength();
484 
485             for ( sal_Int32 nI = 0; nI < nValues; ++nI )
486                 maDisabledSmartTagTypes.insert( aValues[nI] );
487         }
488 
489         if ( bRecognize )
490         {
491             Any aAny = mxConfigurationSettings->getPropertyValue( C2U("RecognizeSmartTags") );
492             sal_Bool bValue = sal_True;
493             aAny >>= bValue;
494 
495             mbLabelTextWithSmartTags = bValue;
496         }
497     }
498 }
499 
500 /**
501 */
RegisterListener()502 void SmartTagMgr::RegisterListener()
503 {
504     // register as listener at package manager
505     try
506     {
507         Reference<deployment::XExtensionManager> xExtensionManager(
508                 deployment::ExtensionManager::get( mxContext ) );
509         Reference< util::XModifyBroadcaster > xMB ( xExtensionManager, UNO_QUERY_THROW );
510 
511         Reference< util::XModifyListener > xListener( this );
512         xMB->addModifyListener( xListener );
513     }
514     catch ( uno::Exception& )
515     {
516     }
517 
518     // register as listener at configuration
519     try
520     {
521         Reference<util::XChangesNotifier> xCN( mxConfigurationSettings, UNO_QUERY_THROW );
522         Reference< util::XChangesListener > xListener( this );
523         xCN->addChangesListener( xListener );
524     }
525     catch ( uno::Exception& )
526     {
527     }
528 }
529 
530 typedef std::pair < const rtl::OUString, ActionReference > SmartTagMapElement;
531 
532 /** Sets up a map that maps smart tag type names to actions references.
533 */
AssociateActionsWithRecognizers()534 void SmartTagMgr::AssociateActionsWithRecognizers()
535 {
536     const sal_uInt32 nActionLibCount = maActionList.size();
537     const sal_uInt32 nRecognizerCount = maRecognizerList.size();
538 
539     for ( sal_uInt32 i = 0; i < nRecognizerCount; ++i )
540     {
541         Reference < smarttags::XSmartTagRecognizer > xRecognizer = maRecognizerList[i];
542         const sal_uInt32 nSmartTagCount = xRecognizer->getSmartTagCount();
543         for ( sal_uInt32 j = 0; j < nSmartTagCount; ++j )
544         {
545             const rtl::OUString aSmartTagName = xRecognizer->getSmartTagName(j);
546 
547             // check if smart tag type has already been processed:
548             if ( maSmartTagMap.find( aSmartTagName ) != maSmartTagMap.end() )
549                 continue;
550 
551             bool bFound = false;
552             for ( sal_uInt32 k = 0; k < nActionLibCount; ++k )
553             {
554                 Reference< smarttags::XSmartTagAction > xActionLib = maActionList[k];
555                 const sal_uInt32 nSmartTagCountInActionLib = xActionLib->getSmartTagCount();
556                 for ( sal_uInt32 l = 0; l < nSmartTagCountInActionLib; ++l )
557                 {
558                     const rtl::OUString aSmartTagNameInActionLib = xActionLib->getSmartTagName(l);
559                     if ( aSmartTagName.equals( aSmartTagNameInActionLib ) )
560                     {
561                         // found actions and recognizer for same smarttag
562                         ActionReference aActionRef( xActionLib, l );
563 
564                         // add recognizer/action pair to map
565                         maSmartTagMap.insert( SmartTagMapElement( aSmartTagName, aActionRef ));
566 
567                         bFound = true;
568                     }
569                 }
570             }
571 
572             if ( !bFound )
573             {
574                 // insert 'empty' action reference if there is no action associated with
575                 // the current smart tag type:
576                 Reference< smarttags::XSmartTagAction > xActionLib;
577                 ActionReference aActionRef( xActionLib, 0 );
578 
579                 // add recognizer/action pair to map
580                 maSmartTagMap.insert( SmartTagMapElement( aSmartTagName, aActionRef ));
581             }
582         }
583     }
584 }
585 
586