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