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 <memory>
21 #include "stringresource.hxx"
22 #include <com/sun/star/io/TempFile.hpp>
23 #include <com/sun/star/io/TextInputStream.hpp>
24 #include <com/sun/star/io/TextOutputStream.hpp>
25 #include <com/sun/star/io/XStream.hpp>
26 #include <com/sun/star/io/XSeekable.hpp>
27 #include <com/sun/star/embed/ElementModes.hpp>
28 #include <com/sun/star/lang/NoSupportException.hpp>
29 #include <com/sun/star/resource/MissingResourceException.hpp>
30 #include <cppuhelper/supportsservice.hxx>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/container/ElementExistException.hpp>
33 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
34 
35 #include <osl/diagnose.h>
36 #include <rtl/tencinfo.h>
37 #include <rtl/ustrbuf.hxx>
38 #include <tools/urlobj.hxx>
39 #include <i18nlangtag/languagetag.hxx>
40 
41 using namespace ::com::sun::star;
42 using namespace ::com::sun::star::lang;
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::ucb;
45 using namespace ::com::sun::star::util;
46 using namespace ::com::sun::star::embed;
47 using namespace ::com::sun::star::container;
48 
49 
50 namespace stringresource
51 {
52 
53 
54 // mutex
55 
56 
getMutex()57 ::osl::Mutex& getMutex()
58 {
59     static ::osl::Mutex s_aMutex;
60 
61     return s_aMutex;
62 }
63 
64 
65 // StringResourceImpl
66 
67 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
scripting_StringResourcePersistenceImpl_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)68 scripting_StringResourcePersistenceImpl_implementation(
69     css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
70 {
71     return cppu::acquire(new StringResourcePersistenceImpl(context));
72 }
73 
74 
StringResourceImpl(const Reference<XComponentContext> & rxContext)75 StringResourceImpl::StringResourceImpl( const Reference< XComponentContext >& rxContext )
76     : m_xContext( rxContext )
77     , m_pCurrentLocaleItem( nullptr )
78     , m_pDefaultLocaleItem( nullptr )
79     , m_bDefaultModified( false )
80     , m_aListenerContainer( getMutex() )
81     , m_bModified( false )
82     , m_bReadOnly( false )
83     , m_nNextUniqueNumericId( UNIQUE_NUMBER_NEEDS_INITIALISATION )
84 {
85 }
86 
87 
~StringResourceImpl()88 StringResourceImpl::~StringResourceImpl()
89 {
90 }
91 
92 
93 // XServiceInfo
94 
getImplementationName()95 OUString StringResourceImpl::getImplementationName(  )
96 {
97     return "com.sun.star.comp.scripting.StringResource";
98 }
99 
supportsService(const OUString & rServiceName)100 sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName )
101 {
102     return cppu::supportsService(this, rServiceName);
103 }
104 
getSupportedServiceNames()105 Sequence< OUString > StringResourceImpl::getSupportedServiceNames(  )
106 {
107     return { "com.sun.star.resource.StringResource" };
108 }
109 
110 
111 // XModifyBroadcaster
112 
addModifyListener(const Reference<XModifyListener> & aListener)113 void StringResourceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
114 {
115     if( !aListener.is() )
116         throw RuntimeException();
117 
118     ::osl::MutexGuard aGuard( getMutex() );
119     m_aListenerContainer.addInterface( Reference<XInterface>( aListener, UNO_QUERY ) );
120 }
121 
removeModifyListener(const Reference<XModifyListener> & aListener)122 void StringResourceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
123 {
124     if( !aListener.is() )
125         throw RuntimeException();
126 
127     ::osl::MutexGuard aGuard( getMutex() );
128     m_aListenerContainer.removeInterface( Reference<XInterface>( aListener, UNO_QUERY ) );
129 }
130 
131 
132 // XStringResourceResolver
133 
implResolveString(const OUString & ResourceID,LocaleItem * pLocaleItem)134 OUString StringResourceImpl::implResolveString
135     ( const OUString& ResourceID, LocaleItem* pLocaleItem )
136 {
137     OUString aRetStr;
138     bool bSuccess = false;
139     if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
140     {
141         IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
142         if( it != pLocaleItem->m_aIdToStringMap.end() )
143         {
144             aRetStr = (*it).second;
145             bSuccess = true;
146         }
147     }
148     if( !bSuccess )
149     {
150         throw css::resource::MissingResourceException( "StringResourceImpl: No entry for ResourceID: " + ResourceID );
151     }
152     return aRetStr;
153 }
154 
resolveString(const OUString & ResourceID)155 OUString StringResourceImpl::resolveString( const OUString& ResourceID )
156 {
157     ::osl::MutexGuard aGuard( getMutex() );
158     return implResolveString( ResourceID, m_pCurrentLocaleItem );
159 }
160 
resolveStringForLocale(const OUString & ResourceID,const Locale & locale)161 OUString StringResourceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
162 {
163     ::osl::MutexGuard aGuard( getMutex() );
164     LocaleItem* pLocaleItem = getItemForLocale( locale, false );
165     return implResolveString( ResourceID, pLocaleItem );
166 }
167 
implHasEntryForId(const OUString & ResourceID,LocaleItem * pLocaleItem)168 bool StringResourceImpl::implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem )
169 {
170     bool bSuccess = false;
171     if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
172     {
173         IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
174         if( it != pLocaleItem->m_aIdToStringMap.end() )
175             bSuccess = true;
176     }
177     return bSuccess;
178 }
179 
hasEntryForId(const OUString & ResourceID)180 sal_Bool StringResourceImpl::hasEntryForId( const OUString& ResourceID )
181 {
182     ::osl::MutexGuard aGuard( getMutex() );
183     return implHasEntryForId( ResourceID, m_pCurrentLocaleItem );
184 }
185 
hasEntryForIdAndLocale(const OUString & ResourceID,const Locale & locale)186 sal_Bool StringResourceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
187     const Locale& locale )
188 {
189     ::osl::MutexGuard aGuard( getMutex() );
190     LocaleItem* pLocaleItem = getItemForLocale( locale, false );
191     return implHasEntryForId( ResourceID, pLocaleItem );
192 }
193 
implGetResourceIDs(LocaleItem * pLocaleItem)194 Sequence< OUString > StringResourceImpl::implGetResourceIDs( LocaleItem* pLocaleItem )
195 {
196     Sequence< OUString > aIDSeq( 0 );
197     if( pLocaleItem && loadLocale( pLocaleItem ) )
198     {
199         const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
200         sal_Int32 nResourceIDCount = rHashMap.size();
201         aIDSeq.realloc( nResourceIDCount );
202         OUString* pStrings = aIDSeq.getArray();
203 
204         int iTarget = 0;
205         for( const auto& rEntry : rHashMap )
206         {
207             OUString aStr = rEntry.first;
208             pStrings[iTarget] = aStr;
209             iTarget++;
210         }
211     }
212     return aIDSeq;
213 }
214 
getResourceIDsForLocale(const Locale & locale)215 Sequence< OUString > StringResourceImpl::getResourceIDsForLocale
216     ( const Locale& locale )
217 {
218     ::osl::MutexGuard aGuard( getMutex() );
219     LocaleItem* pLocaleItem = getItemForLocale( locale, false );
220     return implGetResourceIDs( pLocaleItem );
221 }
222 
getResourceIDs()223 Sequence< OUString > StringResourceImpl::getResourceIDs(  )
224 {
225     ::osl::MutexGuard aGuard( getMutex() );
226     return implGetResourceIDs( m_pCurrentLocaleItem );
227 }
228 
getCurrentLocale()229 Locale StringResourceImpl::getCurrentLocale()
230 {
231     ::osl::MutexGuard aGuard( getMutex() );
232 
233     Locale aRetLocale;
234     if( m_pCurrentLocaleItem != nullptr )
235         aRetLocale = m_pCurrentLocaleItem->m_locale;
236     return aRetLocale;
237 }
238 
getDefaultLocale()239 Locale StringResourceImpl::getDefaultLocale(  )
240 {
241     ::osl::MutexGuard aGuard( getMutex() );
242 
243     Locale aRetLocale;
244     if( m_pDefaultLocaleItem != nullptr )
245         aRetLocale = m_pDefaultLocaleItem->m_locale;
246     return aRetLocale;
247 }
248 
getLocales()249 Sequence< Locale > StringResourceImpl::getLocales(  )
250 {
251     ::osl::MutexGuard aGuard( getMutex() );
252 
253     sal_Int32 nSize = m_aLocaleItemVector.size();
254     Sequence< Locale > aLocalSeq( nSize );
255     Locale* pLocales = aLocalSeq.getArray();
256     int iTarget = 0;
257     for( const auto& pLocaleItem : m_aLocaleItemVector )
258     {
259         pLocales[iTarget] = pLocaleItem->m_locale;
260         iTarget++;
261     }
262     return aLocalSeq;
263 }
264 
265 
266 // XStringResourceManager
267 
implCheckReadOnly(const char * pExceptionMsg)268 void StringResourceImpl::implCheckReadOnly( const char* pExceptionMsg )
269 {
270     if( m_bReadOnly )
271     {
272         OUString errorMsg = OUString::createFromAscii( pExceptionMsg );
273         throw NoSupportException( errorMsg );
274     }
275 }
276 
isReadOnly()277 sal_Bool StringResourceImpl::isReadOnly()
278 {
279     return m_bReadOnly;
280 }
281 
implSetCurrentLocale(const Locale & locale,bool FindClosestMatch,bool bUseDefaultIfNoMatch)282 void StringResourceImpl::implSetCurrentLocale( const Locale& locale,
283     bool FindClosestMatch, bool bUseDefaultIfNoMatch )
284 {
285     ::osl::MutexGuard aGuard( getMutex() );
286 
287     LocaleItem* pLocaleItem = nullptr;
288     if( FindClosestMatch )
289         pLocaleItem = getClosestMatchItemForLocale( locale );
290     else
291         pLocaleItem = getItemForLocale( locale, true );
292 
293     if( pLocaleItem == nullptr && bUseDefaultIfNoMatch )
294         pLocaleItem = m_pDefaultLocaleItem;
295 
296     if( pLocaleItem != nullptr )
297     {
298         (void)loadLocale( pLocaleItem );
299         m_pCurrentLocaleItem = pLocaleItem;
300 
301         // Only notify without modifying
302         implNotifyListeners();
303     }
304 }
305 
setCurrentLocale(const Locale & locale,sal_Bool FindClosestMatch)306 void StringResourceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
307 {
308     implSetCurrentLocale( locale, FindClosestMatch, false/*bUseDefaultIfNoMatch*/ );
309 }
310 
setDefaultLocale(const Locale & locale)311 void StringResourceImpl::setDefaultLocale( const Locale& locale )
312 {
313     ::osl::MutexGuard aGuard( getMutex() );
314     implCheckReadOnly( "StringResourceImpl::setDefaultLocale(): Read only" );
315 
316     LocaleItem* pLocaleItem = getItemForLocale( locale, true );
317     if( pLocaleItem && pLocaleItem != m_pDefaultLocaleItem )
318     {
319         if( m_pDefaultLocaleItem )
320         {
321             m_aChangedDefaultLocaleVector.push_back(
322                 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
323         }
324 
325         m_pDefaultLocaleItem = pLocaleItem;
326         m_bDefaultModified = true;
327         implModified();
328     }
329 }
330 
implSetString(const OUString & ResourceID,const OUString & Str,LocaleItem * pLocaleItem)331 void StringResourceImpl::implSetString( const OUString& ResourceID,
332     const OUString& Str, LocaleItem* pLocaleItem )
333 {
334     if( !(pLocaleItem != nullptr && loadLocale( pLocaleItem )) )
335         return;
336 
337     IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
338 
339     IdToStringMap::iterator it = rHashMap.find( ResourceID );
340     bool bNew = ( it == rHashMap.end() );
341     if( bNew )
342     {
343         IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
344         rIndexMap[ ResourceID ] = pLocaleItem->m_nNextIndex++;
345         implScanIdForNumber( ResourceID );
346     }
347     rHashMap[ ResourceID ] = Str;
348     pLocaleItem->m_bModified = true;
349     implModified();
350 }
351 
setString(const OUString & ResourceID,const OUString & Str)352 void StringResourceImpl::setString( const OUString& ResourceID, const OUString& Str )
353 {
354     ::osl::MutexGuard aGuard( getMutex() );
355     implCheckReadOnly( "StringResourceImpl::setString(): Read only" );
356     implSetString( ResourceID, Str, m_pCurrentLocaleItem );
357 }
358 
setStringForLocale(const OUString & ResourceID,const OUString & Str,const Locale & locale)359 void StringResourceImpl::setStringForLocale
360     ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
361 {
362     ::osl::MutexGuard aGuard( getMutex() );
363     implCheckReadOnly( "StringResourceImpl::setStringForLocale(): Read only" );
364     LocaleItem* pLocaleItem = getItemForLocale( locale, false );
365     implSetString( ResourceID, Str, pLocaleItem );
366 }
367 
implRemoveId(const OUString & ResourceID,LocaleItem * pLocaleItem)368 void StringResourceImpl::implRemoveId( const OUString& ResourceID, LocaleItem* pLocaleItem )
369 {
370     if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
371     {
372         IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
373         IdToStringMap::iterator it = rHashMap.find( ResourceID );
374         if( it == rHashMap.end() )
375         {
376             throw css::resource::MissingResourceException( "StringResourceImpl: No entries for ResourceID: " + ResourceID );
377         }
378         rHashMap.erase( it );
379         pLocaleItem->m_bModified = true;
380         implModified();
381     }
382 }
383 
removeId(const OUString & ResourceID)384 void StringResourceImpl::removeId( const OUString& ResourceID )
385 {
386     ::osl::MutexGuard aGuard( getMutex() );
387     implCheckReadOnly( "StringResourceImpl::removeId(): Read only" );
388     implRemoveId( ResourceID, m_pCurrentLocaleItem );
389 }
390 
removeIdForLocale(const OUString & ResourceID,const Locale & locale)391 void StringResourceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
392 {
393     ::osl::MutexGuard aGuard( getMutex() );
394     implCheckReadOnly( "StringResourceImpl::removeIdForLocale(): Read only" );
395     LocaleItem* pLocaleItem = getItemForLocale( locale, false );
396     implRemoveId( ResourceID, pLocaleItem );
397 }
398 
newLocale(const Locale & locale)399 void StringResourceImpl::newLocale( const Locale& locale )
400 {
401     ::osl::MutexGuard aGuard( getMutex() );
402     implCheckReadOnly( "StringResourceImpl::newLocale(): Read only" );
403 
404     if( getItemForLocale( locale, false ) != nullptr )
405     {
406         throw ElementExistException( "StringResourceImpl: locale already exists" );
407     }
408 
409     // TODO?: Check if locale is valid? How?
410     //if (!bValid)
411     //{
412     //    OUString errorMsg("StringResourceImpl: Invalid locale");
413     //    throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 0 );
414     //}
415 
416     LocaleItem* pLocaleItem = new LocaleItem( locale );
417     m_aLocaleItemVector.emplace_back( pLocaleItem );
418     pLocaleItem->m_bModified = true;
419 
420     // Copy strings from default locale
421     LocaleItem* pCopyFromItem = m_pDefaultLocaleItem;
422     if( pCopyFromItem == nullptr )
423         pCopyFromItem = m_pCurrentLocaleItem;
424     if( pCopyFromItem != nullptr && loadLocale( pCopyFromItem ) )
425     {
426         const IdToStringMap& rSourceMap = pCopyFromItem->m_aIdToStringMap;
427         IdToStringMap& rTargetMap = pLocaleItem->m_aIdToStringMap;
428         for( const auto& rEntry : rSourceMap )
429         {
430             OUString aId  = rEntry.first;
431             OUString aStr = rEntry.second;
432             rTargetMap[ aId ] = aStr;
433         }
434 
435         const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap;
436         IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap;
437         for( const auto& rIndex : rSourceIndexMap )
438         {
439             OUString aId = rIndex.first;
440             sal_Int32 nIndex = rIndex.second;
441             rTargetIndexMap[ aId ] = nIndex;
442         }
443         pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex;
444     }
445 
446     if( m_pCurrentLocaleItem == nullptr )
447         m_pCurrentLocaleItem = pLocaleItem;
448 
449     if( m_pDefaultLocaleItem == nullptr )
450     {
451         m_pDefaultLocaleItem = pLocaleItem;
452         m_bDefaultModified = true;
453     }
454 
455     implModified();
456 }
457 
removeLocale(const Locale & locale)458 void StringResourceImpl::removeLocale( const Locale& locale )
459 {
460     ::osl::MutexGuard aGuard( getMutex() );
461     implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" );
462 
463     LocaleItem* pRemoveItem = getItemForLocale( locale, true );
464     if( !pRemoveItem )
465         return;
466 
467     // Last locale?
468     sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
469     if( nLocaleCount > 1 )
470     {
471         if( m_pCurrentLocaleItem == pRemoveItem ||
472             m_pDefaultLocaleItem  == pRemoveItem )
473         {
474             LocaleItem* pFallbackItem = nullptr;
475             for( const auto& pLocaleItem : m_aLocaleItemVector )
476             {
477                 if( pLocaleItem.get() != pRemoveItem )
478                 {
479                     pFallbackItem = pLocaleItem.get();
480                     break;
481                 }
482             }
483             if( m_pCurrentLocaleItem == pRemoveItem )
484             {
485                 setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ );
486             }
487             if( m_pDefaultLocaleItem == pRemoveItem )
488             {
489                 setDefaultLocale( pFallbackItem->m_locale );
490             }
491         }
492     }
493     auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(),
494         [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; });
495     if (it == m_aLocaleItemVector.end())
496         return;
497 
498     // Remember locale item to delete file while storing
499     m_aDeletedLocaleItemVector.push_back( std::move(*it) );
500 
501     // Last locale?
502     if( nLocaleCount == 1 )
503     {
504         m_nNextUniqueNumericId = 0;
505         if( m_pDefaultLocaleItem )
506         {
507             m_aChangedDefaultLocaleVector.push_back(
508                     std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
509         }
510         m_pCurrentLocaleItem = nullptr;
511         m_pDefaultLocaleItem = nullptr;
512     }
513 
514     m_aLocaleItemVector.erase( it );
515 
516     implModified();
517 }
518 
implScanIdForNumber(const OUString & ResourceID)519 void StringResourceImpl::implScanIdForNumber( const OUString& ResourceID )
520 {
521     const sal_Unicode* pSrc = ResourceID.getStr();
522     sal_Int32 nLen = ResourceID.getLength();
523 
524     sal_Int32 nNumber = 0;
525     for( sal_Int32 i = 0 ; i < nLen ; i++ )
526     {
527         sal_Unicode c = pSrc[i];
528         if( c >= '0' && c <= '9' )
529         {
530             sal_uInt16 nDigitVal = c - '0';
531             nNumber = 10*nNumber + nDigitVal;
532         }
533         else
534             break;
535     }
536 
537     if( m_nNextUniqueNumericId < nNumber + 1 )
538         m_nNextUniqueNumericId = nNumber + 1;
539 }
540 
getUniqueNumericId()541 sal_Int32 StringResourceImpl::getUniqueNumericId(  )
542 {
543     if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION )
544     {
545         implLoadAllLocales();
546         m_nNextUniqueNumericId = 0;
547     }
548 
549     if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION )
550     {
551         throw NoSupportException( "getUniqueNumericId: Extended sal_Int32 range" );
552     }
553     return m_nNextUniqueNumericId;
554 }
555 
556 
557 // Private helper methods
558 
getItemForLocale(const Locale & locale,bool bException)559 LocaleItem* StringResourceImpl::getItemForLocale
560     ( const Locale& locale, bool bException )
561 {
562     LocaleItem* pRetItem = nullptr;
563 
564     // Search for locale
565     for( auto& pLocaleItem : m_aLocaleItemVector )
566     {
567         if( pLocaleItem )
568         {
569             Locale& cmp_locale = pLocaleItem->m_locale;
570             if( cmp_locale.Language == locale.Language &&
571                 cmp_locale.Country  == locale.Country &&
572                 cmp_locale.Variant  == locale.Variant )
573             {
574                 pRetItem = pLocaleItem.get();
575                 break;
576             }
577         }
578     }
579 
580     if( pRetItem == nullptr && bException )
581     {
582         throw IllegalArgumentException( "StringResourceImpl: Invalid locale", Reference< XInterface >(), 0 );
583     }
584     return pRetItem;
585 }
586 
587 // Returns the LocaleItem for a given locale, if it exists, otherwise NULL.
588 // This method performs a closest match search, at least the language must match.
getClosestMatchItemForLocale(const Locale & locale)589 LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale )
590 {
591     LocaleItem* pRetItem = nullptr;
592 
593     ::std::vector< Locale > aLocales( m_aLocaleItemVector.size());
594     size_t i = 0;
595     for( const auto& pLocaleItem : m_aLocaleItemVector )
596     {
597         aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale());
598         ++i;
599     }
600     ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale));
601     if (iFound != aLocales.end())
602         pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get();
603 
604     return pRetItem;
605 }
606 
implModified()607 void StringResourceImpl::implModified()
608 {
609     m_bModified = true;
610     implNotifyListeners();
611 }
612 
implNotifyListeners()613 void StringResourceImpl::implNotifyListeners()
614 {
615     EventObject aEvent;
616     aEvent.Source = static_cast< XInterface* >( static_cast<OWeakObject*>(this) );
617 
618     ::comphelper::OInterfaceIteratorHelper2 it( m_aListenerContainer );
619     while( it.hasMoreElements() )
620     {
621         Reference< XInterface > xIface = it.next();
622         Reference< XModifyListener > xListener( xIface, UNO_QUERY );
623         try
624         {
625             xListener->modified( aEvent );
626         }
627         catch(RuntimeException&)
628         {
629             it.remove();
630         }
631     }
632 }
633 
634 
635 // Loading
636 
loadLocale(LocaleItem *)637 bool StringResourceImpl::loadLocale( LocaleItem* )
638 {
639     // Base implementation has nothing to load
640     return true;
641 }
642 
implLoadAllLocales()643 void StringResourceImpl::implLoadAllLocales()
644 {
645     // Base implementation has nothing to load
646 }
647 
648 
649 // StringResourcePersistenceImpl
650 
651 
StringResourcePersistenceImpl(const Reference<XComponentContext> & rxContext)652 StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext )
653     : StringResourcePersistenceImpl_BASE( rxContext )
654 {
655 }
656 
657 
~StringResourcePersistenceImpl()658 StringResourcePersistenceImpl::~StringResourcePersistenceImpl()
659 {
660 }
661 
662 
663 // XServiceInfo
664 
665 
getImplementationName()666 OUString StringResourcePersistenceImpl::getImplementationName(  )
667 {
668     return "com.sun.star.comp.scripting.StringResource";
669 }
670 
671 
supportsService(const OUString & rServiceName)672 sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName )
673 {
674     return cppu::supportsService( this, rServiceName );
675 }
676 
677 
getSupportedServiceNames()678 Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames(  )
679 {
680     return StringResourceImpl::getSupportedServiceNames();
681 }
682 
683 
684 // XInitialization base functionality for derived classes
685 
686 
687 constexpr OUStringLiteral aNameBaseDefaultStr = u"strings";
688 
implInitializeCommonParameters(const Sequence<Any> & aArguments)689 void StringResourcePersistenceImpl::implInitializeCommonParameters
690     ( const Sequence< Any >& aArguments )
691 {
692     bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly);
693     if( !bReadOnlyOk )
694     {
695         throw IllegalArgumentException( "XInitialization::initialize: Expected ReadOnly flag", Reference< XInterface >(), 1 );
696     }
697 
698     css::lang::Locale aCurrentLocale;
699     bool bLocaleOk = (aArguments[2] >>= aCurrentLocale);
700     if( !bLocaleOk )
701     {
702         throw IllegalArgumentException( "XInitialization::initialize: Expected Locale", Reference< XInterface >(), 2 );
703     }
704 
705     bool bNameBaseOk = (aArguments[3] >>= m_aNameBase);
706     if( !bNameBaseOk )
707     {
708         throw IllegalArgumentException( "XInitialization::initialize: Expected NameBase string", Reference< XInterface >(), 3 );
709     }
710     if( m_aNameBase.isEmpty() )
711         m_aNameBase = aNameBaseDefaultStr;
712 
713     bool bCommentOk = (aArguments[4] >>= m_aComment);
714     if( !bCommentOk )
715     {
716         throw IllegalArgumentException( "XInitialization::initialize: Expected Comment string", Reference< XInterface >(), 4 );
717     }
718 
719     implScanLocales();
720 
721     implSetCurrentLocale( aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ );
722 }
723 
724 
725 // Forwarding calls to base class
726 
727 // XModifyBroadcaster
addModifyListener(const Reference<XModifyListener> & aListener)728 void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
729 {
730     StringResourceImpl::addModifyListener( aListener );
731 }
removeModifyListener(const Reference<XModifyListener> & aListener)732 void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
733 {
734     StringResourceImpl::removeModifyListener( aListener );
735 }
736 
737 // XStringResourceResolver
resolveString(const OUString & ResourceID)738 OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID )
739 {
740     return StringResourceImpl::resolveString( ResourceID ) ;
741 }
resolveStringForLocale(const OUString & ResourceID,const Locale & locale)742 OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
743 {
744     return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
745 }
hasEntryForId(const OUString & ResourceID)746 sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID )
747 {
748     return StringResourceImpl::hasEntryForId( ResourceID ) ;
749 }
hasEntryForIdAndLocale(const OUString & ResourceID,const Locale & locale)750 sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
751     const Locale& locale )
752 {
753     return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
754 }
getCurrentLocale()755 Locale StringResourcePersistenceImpl::getCurrentLocale()
756 {
757     return StringResourceImpl::getCurrentLocale();
758 }
getDefaultLocale()759 Locale StringResourcePersistenceImpl::getDefaultLocale(  )
760 {
761     return StringResourceImpl::getDefaultLocale();
762 }
getLocales()763 Sequence< Locale > StringResourcePersistenceImpl::getLocales(  )
764 {
765     return StringResourceImpl::getLocales();
766 }
767 
768 // XStringResourceManager
isReadOnly()769 sal_Bool StringResourcePersistenceImpl::isReadOnly()
770 {
771     return StringResourceImpl::isReadOnly();
772 }
setCurrentLocale(const Locale & locale,sal_Bool FindClosestMatch)773 void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
774 {
775     StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
776 }
setDefaultLocale(const Locale & locale)777 void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale )
778 {
779     StringResourceImpl::setDefaultLocale( locale );
780 }
getResourceIDs()781 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs(  )
782 {
783     return StringResourceImpl::getResourceIDs();
784 }
setString(const OUString & ResourceID,const OUString & Str)785 void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str )
786 {
787     StringResourceImpl::setString( ResourceID, Str );
788 }
setStringForLocale(const OUString & ResourceID,const OUString & Str,const Locale & locale)789 void StringResourcePersistenceImpl::setStringForLocale
790     ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
791 {
792     StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
793 }
getResourceIDsForLocale(const Locale & locale)794 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale
795     ( const Locale& locale )
796 {
797     return StringResourceImpl::getResourceIDsForLocale( locale );
798 }
removeId(const OUString & ResourceID)799 void StringResourcePersistenceImpl::removeId( const OUString& ResourceID )
800 {
801     StringResourceImpl::removeId( ResourceID );
802 }
removeIdForLocale(const OUString & ResourceID,const Locale & locale)803 void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
804 {
805     StringResourceImpl::removeIdForLocale( ResourceID, locale );
806 }
newLocale(const Locale & locale)807 void StringResourcePersistenceImpl::newLocale( const Locale& locale )
808 {
809     StringResourceImpl::newLocale( locale );
810 }
removeLocale(const Locale & locale)811 void StringResourcePersistenceImpl::removeLocale( const Locale& locale )
812 {
813     StringResourceImpl::removeLocale( locale );
814 }
getUniqueNumericId()815 sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId(  )
816 {
817     return StringResourceImpl::getUniqueNumericId();
818 }
819 
820 
821 // XStringResourcePersistence
822 
store()823 void StringResourcePersistenceImpl::store()
824 {
825 }
826 
isModified()827 sal_Bool StringResourcePersistenceImpl::isModified(  )
828 {
829     ::osl::MutexGuard aGuard( getMutex() );
830 
831     return m_bModified;
832 }
833 
setComment(const OUString & Comment)834 void StringResourcePersistenceImpl::setComment( const OUString& Comment )
835 {
836     m_aComment = Comment;
837 }
838 
storeToStorage(const Reference<XStorage> & Storage,const OUString & NameBase,const OUString & Comment)839 void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage,
840     const OUString& NameBase, const OUString& Comment )
841 {
842     ::osl::MutexGuard aGuard( getMutex() );
843 
844     implStoreAtStorage( NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ );
845 }
846 
implStoreAtStorage(const OUString & aNameBase,const OUString & aComment,const Reference<css::embed::XStorage> & Storage,bool bUsedForStore,bool bStoreAll)847 void StringResourcePersistenceImpl::implStoreAtStorage
848 (
849     const OUString& aNameBase,
850     const OUString& aComment,
851     const Reference< css::embed::XStorage >& Storage,
852     bool bUsedForStore,
853     bool bStoreAll
854 )
855 {
856     // Delete files for deleted locales
857     if( bUsedForStore )
858     {
859         for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
860         {
861             if( pLocaleItem )
862             {
863                 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties";
864 
865                 try
866                 {
867                     Storage->removeElement( aStreamName );
868                 }
869                 catch( Exception& )
870                 {}
871 
872                 pLocaleItem.reset();
873             }
874         }
875         m_aDeletedLocaleItemVector.clear();
876     }
877 
878     for( auto& pLocaleItem : m_aLocaleItemVector )
879     {
880         if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) &&
881             loadLocale( pLocaleItem.get() ) )
882         {
883             OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties";
884 
885             Reference< io::XStream > xElementStream =
886                     Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
887 
888             uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY );
889             OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" );
890             if ( xProps.is() )
891             {
892                 OUString aPropName("MediaType");
893                 xProps->setPropertyValue( aPropName, uno::makeAny( OUString("text/plain") ) );
894 
895                 aPropName = "UseCommonStoragePasswordEncryption";
896                 xProps->setPropertyValue( aPropName, uno::makeAny( true ) );
897             }
898 
899             Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
900             if( xOutputStream.is() )
901                 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
902             xOutputStream->closeOutput();
903 
904             if( bUsedForStore )
905                 pLocaleItem->m_bModified = false;
906         }
907     }
908 
909     // Delete files for changed defaults
910     if( bUsedForStore )
911     {
912         for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
913         {
914             OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default";
915 
916             try
917             {
918                 Storage->removeElement( aStreamName );
919             }
920             catch( Exception& )
921             {}
922 
923             pLocaleItem.reset();
924         }
925         m_aChangedDefaultLocaleVector.clear();
926     }
927 
928     // Default locale
929     if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) )
930         return;
931 
932     OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default";
933 
934     Reference< io::XStream > xElementStream =
935             Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
936 
937     // Only create stream without content
938     Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
939     xOutputStream->closeOutput();
940 
941     if( bUsedForStore )
942         m_bDefaultModified = false;
943 }
944 
storeToURL(const OUString & URL,const OUString & NameBase,const OUString & Comment,const Reference<css::task::XInteractionHandler> & Handler)945 void StringResourcePersistenceImpl::storeToURL( const OUString& URL,
946     const OUString& NameBase, const OUString& Comment,
947     const Reference< css::task::XInteractionHandler >& Handler )
948 {
949     ::osl::MutexGuard aGuard( getMutex() );
950 
951     Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext);
952     if( xFileAccess.is() && Handler.is() )
953         xFileAccess->setInteractionHandler( Handler );
954 
955     implStoreAtLocation( URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/ );
956 }
957 
implKillRemovedLocaleFiles(const OUString & Location,const OUString & aNameBase,const css::uno::Reference<css::ucb::XSimpleFileAccess3> & xFileAccess)958 void StringResourcePersistenceImpl::implKillRemovedLocaleFiles
959 (
960     const OUString& Location,
961     const OUString& aNameBase,
962     const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
963 )
964 {
965     // Delete files for deleted locales
966     for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
967     {
968         if( pLocaleItem )
969         {
970             OUString aCompleteFileName =
971                 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
972             if( xFileAccess->exists( aCompleteFileName ) )
973                 xFileAccess->kill( aCompleteFileName );
974 
975             pLocaleItem.reset();
976         }
977     }
978     m_aDeletedLocaleItemVector.clear();
979 }
980 
implKillChangedDefaultFiles(const OUString & Location,const OUString & aNameBase,const css::uno::Reference<css::ucb::XSimpleFileAccess3> & xFileAccess)981 void StringResourcePersistenceImpl::implKillChangedDefaultFiles
982 (
983     const OUString& Location,
984     const OUString& aNameBase,
985     const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
986 )
987 {
988     // Delete files for changed defaults
989     for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
990     {
991         OUString aCompleteFileName =
992             implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true );
993         if( xFileAccess->exists( aCompleteFileName ) )
994             xFileAccess->kill( aCompleteFileName );
995         pLocaleItem.reset();
996     }
997     m_aChangedDefaultLocaleVector.clear();
998 }
999 
implStoreAtLocation(const OUString & Location,const OUString & aNameBase,const OUString & aComment,const Reference<ucb::XSimpleFileAccess3> & xFileAccess,bool bUsedForStore,bool bStoreAll,bool bKillAll)1000 void StringResourcePersistenceImpl::implStoreAtLocation
1001 (
1002     const OUString& Location,
1003     const OUString& aNameBase,
1004     const OUString& aComment,
1005     const Reference< ucb::XSimpleFileAccess3 >& xFileAccess,
1006     bool bUsedForStore,
1007     bool bStoreAll,
1008     bool bKillAll
1009 )
1010 {
1011     // Delete files for deleted locales
1012     if( bUsedForStore || bKillAll )
1013         implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess );
1014 
1015     for( auto& pLocaleItem : m_aLocaleItemVector )
1016     {
1017         if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) &&
1018             loadLocale( pLocaleItem.get() ) )
1019         {
1020             OUString aCompleteFileName =
1021                 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
1022             if( xFileAccess->exists( aCompleteFileName ) )
1023                 xFileAccess->kill( aCompleteFileName );
1024 
1025             if( !bKillAll )
1026             {
1027                 // Create Output stream
1028                 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1029                 if( xOutputStream.is() )
1030                 {
1031                     implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
1032                     xOutputStream->closeOutput();
1033                 }
1034                 if( bUsedForStore )
1035                     pLocaleItem->m_bModified = false;
1036             }
1037         }
1038     }
1039 
1040     // Delete files for changed defaults
1041     if( bUsedForStore || bKillAll )
1042         implKillChangedDefaultFiles( Location, aNameBase, xFileAccess );
1043 
1044     // Default locale
1045     if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) )
1046         return;
1047 
1048     OUString aCompleteFileName =
1049         implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true );
1050     if( xFileAccess->exists( aCompleteFileName ) )
1051         xFileAccess->kill( aCompleteFileName );
1052 
1053     if( !bKillAll )
1054     {
1055         // Create Output stream
1056         Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1057         if( xOutputStream.is() )
1058             xOutputStream->closeOutput();
1059 
1060         if( bUsedForStore )
1061             m_bDefaultModified = false;
1062     }
1063 }
1064 
1065 
1066 // BinaryOutput, helper class for exportBinary
1067 
1068 class BinaryOutput
1069 {
1070     Reference< XComponentContext >          m_xContext;
1071     Reference< XInterface >                 m_xTempFile;
1072     Reference< io::XOutputStream >          m_xOutputStream;
1073 
1074 public:
1075     explicit BinaryOutput( Reference< XComponentContext > const & xContext );
1076 
getOutputStream() const1077     const Reference< io::XOutputStream >& getOutputStream() const
1078         { return m_xOutputStream; }
1079 
1080     Sequence< ::sal_Int8 > closeAndGetData();
1081 
1082     // Template to be used with sal_Int16 and sal_Unicode
1083     template< class T >
1084     void write16BitInt( T n );
writeInt16(sal_Int16 n)1085     void writeInt16( sal_Int16 n )
1086         { write16BitInt( n ); }
writeUnicodeChar(sal_Unicode n)1087     void writeUnicodeChar( sal_Unicode n )
1088         { write16BitInt( n ); }
1089     void writeInt32( sal_Int32 n );
1090     void writeString( const OUString& aStr );
1091 };
1092 
BinaryOutput(Reference<XComponentContext> const & xContext)1093 BinaryOutput::BinaryOutput( Reference< XComponentContext > const & xContext )
1094         : m_xContext( xContext )
1095 {
1096     m_xTempFile = io::TempFile::create( m_xContext );
1097     m_xOutputStream.set( m_xTempFile, UNO_QUERY_THROW );
1098 }
1099 
1100 template< class T >
write16BitInt(T n)1101 void BinaryOutput::write16BitInt( T n )
1102 {
1103     if( !m_xOutputStream.is() )
1104         return;
1105 
1106     Sequence< sal_Int8 > aSeq( 2 );
1107     sal_Int8* p = aSeq.getArray();
1108 
1109     sal_Int8 nLow  = sal_Int8( n & 0xff );
1110     sal_Int8 nHigh = sal_Int8( n >> 8 );
1111 
1112     p[0] = nLow;
1113     p[1] = nHigh;
1114     m_xOutputStream->writeBytes( aSeq );
1115 }
1116 
writeInt32(sal_Int32 n)1117 void BinaryOutput::writeInt32( sal_Int32 n )
1118 {
1119     if( !m_xOutputStream.is() )
1120         return;
1121 
1122     Sequence< sal_Int8 > aSeq( 4 );
1123     sal_Int8* p = aSeq.getArray();
1124 
1125     for( sal_Int16 i = 0 ; i < 4 ; i++ )
1126     {
1127         p[i] = sal_Int8( n & 0xff );
1128         n >>= 8;
1129     }
1130     m_xOutputStream->writeBytes( aSeq );
1131 }
1132 
writeString(const OUString & aStr)1133 void BinaryOutput::writeString( const OUString& aStr )
1134 {
1135     sal_Int32 nLen = aStr.getLength();
1136     const sal_Unicode* pStr = aStr.getStr();
1137 
1138     for( sal_Int32 i = 0 ; i < nLen ; i++ )
1139         writeUnicodeChar( pStr[i] );
1140 
1141     writeUnicodeChar( 0 );
1142 }
1143 
closeAndGetData()1144 Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData()
1145 {
1146     Sequence< ::sal_Int8 > aRetSeq;
1147     if( !m_xOutputStream.is() )
1148         return aRetSeq;
1149 
1150     m_xOutputStream->closeOutput();
1151 
1152     Reference< io::XSeekable> xSeekable( m_xTempFile, UNO_QUERY );
1153     if( !xSeekable.is() )
1154         return aRetSeq;
1155 
1156     sal_Int32 nSize = static_cast<sal_Int32>(xSeekable->getPosition());
1157 
1158     Reference< io::XInputStream> xInputStream( m_xTempFile, UNO_QUERY );
1159     if( !xInputStream.is() )
1160         return aRetSeq;
1161 
1162     xSeekable->seek( 0 );
1163     sal_Int32 nRead = xInputStream->readBytes( aRetSeq, nSize );
1164     OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" );
1165 
1166     return aRetSeq;
1167 }
1168 
1169 
1170 // Binary format:
1171 
1172 // Header
1173 // Byte         Content
1174 // 0 + 1        sal_Int16:  Version, currently 0, low byte first
1175 // 2 + 3        sal_Int16:  Locale count = n, low byte first
1176 // 4 + 5        sal_Int16:  Default Locale position in Locale list, == n if none
1177 // 6 - 7        sal_Int32:  Start index locale block 0, lowest byte first
1178 // (n-1) *      sal_Int32:  Start index locale block 1 to n, lowest byte first
1179 // 6 + 4*n      sal_Int32:  "Start index" non existing locale block n+1,
1180 //                          marks the first invalid index, kind of EOF
1181 
1182 // Locale block
1183 // All strings are stored as 2-Byte-0 terminated sequence
1184 // of 16 bit Unicode characters, each with low byte first
1185 // Empty strings only contain the 2-Byte-0
1186 
1187 // Members of com.sun.star.lang.Locale
1188 // with l1 = Locale.Language.getLength()
1189 // with l2 = Locale.Country.getLength()
1190 // with l3 = Locale.Variant.getLength()
1191 // pos0 = 0                     Locale.Language
1192 // pos1 = 2 * (l1 + 1)          Locale.Country
1193 // pos2 = pos1 + 2 * (l2 + 1)   Locale.Variant
1194 // pos3 = pos2 + 2 * (l3 + 1)
1195 // pos3                         Properties file written by implWritePropertiesFile
1196 
exportBinary()1197 Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary(  )
1198 {
1199     BinaryOutput aOut( m_xContext );
1200 
1201     sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
1202     std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount);
1203 
1204     sal_Int32 iLocale = 0;
1205     sal_Int32 iDefault = 0;
1206     for( auto& pLocaleItem : m_aLocaleItemVector )
1207     {
1208         if( pLocaleItem != nullptr && loadLocale( pLocaleItem.get() ) )
1209         {
1210             if( m_pDefaultLocaleItem == pLocaleItem.get() )
1211                 iDefault = iLocale;
1212 
1213             BinaryOutput aLocaleOut( m_xContext );
1214             implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut );
1215 
1216             aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData();
1217         }
1218         ++iLocale;
1219     }
1220 
1221     // Write header
1222     sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount);
1223     sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault);
1224     aOut.writeInt16( 0 ); // nVersion
1225     aOut.writeInt16( nLocaleCount16 );
1226     aOut.writeInt16( iDefault16 );
1227 
1228     // Write data positions
1229     sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1);
1230     for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1231     {
1232         aOut.writeInt32( nDataPos );
1233 
1234         Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1235         sal_Int32 nSeqLen = rSeq.getLength();
1236         nDataPos += nSeqLen;
1237     }
1238     // Write final position
1239     aOut.writeInt32( nDataPos );
1240 
1241     // Write data
1242     Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream();
1243     if( xOutputStream.is() )
1244     {
1245         for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1246         {
1247             Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1248             xOutputStream->writeBytes( rSeq );
1249         }
1250     }
1251 
1252     Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData();
1253     return aRetSeq;
1254 }
1255 
implWriteLocaleBinary(LocaleItem * pLocaleItem,BinaryOutput & rOut)1256 void StringResourcePersistenceImpl::implWriteLocaleBinary
1257     ( LocaleItem* pLocaleItem, BinaryOutput& rOut )
1258 {
1259     Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream();
1260     if( !xOutputStream.is() )
1261         return;
1262 
1263     Locale& rLocale = pLocaleItem->m_locale;
1264     rOut.writeString( rLocale.Language );
1265     rOut.writeString( rLocale.Country );
1266     rOut.writeString( rLocale.Variant );
1267     implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment );
1268 }
1269 
1270 
1271 // BinaryOutput, helper class for exportBinary
1272 
1273 namespace {
1274 
1275 class BinaryInput
1276 {
1277     Sequence< sal_Int8 >                    m_aData;
1278     Reference< XComponentContext >          m_xContext;
1279 
1280     const sal_Int8*                         m_pData;
1281     sal_Int32                               m_nCurPos;
1282     sal_Int32                               m_nSize;
1283 
1284 public:
1285     BinaryInput( const Sequence< ::sal_Int8 >& aData, Reference< XComponentContext > const & xContext );
1286 
1287     Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize );
1288 
1289     void seek( sal_Int32 nPos );
getPosition() const1290     sal_Int32 getPosition() const
1291         { return m_nCurPos; }
1292 
1293     sal_Int16 readInt16();
1294     sal_Int32 readInt32();
1295     sal_Unicode readUnicodeChar();
1296     OUString readString();
1297 };
1298 
1299 }
1300 
BinaryInput(const Sequence<::sal_Int8> & aData,Reference<XComponentContext> const & xContext)1301 BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData, Reference< XComponentContext > const & xContext )
1302         : m_aData( aData )
1303         , m_xContext( xContext )
1304 {
1305     m_pData = m_aData.getConstArray();
1306     m_nCurPos = 0;
1307     m_nSize = m_aData.getLength();
1308 }
1309 
getInputStreamForSection(sal_Int32 nSize)1310 Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize )
1311 {
1312     Reference< io::XInputStream > xIn;
1313     if( m_nCurPos + nSize <= m_nSize )
1314     {
1315         Reference< io::XOutputStream > xTempOut( io::TempFile::create(m_xContext), UNO_QUERY_THROW );
1316         Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize );
1317         xTempOut->writeBytes( aSection );
1318 
1319         Reference< io::XSeekable> xSeekable( xTempOut, UNO_QUERY );
1320         if( xSeekable.is() )
1321             xSeekable->seek( 0 );
1322 
1323         xIn.set( xTempOut, UNO_QUERY );
1324     }
1325     else
1326         OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" );
1327 
1328     return xIn;
1329 }
1330 
seek(sal_Int32 nPos)1331 void BinaryInput::seek( sal_Int32 nPos )
1332 {
1333     if( nPos <= m_nSize )
1334         m_nCurPos = nPos;
1335     else
1336         OSL_FAIL( "BinaryInput::seek(): Position past end" );
1337 }
1338 
1339 
readInt16()1340 sal_Int16 BinaryInput::readInt16()
1341 {
1342     sal_Int16 nRet = 0;
1343     if( m_nCurPos + 2 <= m_nSize )
1344     {
1345         nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1346         nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1347     }
1348     else
1349         OSL_FAIL( "BinaryInput::readInt16(): Read past end" );
1350 
1351     return nRet;
1352 }
1353 
readInt32()1354 sal_Int32 BinaryInput::readInt32()
1355 {
1356     sal_Int32 nRet = 0;
1357     if( m_nCurPos + 4 <= m_nSize )
1358     {
1359         sal_Int32 nFactor = 1;
1360         for( sal_Int16 i = 0; i < 4; i++ )
1361         {
1362             nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor;
1363             nFactor *= 256;
1364         }
1365     }
1366     else
1367         OSL_FAIL( "BinaryInput::readInt32(): Read past end" );
1368 
1369     return nRet;
1370 }
1371 
readUnicodeChar()1372 sal_Unicode BinaryInput::readUnicodeChar()
1373 {
1374     sal_uInt16 nRet = 0;
1375     if( m_nCurPos + 2 <= m_nSize )
1376     {
1377         nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] );
1378         nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] );
1379     }
1380     else
1381         OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" );
1382 
1383     sal_Unicode cRet = nRet;
1384     return cRet;
1385 }
1386 
readString()1387 OUString BinaryInput::readString()
1388 {
1389     OUStringBuffer aBuf;
1390     sal_Unicode c;
1391     do
1392     {
1393         c = readUnicodeChar();
1394         if( c != 0 )
1395             aBuf.append( c );
1396     }
1397     while( c != 0 );
1398 
1399     OUString aRetStr = aBuf.makeStringAndClear();
1400     return aRetStr;
1401 }
1402 
importBinary(const Sequence<::sal_Int8> & Data)1403 void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
1404 {
1405     // Init: Remove all locales
1406     sal_Int32 nOldLocaleCount = 0;
1407     do
1408     {
1409         Sequence< Locale > aLocaleSeq = getLocales();
1410         nOldLocaleCount = aLocaleSeq.getLength();
1411         if( nOldLocaleCount > 0 )
1412         {
1413             Locale aLocale = aLocaleSeq[0];
1414             removeLocale( aLocale );
1415         }
1416     }
1417     while( nOldLocaleCount > 0 );
1418 
1419     // Import data
1420     BinaryInput aIn( Data, m_xContext );
1421 
1422     aIn.readInt16(); // version
1423     sal_Int32 nLocaleCount = aIn.readInt16();
1424     sal_Int32 iDefault = aIn.readInt16();
1425 
1426     std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] );
1427     for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ )
1428         pPositions[i] = aIn.readInt32();
1429 
1430     // Import locales
1431     LocaleItem* pUseAsDefaultItem = nullptr;
1432     for( sal_Int32 i = 0; i < nLocaleCount; i++ )
1433     {
1434         sal_Int32 nPos = pPositions[i];
1435         aIn.seek( nPos );
1436 
1437         Locale aLocale;
1438         aLocale.Language = aIn.readString();
1439         aLocale.Country = aIn.readString();
1440         aLocale.Variant = aIn.readString();
1441 
1442         sal_Int32 nAfterStringPos = aIn.getPosition();
1443         sal_Int32 nSize = pPositions[i+1] - nAfterStringPos;
1444         Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize );
1445         if( xInput.is() )
1446         {
1447             LocaleItem* pLocaleItem = new LocaleItem( aLocale );
1448             if( iDefault == i )
1449                 pUseAsDefaultItem = pLocaleItem;
1450             m_aLocaleItemVector.emplace_back( pLocaleItem );
1451             implReadPropertiesFile( pLocaleItem, xInput );
1452         }
1453     }
1454 
1455     if( pUseAsDefaultItem != nullptr )
1456         setDefaultLocale( pUseAsDefaultItem->m_locale );
1457 }
1458 
1459 
1460 // Private helper methods
1461 
checkNamingSceme(const OUString & aName,const OUString & aNameBase,Locale & aLocale)1462 static bool checkNamingSceme( const OUString& aName, const OUString& aNameBase,
1463                        Locale& aLocale )
1464 {
1465     bool bSuccess = false;
1466 
1467     sal_Int32 nNameLen = aName.getLength();
1468     sal_Int32 nNameBaseLen = aNameBase.getLength();
1469 
1470     // Name has to start with NameBase followed
1471     // by a '_' and at least one more character
1472     if( aName.startsWith( aNameBase ) && nNameBaseLen < nNameLen-1 &&
1473         aName[nNameBaseLen] == '_' )
1474     {
1475         bSuccess = true;
1476 
1477         /* FIXME-BCP47: this uses '_' underscore character as separator and
1478          * also appends Variant, which can't be blindly changed as it would
1479          * violate the naming scheme in use. */
1480 
1481         sal_Int32 iStart = nNameBaseLen + 1;
1482         sal_Int32 iNext_ = aName.indexOf( '_', iStart );
1483         if( iNext_ != -1 && iNext_ < nNameLen-1 )
1484         {
1485             aLocale.Language = aName.copy( iStart, iNext_ - iStart );
1486 
1487             iStart = iNext_ + 1;
1488             iNext_ = aName.indexOf( '_', iStart );
1489             if( iNext_ != -1 && iNext_ < nNameLen-1 )
1490             {
1491                 aLocale.Country = aName.copy( iStart, iNext_ - iStart );
1492                 aLocale.Variant = aName.copy( iNext_ + 1 );
1493             }
1494             else
1495                 aLocale.Country = aName.copy( iStart );
1496         }
1497         else
1498             aLocale.Language = aName.copy( iStart );
1499     }
1500     return bSuccess;
1501 }
1502 
implLoadAllLocales()1503 void StringResourcePersistenceImpl::implLoadAllLocales()
1504 {
1505     for( auto& pLocaleItem : m_aLocaleItemVector )
1506         if( pLocaleItem )
1507             loadLocale( pLocaleItem.get() );
1508 }
1509 
1510 // Scan locale properties files helper
implScanLocaleNames(const Sequence<OUString> & aContentSeq)1511 void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq )
1512 {
1513     Locale aDefaultLocale;
1514     bool bDefaultFound = false;
1515 
1516     for( const OUString& aCompleteName : aContentSeq )
1517     {
1518         OUString aPureName;
1519         OUString aExtension;
1520         sal_Int32 iDot = aCompleteName.lastIndexOf( '.' );
1521         sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' );
1522         if( iDot != -1 && iDot > iSlash)
1523         {
1524             sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0;
1525             aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom );
1526             aExtension = aCompleteName.copy( iDot + 1 );
1527         }
1528 
1529         if ( aExtension == "properties" )
1530         {
1531             //OUString aName = aInetObj.getBase();
1532             Locale aLocale;
1533 
1534             if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) )
1535             {
1536                 LocaleItem* pLocaleItem = new LocaleItem( aLocale, false );
1537                 m_aLocaleItemVector.emplace_back( pLocaleItem );
1538 
1539                 if( m_pCurrentLocaleItem == nullptr )
1540                     m_pCurrentLocaleItem = pLocaleItem;
1541 
1542                 if( m_pDefaultLocaleItem == nullptr )
1543                 {
1544                     m_pDefaultLocaleItem = pLocaleItem;
1545                     m_bDefaultModified = true;
1546                 }
1547             }
1548         }
1549         else if( !bDefaultFound && aExtension == "default" )
1550         {
1551             if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) )
1552                 bDefaultFound = true;
1553         }
1554     }
1555     if( bDefaultFound )
1556     {
1557         LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false );
1558         if( pLocaleItem )
1559         {
1560             m_pDefaultLocaleItem = pLocaleItem;
1561             m_bDefaultModified = false;
1562         }
1563     }
1564 }
1565 
1566 // Scan locale properties files
implScanLocales()1567 void StringResourcePersistenceImpl::implScanLocales()
1568 {
1569     // Dummy implementation, method not called for this
1570     // base class, but pure virtual not possible-
1571 }
1572 
loadLocale(LocaleItem * pLocaleItem)1573 bool StringResourcePersistenceImpl::loadLocale( LocaleItem* pLocaleItem )
1574 {
1575     bool bSuccess = false;
1576 
1577     OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" );
1578     if( pLocaleItem )
1579     {
1580         if( pLocaleItem->m_bLoaded )
1581         {
1582             bSuccess = true;
1583         }
1584         else
1585         {
1586             bSuccess = implLoadLocale( pLocaleItem );
1587             pLocaleItem->m_bLoaded = true;      // = bSuccess??? -> leads to more tries
1588         }
1589     }
1590     return bSuccess;
1591 }
1592 
implLoadLocale(LocaleItem *)1593 bool StringResourcePersistenceImpl::implLoadLocale( LocaleItem* )
1594 {
1595     // Dummy implementation, method not called for this
1596     // base class, but pure virtual not possible-
1597     return false;
1598 }
1599 
implGetNameScemeForLocaleItem(const LocaleItem * pLocaleItem)1600 static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem )
1601 {
1602     /* FIXME-BCP47: this uses '_' underscore character as separator and
1603      * also appends Variant, which can't be blindly changed as it would
1604      * violate the naming scheme in use. */
1605 
1606     static const char aUnder[] = "_";
1607 
1608     OSL_ENSURE( pLocaleItem,
1609         "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL" );
1610     Locale aLocale = pLocaleItem->m_locale;
1611 
1612     OUString aRetStr = aUnder + aLocale.Language;
1613 
1614     OUString aCountry  = aLocale.Country;
1615     if( !aCountry.isEmpty() )
1616     {
1617         aRetStr += aUnder + aCountry;
1618     }
1619 
1620     OUString aVariant  = aLocale.Variant;
1621     if( !aVariant.isEmpty() )
1622     {
1623         aRetStr += aUnder + aVariant;
1624     }
1625     return aRetStr;
1626 }
1627 
implGetFileNameForLocaleItem(LocaleItem const * pLocaleItem,const OUString & aNameBase)1628 OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem
1629     ( LocaleItem const * pLocaleItem, const OUString& aNameBase )
1630 {
1631     OUString aFileName = aNameBase;
1632     if( aFileName.isEmpty() )
1633         aFileName = aNameBaseDefaultStr;
1634 
1635     aFileName += implGetNameScemeForLocaleItem( pLocaleItem );
1636     return aFileName;
1637 }
1638 
implGetPathForLocaleItem(LocaleItem const * pLocaleItem,const OUString & aNameBase,const OUString & aLocation,bool bDefaultFile)1639 OUString StringResourcePersistenceImpl::implGetPathForLocaleItem
1640     ( LocaleItem const * pLocaleItem, const OUString& aNameBase,
1641       const OUString& aLocation, bool bDefaultFile )
1642 {
1643     OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase );
1644     INetURLObject aInetObj( aLocation );
1645     aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All );
1646     if( bDefaultFile )
1647         aInetObj.setExtension( u"default" );
1648     else
1649         aInetObj.setExtension( u"properties" );
1650     OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1651     return aCompleteFileName;
1652 }
1653 
1654 // White space according to Java property files specification in
1655 // http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)
isWhiteSpace(sal_Unicode c)1656 static bool isWhiteSpace( sal_Unicode c )
1657 {
1658     bool bWhite = ( c == 0x0020 ||      // space
1659                     c == 0x0009 ||      // tab
1660                     c == 0x000a ||      // line feed, not always handled by TextInputStream
1661                     c == 0x000d ||      // carriage return, not always handled by TextInputStream
1662                     c == 0x000C );      // form feed
1663     return bWhite;
1664 }
1665 
skipWhites(const sal_Unicode * pBuf,sal_Int32 nLen,sal_Int32 & ri)1666 static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1667 {
1668     while( ri < nLen )
1669     {
1670         if( !isWhiteSpace( pBuf[ri] ) )
1671             break;
1672         ri++;
1673     }
1674 }
1675 
isHexDigit(sal_Unicode c,sal_uInt16 & nDigitVal)1676 static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
1677 {
1678     bool bRet = true;
1679     if( c >= '0' && c <= '9' )
1680         nDigitVal = c - '0';
1681     else if( c >= 'a' && c <= 'f' )
1682         nDigitVal = c - 'a' + 10;
1683     else if( c >= 'A' && c <= 'F' )
1684         nDigitVal = c - 'A' + 10;
1685     else
1686         bRet = false;
1687     return bRet;
1688 }
1689 
getEscapeChar(const sal_Unicode * pBuf,sal_Int32 nLen,sal_Int32 & ri)1690 static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1691 {
1692     sal_Int32 i = ri;
1693 
1694     sal_Unicode cRet = 0;
1695     sal_Unicode c = pBuf[i];
1696     switch( c )
1697     {
1698         case 't':
1699             cRet = 0x0009;
1700             break;
1701         case 'n':
1702             cRet = 0x000a;
1703             break;
1704         case 'f':
1705             cRet = 0x000c;
1706             break;
1707         case 'r':
1708             cRet = 0x000d;
1709             break;
1710         case '\\':
1711             cRet = '\\';
1712             break;
1713         case 'u':
1714         {
1715             // Skip multiple u
1716             i++;
1717             while( i < nLen && pBuf[i] == 'u' )
1718                 i++;
1719 
1720             // Process hex digits
1721             sal_Int32 nDigitCount = 0;
1722             sal_uInt16 nDigitVal;
1723             while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
1724             {
1725                 cRet = 16 * cRet + nDigitVal;
1726 
1727                 nDigitCount++;
1728                 if( nDigitCount == 4 )
1729                 {
1730                     // Write back position
1731                     ri = i;
1732                     break;
1733                 }
1734                 i++;
1735             }
1736             break;
1737         }
1738         default:
1739             cRet = c;
1740     }
1741 
1742     return cRet;
1743 }
1744 
CheckContinueInNextLine(const Reference<io::XTextInputStream2> & xTextInputStream,OUString & aLine,bool & bEscapePending,const sal_Unicode * & pBuf,sal_Int32 & nLen,sal_Int32 & i)1745 static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream,
1746     OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf,
1747     sal_Int32& nLen, sal_Int32& i )
1748 {
1749     if( !(i == nLen && bEscapePending) )
1750         return;
1751 
1752     bEscapePending = false;
1753 
1754     if( !xTextInputStream->isEOF() )
1755     {
1756         aLine = xTextInputStream->readLine();
1757         nLen = aLine.getLength();
1758         pBuf = aLine.getStr();
1759         i = 0;
1760 
1761         skipWhites( pBuf, nLen, i );
1762     }
1763 }
1764 
implReadPropertiesFile(LocaleItem * pLocaleItem,const Reference<io::XInputStream> & xInputStream)1765 bool StringResourcePersistenceImpl::implReadPropertiesFile
1766     ( LocaleItem* pLocaleItem, const Reference< io::XInputStream >& xInputStream )
1767 {
1768     if( !xInputStream.is() || pLocaleItem == nullptr )
1769         return false;
1770 
1771     Reference< io::XTextInputStream2 > xTextInputStream = io::TextInputStream::create( m_xContext );
1772 
1773     xTextInputStream->setInputStream( xInputStream );
1774 
1775     OUString aEncodingStr = OUString::createFromAscii
1776         ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1777     xTextInputStream->setEncoding( aEncodingStr );
1778 
1779     OUString aLine;
1780     while( !xTextInputStream->isEOF() )
1781     {
1782         aLine = xTextInputStream->readLine();
1783 
1784         sal_Int32 nLen = aLine.getLength();
1785         if( 0 == nLen )
1786             continue;
1787         const sal_Unicode* pBuf = aLine.getStr();
1788         OUStringBuffer aBuf;
1789         sal_Unicode c = 0;
1790         sal_Int32 i = 0;
1791 
1792         skipWhites( pBuf, nLen, i );
1793         if( i == nLen )
1794             continue;   // line contains only white spaces
1795 
1796         // Comment?
1797         c = pBuf[i];
1798         if( c == '#' || c == '!' )
1799             continue;
1800 
1801         // Scan key
1802         OUString aResourceID;
1803         bool bEscapePending = false;
1804         bool bStrComplete = false;
1805         while( i < nLen && !bStrComplete )
1806         {
1807             c = pBuf[i];
1808             if( bEscapePending )
1809             {
1810                 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1811                 bEscapePending = false;
1812             }
1813             else
1814             {
1815                 if( c == '\\' )
1816                 {
1817                     bEscapePending = true;
1818                 }
1819                 else
1820                 {
1821                     if( c == ':' || c == '=' || isWhiteSpace( c ) )
1822                         bStrComplete = true;
1823                     else
1824                         aBuf.append( c );
1825                 }
1826             }
1827             i++;
1828 
1829             CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1830             if( i == nLen )
1831                 bStrComplete = true;
1832 
1833             if( bStrComplete )
1834                 aResourceID = aBuf.makeStringAndClear();
1835         }
1836 
1837         // Ignore lines with empty keys
1838         if( aResourceID.isEmpty() )
1839             continue;
1840 
1841         // Scan value
1842         skipWhites( pBuf, nLen, i );
1843 
1844         OUString aValueStr;
1845         bEscapePending = false;
1846         bStrComplete = false;
1847         while( i < nLen && !bStrComplete )
1848         {
1849             c = pBuf[i];
1850             if( c == 0x000a || c == 0x000d )    // line feed/carriage return, not always handled by TextInputStream
1851             {
1852                 i++;
1853             }
1854             else
1855             {
1856                 if( bEscapePending )
1857                 {
1858                     aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1859                     bEscapePending = false;
1860                 }
1861                 else if( c == '\\' )
1862                     bEscapePending = true;
1863                 else
1864                     aBuf.append( c );
1865                 i++;
1866 
1867                 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1868             }
1869             if( i == nLen )
1870                 bStrComplete = true;
1871 
1872             if( bStrComplete )
1873                 aValueStr = aBuf.makeStringAndClear();
1874         }
1875 
1876         // Push into table
1877         pLocaleItem->m_aIdToStringMap[ aResourceID ] = aValueStr;
1878         implScanIdForNumber( aResourceID );
1879         IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1880         rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++;
1881     }
1882 
1883     return true;
1884 }
1885 
1886 
getHexCharForDigit(sal_uInt16 nDigitVal)1887 static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal )
1888 {
1889     sal_Unicode cRet = ( nDigitVal < 10 ) ? ('0' + nDigitVal) : ('a' + (nDigitVal-10));
1890     return cRet;
1891 }
1892 
implWriteCharToBuffer(OUStringBuffer & aBuf,sal_Unicode cu,bool bKey)1893 static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey )
1894 {
1895     if( cu == '\\' )
1896     {
1897         aBuf.append( '\\' );
1898         aBuf.append( '\\' );
1899     }
1900     else if( cu == 0x000a )
1901     {
1902         aBuf.append( '\\' );
1903         aBuf.append( 'n' );
1904     }
1905     else if( cu == 0x000d )
1906     {
1907         aBuf.append( '\\' );
1908         aBuf.append( 'r' );
1909     }
1910     else if( bKey && cu == '=' )
1911     {
1912         aBuf.append( '\\' );
1913         aBuf.append( '=' );
1914     }
1915     else if( bKey && cu == ':' )
1916     {
1917         aBuf.append( '\\' );
1918         aBuf.append( ':' );
1919     }
1920     // ISO/IEC 8859-1 range according to:
1921     // http://en.wikipedia.org/wiki/ISO/IEC_8859-1
1922     else if( cu >= 0x20 && cu <= 0x7e )
1923     //TODO: Check why (cu >= 0xa0 && cu <= 0xFF)
1924     //is encoded in sample properties files
1925     //else if( (cu >= 0x20 && cu <= 0x7e) ||
1926     //       (cu >= 0xa0 && cu <= 0xFF) )
1927     {
1928         aBuf.append( cu );
1929     }
1930     else
1931     {
1932         // Unicode encoding
1933         aBuf.append( '\\' );
1934         aBuf.append( 'u' );
1935 
1936         sal_uInt16 nVal = cu;
1937         for( sal_uInt16 i = 0 ; i < 4 ; i++ )
1938         {
1939             sal_uInt16 nDigit = nVal / 0x1000;
1940             nVal -= nDigit * 0x1000;
1941             nVal *= 0x10;
1942             aBuf.append( getHexCharForDigit( nDigit ) );
1943         }
1944     }
1945 }
1946 
implWriteStringWithEncoding(const OUString & aStr,Reference<io::XTextOutputStream2> const & xTextOutputStream,bool bKey)1947 static void implWriteStringWithEncoding( const OUString& aStr,
1948     Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey )
1949 {
1950     static const sal_Unicode cLineFeed = 0xa;
1951 
1952     OUStringBuffer aBuf;
1953     sal_Int32 nLen = aStr.getLength();
1954     const sal_Unicode* pSrc = aStr.getStr();
1955     for( sal_Int32 i = 0 ; i < nLen ; i++ )
1956     {
1957         sal_Unicode cu = pSrc[i];
1958         implWriteCharToBuffer( aBuf, cu, bKey );
1959         // TODO?: split long lines
1960     }
1961     if( !bKey )
1962         aBuf.append( cLineFeed );
1963 
1964     OUString aWriteStr = aBuf.makeStringAndClear();
1965     xTextOutputStream->writeString( aWriteStr );
1966 }
1967 
implWritePropertiesFile(LocaleItem const * pLocaleItem,const Reference<io::XOutputStream> & xOutputStream,const OUString & aComment)1968 bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem,
1969     const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment )
1970 {
1971     if( !xOutputStream.is() || pLocaleItem == nullptr )
1972         return false;
1973 
1974     bool bSuccess = false;
1975     Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext);
1976 
1977     xTextOutputStream->setOutputStream( xOutputStream );
1978 
1979     OUString aEncodingStr = OUString::createFromAscii
1980         ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1981     xTextOutputStream->setEncoding( aEncodingStr );
1982 
1983     xTextOutputStream->writeString( aComment );
1984     xTextOutputStream->writeString( "\n" );
1985 
1986     const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
1987     if( !rHashMap.empty() )
1988     {
1989         // Sort ids according to read order
1990         const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1991 
1992         // Find max/min index
1993         auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(),
1994             [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; });
1995         sal_Int32 nMinIndex = itMinMax.first->second;
1996         sal_Int32 nMaxIndex = itMinMax.second->second;
1997         sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1;
1998 
1999         // Create sorted array of pointers to the id strings
2000         std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] );
2001         for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
2002             pIdPtrs[i] = nullptr;
2003         for( const auto& rIndex : rIndexMap )
2004         {
2005             sal_Int32 nIndex = rIndex.second;
2006             pIdPtrs[nIndex - nMinIndex] = &(rIndex.first);
2007         }
2008 
2009         // Write lines in correct order
2010         for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
2011         {
2012             const OUString* pStr = pIdPtrs[i];
2013             if( pStr != nullptr )
2014             {
2015                 OUString aResourceID = *pStr;
2016                 IdToStringMap::const_iterator it = rHashMap.find( aResourceID );
2017                 if( it != rHashMap.end() )
2018                 {
2019                     implWriteStringWithEncoding( aResourceID, xTextOutputStream, true );
2020                     xTextOutputStream->writeString( "=" );
2021                     OUString aValStr = (*it).second;
2022                     implWriteStringWithEncoding( aValStr, xTextOutputStream, false );
2023                 }
2024             }
2025         }
2026     }
2027 
2028     bSuccess = true;
2029 
2030     return bSuccess;
2031 }
2032 
2033 
2034 // StringResourceWithStorageImpl
2035 
2036 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
scripting_StringResourceWithStorageImpl_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2037 scripting_StringResourceWithStorageImpl_get_implementation(
2038     css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2039 {
2040     return cppu::acquire(new StringResourceWithStorageImpl(context));
2041 }
2042 
2043 
StringResourceWithStorageImpl(const Reference<XComponentContext> & rxContext)2044 StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext )
2045     : StringResourceWithStorageImpl_BASE( rxContext )
2046     , m_bStorageChanged( false )
2047 {
2048 }
2049 
2050 
~StringResourceWithStorageImpl()2051 StringResourceWithStorageImpl::~StringResourceWithStorageImpl()
2052 {
2053 }
2054 
2055 
2056 // XServiceInfo
2057 
2058 
getImplementationName()2059 OUString StringResourceWithStorageImpl::getImplementationName(  )
2060 {
2061     return "com.sun.star.comp.scripting.StringResourceWithStorage";
2062 }
2063 
supportsService(const OUString & rServiceName)2064 sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName )
2065 {
2066     return cppu::supportsService(this, rServiceName);
2067 }
2068 
getSupportedServiceNames()2069 Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames(  )
2070 {
2071     return { "com.sun.star.resource.StringResourceWithStorage" };
2072 }
2073 
2074 
2075 // XInitialization
2076 
2077 
initialize(const Sequence<Any> & aArguments)2078 void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments )
2079 {
2080     ::osl::MutexGuard aGuard( getMutex() );
2081 
2082     if ( aArguments.getLength() != 5 )
2083     {
2084         throw RuntimeException(
2085             "StringResourceWithStorageImpl::initialize: invalid number of arguments!" );
2086     }
2087 
2088     bool bOk = (aArguments[0] >>= m_xStorage);
2089     if( bOk && !m_xStorage.is() )
2090         bOk = false;
2091 
2092     if( !bOk )
2093     {
2094         throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid storage", Reference< XInterface >(), 0 );
2095     }
2096 
2097     implInitializeCommonParameters( aArguments );
2098 }
2099 
2100 
2101 // Forwarding calls to base class
2102 
2103 // XModifyBroadcaster
addModifyListener(const Reference<XModifyListener> & aListener)2104 void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2105 {
2106     StringResourceImpl::addModifyListener( aListener );
2107 }
removeModifyListener(const Reference<XModifyListener> & aListener)2108 void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2109 {
2110     StringResourceImpl::removeModifyListener( aListener );
2111 }
2112 
2113 // XStringResourceResolver
resolveString(const OUString & ResourceID)2114 OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID )
2115 {
2116     return StringResourceImpl::resolveString( ResourceID ) ;
2117 }
resolveStringForLocale(const OUString & ResourceID,const Locale & locale)2118 OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2119 {
2120     return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2121 }
hasEntryForId(const OUString & ResourceID)2122 sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID )
2123 {
2124     return StringResourceImpl::hasEntryForId( ResourceID ) ;
2125 }
hasEntryForIdAndLocale(const OUString & ResourceID,const Locale & locale)2126 sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2127     const Locale& locale )
2128 {
2129     return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2130 }
getResourceIDs()2131 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs(  )
2132 {
2133     return StringResourceImpl::getResourceIDs();
2134 }
getResourceIDsForLocale(const Locale & locale)2135 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale
2136     ( const Locale& locale )
2137 {
2138     return StringResourceImpl::getResourceIDsForLocale( locale );
2139 }
getCurrentLocale()2140 Locale StringResourceWithStorageImpl::getCurrentLocale()
2141 {
2142     return StringResourceImpl::getCurrentLocale();
2143 }
getDefaultLocale()2144 Locale StringResourceWithStorageImpl::getDefaultLocale(  )
2145 {
2146     return StringResourceImpl::getDefaultLocale();
2147 }
getLocales()2148 Sequence< Locale > StringResourceWithStorageImpl::getLocales(  )
2149 {
2150     return StringResourceImpl::getLocales();
2151 }
2152 
2153 // XStringResourceManager
isReadOnly()2154 sal_Bool StringResourceWithStorageImpl::isReadOnly()
2155 {
2156     return StringResourceImpl::isReadOnly();
2157 }
setCurrentLocale(const Locale & locale,sal_Bool FindClosestMatch)2158 void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2159 {
2160     StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2161 }
setDefaultLocale(const Locale & locale)2162 void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale )
2163 {
2164     StringResourceImpl::setDefaultLocale( locale );
2165 }
setString(const OUString & ResourceID,const OUString & Str)2166 void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str )
2167 {
2168     StringResourceImpl::setString( ResourceID, Str );
2169 }
setStringForLocale(const OUString & ResourceID,const OUString & Str,const Locale & locale)2170 void StringResourceWithStorageImpl::setStringForLocale
2171     ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2172 {
2173     StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2174 }
removeId(const OUString & ResourceID)2175 void StringResourceWithStorageImpl::removeId( const OUString& ResourceID )
2176 {
2177     StringResourceImpl::removeId( ResourceID );
2178 }
removeIdForLocale(const OUString & ResourceID,const Locale & locale)2179 void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2180 {
2181     StringResourceImpl::removeIdForLocale( ResourceID, locale );
2182 }
newLocale(const Locale & locale)2183 void StringResourceWithStorageImpl::newLocale( const Locale& locale )
2184 {
2185     StringResourceImpl::newLocale( locale );
2186 }
removeLocale(const Locale & locale)2187 void StringResourceWithStorageImpl::removeLocale( const Locale& locale )
2188 {
2189     StringResourceImpl::removeLocale( locale );
2190 }
getUniqueNumericId()2191 sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId(  )
2192 {
2193     return StringResourceImpl::getUniqueNumericId();
2194 }
2195 
2196 // XStringResourcePersistence
store()2197 void StringResourceWithStorageImpl::store()
2198 {
2199     ::osl::MutexGuard aGuard( getMutex() );
2200     implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" );
2201 
2202     bool bStoreAll = m_bStorageChanged;
2203     m_bStorageChanged = false;
2204     if( !m_bModified && !bStoreAll )
2205         return;
2206 
2207     implStoreAtStorage( m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll );
2208     m_bModified = false;
2209 }
2210 
isModified()2211 sal_Bool StringResourceWithStorageImpl::isModified(  )
2212 {
2213     return StringResourcePersistenceImpl::isModified();
2214 }
setComment(const OUString & Comment)2215 void StringResourceWithStorageImpl::setComment( const OUString& Comment )
2216 {
2217     StringResourcePersistenceImpl::setComment( Comment );
2218 }
storeToStorage(const Reference<XStorage> & Storage,const OUString & NameBase,const OUString & Comment)2219 void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage,
2220     const OUString& NameBase, const OUString& Comment )
2221 {
2222     StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2223 }
storeToURL(const OUString & URL,const OUString & NameBase,const OUString & Comment,const Reference<css::task::XInteractionHandler> & Handler)2224 void StringResourceWithStorageImpl::storeToURL( const OUString& URL,
2225     const OUString& NameBase, const OUString& Comment,
2226     const Reference< css::task::XInteractionHandler >& Handler )
2227 {
2228     StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2229 }
exportBinary()2230 Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary(  )
2231 {
2232     return StringResourcePersistenceImpl::exportBinary();
2233 }
importBinary(const Sequence<::sal_Int8> & Data)2234 void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2235 {
2236     StringResourcePersistenceImpl::importBinary( Data );
2237 }
2238 
2239 
2240 // XStringResourceWithStorage
2241 
storeAsStorage(const Reference<XStorage> & Storage)2242 void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage )
2243 {
2244     setStorage( Storage );
2245     store();
2246 }
2247 
setStorage(const Reference<XStorage> & Storage)2248 void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage )
2249 {
2250     ::osl::MutexGuard aGuard( getMutex() );
2251 
2252     if( !Storage.is() )
2253     {
2254         throw IllegalArgumentException( "StringResourceWithStorageImpl::setStorage: invalid storage", Reference< XInterface >(), 0 );
2255     }
2256 
2257     implLoadAllLocales();
2258 
2259     m_xStorage = Storage;
2260     m_bStorageChanged = true;
2261 }
2262 
2263 
2264 // Private helper methods
2265 
2266 
2267 // Scan locale properties files
implScanLocales()2268 void StringResourceWithStorageImpl::implScanLocales()
2269 {
2270     if( m_xStorage.is() )
2271     {
2272         Sequence< OUString > aContentSeq = m_xStorage->getElementNames();
2273         implScanLocaleNames( aContentSeq );
2274     }
2275 
2276     implLoadAllLocales();
2277 }
2278 
2279 // Loading
implLoadLocale(LocaleItem * pLocaleItem)2280 bool StringResourceWithStorageImpl::implLoadLocale( LocaleItem* pLocaleItem )
2281 {
2282     bool bSuccess = false;
2283     try
2284     {
2285         OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties";
2286 
2287         Reference< io::XStream > xElementStream =
2288             m_xStorage->openStreamElement( aStreamName, ElementModes::READ );
2289 
2290         if( xElementStream.is() )
2291         {
2292             Reference< io::XInputStream > xInputStream = xElementStream->getInputStream();
2293             if( xInputStream.is() )
2294             {
2295                 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2296                 xInputStream->closeInput();
2297             }
2298         }
2299     }
2300     catch( uno::Exception& )
2301     {}
2302 
2303     return bSuccess;
2304 }
2305 
2306 
2307 // StringResourceWithLocationImpl
2308 
2309 
2310 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
scripting_StringResourceWithLocationImpl_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2311 scripting_StringResourceWithLocationImpl_get_implementation(
2312     css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2313 {
2314     return cppu::acquire(new StringResourceWithLocationImpl(context));
2315 }
2316 
2317 
2318 
StringResourceWithLocationImpl(const Reference<XComponentContext> & rxContext)2319 StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext )
2320     : StringResourceWithLocationImpl_BASE( rxContext )
2321     , m_bLocationChanged( false )
2322 {
2323 }
2324 
2325 
~StringResourceWithLocationImpl()2326 StringResourceWithLocationImpl::~StringResourceWithLocationImpl()
2327 {
2328 }
2329 
2330 
2331 // XServiceInfo
2332 
2333 
getImplementationName()2334 OUString StringResourceWithLocationImpl::getImplementationName(  )
2335 {
2336     return "com.sun.star.comp.scripting.StringResourceWithLocation";
2337 }
2338 
supportsService(const OUString & rServiceName)2339 sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName )
2340 {
2341     return cppu::supportsService(this, rServiceName);
2342 }
2343 
getSupportedServiceNames()2344 Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames(  )
2345 {
2346     return { "com.sun.star.resource.StringResourceWithLocation" };
2347 }
2348 
2349 
2350 // XInitialization
2351 
2352 
initialize(const Sequence<Any> & aArguments)2353 void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments )
2354 {
2355     ::osl::MutexGuard aGuard( getMutex() );
2356 
2357     if ( aArguments.getLength() != 6 )
2358     {
2359         throw RuntimeException(
2360             "XInitialization::initialize: invalid number of arguments!" );
2361     }
2362 
2363     bool bOk = (aArguments[0] >>= m_aLocation);
2364     sal_Int32 nLen = m_aLocation.getLength();
2365     if( bOk && nLen == 0 )
2366     {
2367         bOk = false;
2368     }
2369     else
2370     {
2371         if( m_aLocation[nLen - 1] != '/' )
2372             m_aLocation += "/";
2373     }
2374 
2375     if( !bOk )
2376     {
2377         throw IllegalArgumentException( "XInitialization::initialize: invalid URL", Reference< XInterface >(), 0 );
2378     }
2379 
2380 
2381     bOk = (aArguments[5] >>= m_xInteractionHandler);
2382     if( !bOk )
2383     {
2384         throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid type", Reference< XInterface >(), 5 );
2385     }
2386 
2387     implInitializeCommonParameters( aArguments );
2388 }
2389 
2390 
2391 // Forwarding calls to base class
2392 
2393 // XModifyBroadcaster
addModifyListener(const Reference<XModifyListener> & aListener)2394 void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2395 {
2396     StringResourceImpl::addModifyListener( aListener );
2397 }
removeModifyListener(const Reference<XModifyListener> & aListener)2398 void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2399 {
2400     StringResourceImpl::removeModifyListener( aListener );
2401 }
2402 
2403 // XStringResourceResolver
resolveString(const OUString & ResourceID)2404 OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID )
2405 {
2406     return StringResourceImpl::resolveString( ResourceID ) ;
2407 }
resolveStringForLocale(const OUString & ResourceID,const Locale & locale)2408 OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2409 {
2410     return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2411 }
hasEntryForId(const OUString & ResourceID)2412 sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID )
2413 {
2414     return StringResourceImpl::hasEntryForId( ResourceID ) ;
2415 }
hasEntryForIdAndLocale(const OUString & ResourceID,const Locale & locale)2416 sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2417     const Locale& locale )
2418 {
2419     return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2420 }
getResourceIDs()2421 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs(  )
2422 {
2423     return StringResourceImpl::getResourceIDs();
2424 }
getResourceIDsForLocale(const Locale & locale)2425 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale
2426     ( const Locale& locale )
2427 {
2428     return StringResourceImpl::getResourceIDsForLocale( locale );
2429 }
getCurrentLocale()2430 Locale StringResourceWithLocationImpl::getCurrentLocale()
2431 {
2432     return StringResourceImpl::getCurrentLocale();
2433 }
getDefaultLocale()2434 Locale StringResourceWithLocationImpl::getDefaultLocale(  )
2435 {
2436     return StringResourceImpl::getDefaultLocale();
2437 }
getLocales()2438 Sequence< Locale > StringResourceWithLocationImpl::getLocales(  )
2439 {
2440     return StringResourceImpl::getLocales();
2441 }
2442 
2443 // XStringResourceManager
isReadOnly()2444 sal_Bool StringResourceWithLocationImpl::isReadOnly()
2445 {
2446     return StringResourceImpl::isReadOnly();
2447 }
setCurrentLocale(const Locale & locale,sal_Bool FindClosestMatch)2448 void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2449 {
2450     StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2451 }
setDefaultLocale(const Locale & locale)2452 void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale )
2453 {
2454     StringResourceImpl::setDefaultLocale( locale );
2455 }
setString(const OUString & ResourceID,const OUString & Str)2456 void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str )
2457 {
2458     StringResourceImpl::setString( ResourceID, Str );
2459 }
setStringForLocale(const OUString & ResourceID,const OUString & Str,const Locale & locale)2460 void StringResourceWithLocationImpl::setStringForLocale
2461     ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2462 {
2463     StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2464 }
removeId(const OUString & ResourceID)2465 void StringResourceWithLocationImpl::removeId( const OUString& ResourceID )
2466 {
2467     StringResourceImpl::removeId( ResourceID );
2468 }
removeIdForLocale(const OUString & ResourceID,const Locale & locale)2469 void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2470 {
2471     StringResourceImpl::removeIdForLocale( ResourceID, locale );
2472 }
newLocale(const Locale & locale)2473 void StringResourceWithLocationImpl::newLocale( const Locale& locale )
2474 {
2475     StringResourceImpl::newLocale( locale );
2476 }
removeLocale(const Locale & locale)2477 void StringResourceWithLocationImpl::removeLocale( const Locale& locale )
2478 {
2479     StringResourceImpl::removeLocale( locale );
2480 }
getUniqueNumericId()2481 sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId(  )
2482 {
2483     return StringResourceImpl::getUniqueNumericId();
2484 }
2485 
2486 // XStringResourcePersistence
store()2487 void StringResourceWithLocationImpl::store()
2488 {
2489     ::osl::MutexGuard aGuard( getMutex() );
2490     implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" );
2491 
2492     bool bStoreAll = m_bLocationChanged;
2493     m_bLocationChanged = false;
2494     if( !m_bModified && !bStoreAll )
2495         return;
2496 
2497     Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccess();
2498     implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2499         xFileAccess, true/*bUsedForStore*/, bStoreAll );
2500     m_bModified = false;
2501 }
2502 
isModified()2503 sal_Bool StringResourceWithLocationImpl::isModified(  )
2504 {
2505     return StringResourcePersistenceImpl::isModified();
2506 }
setComment(const OUString & Comment)2507 void StringResourceWithLocationImpl::setComment( const OUString& Comment )
2508 {
2509     StringResourcePersistenceImpl::setComment( Comment );
2510 }
storeToStorage(const Reference<XStorage> & Storage,const OUString & NameBase,const OUString & Comment)2511 void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage,
2512     const OUString& NameBase, const OUString& Comment )
2513 {
2514     StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2515 }
storeToURL(const OUString & URL,const OUString & NameBase,const OUString & Comment,const Reference<css::task::XInteractionHandler> & Handler)2516 void StringResourceWithLocationImpl::storeToURL( const OUString& URL,
2517     const OUString& NameBase, const OUString& Comment,
2518     const Reference< css::task::XInteractionHandler >& Handler )
2519 {
2520     StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2521 }
exportBinary()2522 Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary(  )
2523 {
2524     return StringResourcePersistenceImpl::exportBinary();
2525 }
importBinary(const Sequence<::sal_Int8> & Data)2526 void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2527 {
2528     StringResourcePersistenceImpl::importBinary( Data );
2529 }
2530 
2531 
2532 // XStringResourceWithLocation
2533 
2534 // XStringResourceWithLocation
storeAsURL(const OUString & URL)2535 void StringResourceWithLocationImpl::storeAsURL( const OUString& URL )
2536 {
2537     setURL( URL );
2538     store();
2539 }
2540 
setURL(const OUString & URL)2541 void StringResourceWithLocationImpl::setURL( const OUString& URL )
2542 {
2543     ::osl::MutexGuard aGuard( getMutex() );
2544     implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" );
2545 
2546     sal_Int32 nLen = URL.getLength();
2547     if( nLen == 0 )
2548     {
2549         throw IllegalArgumentException( "StringResourceWithLocationImpl::setURL: invalid URL", Reference< XInterface >(), 0 );
2550     }
2551 
2552     implLoadAllLocales();
2553 
2554     // Delete files at old location
2555     implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2556         getFileAccess(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ );
2557 
2558     m_aLocation = URL;
2559     m_bLocationChanged = true;
2560 }
2561 
2562 
2563 // Private helper methods
2564 
2565 
2566 // Scan locale properties files
implScanLocales()2567 void StringResourceWithLocationImpl::implScanLocales()
2568 {
2569     const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccess();
2570     if( xFileAccess.is() && xFileAccess->isFolder( m_aLocation ) )
2571     {
2572         Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false );
2573         implScanLocaleNames( aContentSeq );
2574     }
2575 }
2576 
2577 // Loading
implLoadLocale(LocaleItem * pLocaleItem)2578 bool StringResourceWithLocationImpl::implLoadLocale( LocaleItem* pLocaleItem )
2579 {
2580     bool bSuccess = false;
2581 
2582     const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccess();
2583     if( xFileAccess.is() )
2584     {
2585         OUString aCompleteFileName =
2586             implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation );
2587 
2588         Reference< io::XInputStream > xInputStream;
2589         try
2590         {
2591             xInputStream = xFileAccess->openFileRead( aCompleteFileName );
2592         }
2593         catch( Exception& )
2594         {}
2595         if( xInputStream.is() )
2596         {
2597             bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2598             xInputStream->closeInput();
2599         }
2600     }
2601 
2602     return bSuccess;
2603 }
2604 
getFileAccess()2605 const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccess()
2606 {
2607     ::osl::MutexGuard aGuard( getMutex() );
2608 
2609     if( !m_xSFI.is() )
2610     {
2611         m_xSFI = ucb::SimpleFileAccess::create(m_xContext);
2612 
2613         if( m_xSFI.is() && m_xInteractionHandler.is() )
2614             m_xSFI->setInteractionHandler( m_xInteractionHandler );
2615     }
2616     return m_xSFI;
2617 }
2618 
2619 }   // namespace stringresource
2620 
2621 
2622 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2623