Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / scripting / source / stringresource / stringresource.cxx
blob64988a5aafafebd822d182bded2833726d6920b8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
35 #include <osl/diagnose.h>
36 #include <o3tl/string_view.hxx>
37 #include <rtl/ref.hxx>
38 #include <rtl/tencinfo.h>
39 #include <rtl/ustrbuf.hxx>
40 #include <tools/urlobj.hxx>
41 #include <unotools/tempfile.hxx>
42 #include <i18nlangtag/languagetag.hxx>
43 #include <sal/log.hxx>
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::uno;
48 using namespace ::com::sun::star::ucb;
49 using namespace ::com::sun::star::util;
50 using namespace ::com::sun::star::embed;
51 using namespace ::com::sun::star::container;
54 namespace stringresource
57 // StringResourceImpl
59 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
60 scripting_StringResourcePersistenceImpl_implementation(
61 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
63 return cppu::acquire(new StringResourcePersistenceImpl(context));
67 StringResourceImpl::StringResourceImpl( const Reference< XComponentContext >& rxContext )
68 : m_xContext( rxContext )
69 , m_pCurrentLocaleItem( nullptr )
70 , m_pDefaultLocaleItem( nullptr )
71 , m_bDefaultModified( false )
72 , m_bModified( false )
73 , m_bReadOnly( false )
74 , m_nNextUniqueNumericId( UNIQUE_NUMBER_NEEDS_INITIALISATION )
79 StringResourceImpl::~StringResourceImpl()
84 // XServiceInfo
86 OUString StringResourceImpl::getImplementationName( )
88 return "com.sun.star.comp.scripting.StringResource";
91 sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName )
93 return cppu::supportsService(this, rServiceName);
96 Sequence< OUString > StringResourceImpl::getSupportedServiceNames( )
98 return { "com.sun.star.resource.StringResource" };
102 // XModifyBroadcaster
104 void StringResourceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
106 if( !aListener.is() )
107 throw RuntimeException();
109 std::unique_lock aGuard( m_aMutex );
110 m_aListenerContainer.addInterface( aGuard, aListener );
113 void StringResourceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
115 if( !aListener.is() )
116 throw RuntimeException();
118 std::unique_lock aGuard( m_aMutex );
119 m_aListenerContainer.removeInterface( aGuard, aListener );
123 // XStringResourceResolver
125 OUString StringResourceImpl::implResolveString
126 ( const OUString& ResourceID, LocaleItem* pLocaleItem )
128 OUString aRetStr;
129 bool bSuccess = false;
130 if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
132 IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
133 if( it != pLocaleItem->m_aIdToStringMap.end() )
135 aRetStr = (*it).second;
136 bSuccess = true;
139 if( !bSuccess )
141 throw css::resource::MissingResourceException( "StringResourceImpl: No entry for ResourceID: " + ResourceID );
143 return aRetStr;
146 OUString StringResourceImpl::resolveString( const OUString& ResourceID )
148 std::unique_lock aGuard( m_aMutex );
149 return implResolveString( ResourceID, m_pCurrentLocaleItem );
152 OUString StringResourceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
154 std::unique_lock aGuard( m_aMutex );
155 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
156 return implResolveString( ResourceID, pLocaleItem );
159 bool StringResourceImpl::implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem )
161 bool bSuccess = false;
162 if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
164 IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
165 if( it != pLocaleItem->m_aIdToStringMap.end() )
166 bSuccess = true;
168 return bSuccess;
171 sal_Bool StringResourceImpl::hasEntryForId( const OUString& ResourceID )
173 std::unique_lock aGuard( m_aMutex );
174 return implHasEntryForId( ResourceID, m_pCurrentLocaleItem );
177 sal_Bool StringResourceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
178 const Locale& locale )
180 std::unique_lock aGuard( m_aMutex );
181 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
182 return implHasEntryForId( ResourceID, pLocaleItem );
185 Sequence< OUString > StringResourceImpl::implGetResourceIDs( LocaleItem* pLocaleItem )
187 Sequence< OUString > aIDSeq( 0 );
188 if( pLocaleItem && loadLocale( pLocaleItem ) )
190 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
191 sal_Int32 nResourceIDCount = rHashMap.size();
192 aIDSeq.realloc( nResourceIDCount );
193 OUString* pStrings = aIDSeq.getArray();
195 int iTarget = 0;
196 for( const auto& rEntry : rHashMap )
198 OUString aStr = rEntry.first;
199 pStrings[iTarget] = aStr;
200 iTarget++;
203 return aIDSeq;
206 Sequence< OUString > StringResourceImpl::getResourceIDsForLocale
207 ( const Locale& locale )
209 std::unique_lock aGuard( m_aMutex );
210 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
211 return implGetResourceIDs( pLocaleItem );
214 Sequence< OUString > StringResourceImpl::getResourceIDs( )
216 std::unique_lock aGuard( m_aMutex );
217 return implGetResourceIDs( m_pCurrentLocaleItem );
220 Locale StringResourceImpl::getCurrentLocale()
222 std::unique_lock aGuard( m_aMutex );
224 Locale aRetLocale;
225 if( m_pCurrentLocaleItem != nullptr )
226 aRetLocale = m_pCurrentLocaleItem->m_locale;
227 return aRetLocale;
230 Locale StringResourceImpl::getDefaultLocale( )
232 std::unique_lock aGuard( m_aMutex );
234 Locale aRetLocale;
235 if( m_pDefaultLocaleItem != nullptr )
236 aRetLocale = m_pDefaultLocaleItem->m_locale;
237 return aRetLocale;
240 Sequence< Locale > StringResourceImpl::getLocales( )
242 std::unique_lock aGuard( m_aMutex );
244 sal_Int32 nSize = m_aLocaleItemVector.size();
245 Sequence< Locale > aLocalSeq( nSize );
246 Locale* pLocales = aLocalSeq.getArray();
247 int iTarget = 0;
248 for( const auto& pLocaleItem : m_aLocaleItemVector )
250 pLocales[iTarget] = pLocaleItem->m_locale;
251 iTarget++;
253 return aLocalSeq;
257 // XStringResourceManager
259 void StringResourceImpl::implCheckReadOnly( const char* pExceptionMsg )
261 if( m_bReadOnly )
263 OUString errorMsg = OUString::createFromAscii( pExceptionMsg );
264 throw NoSupportException( errorMsg );
268 sal_Bool StringResourceImpl::isReadOnly()
270 return m_bReadOnly;
273 void StringResourceImpl::implSetCurrentLocale( std::unique_lock<std::mutex>& rGuard, const Locale& locale,
274 bool FindClosestMatch, bool bUseDefaultIfNoMatch )
276 LocaleItem* pLocaleItem = nullptr;
277 if( FindClosestMatch )
278 pLocaleItem = getClosestMatchItemForLocale( locale );
279 else
280 pLocaleItem = getItemForLocale( locale, true );
282 if( pLocaleItem == nullptr && bUseDefaultIfNoMatch )
283 pLocaleItem = m_pDefaultLocaleItem;
285 if( pLocaleItem != nullptr )
287 (void)loadLocale( pLocaleItem );
288 m_pCurrentLocaleItem = pLocaleItem;
290 // Only notify without modifying
291 implNotifyListeners(rGuard);
295 void StringResourceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
297 std::unique_lock aGuard( m_aMutex );
298 implSetCurrentLocale( aGuard, locale, FindClosestMatch, false/*bUseDefaultIfNoMatch*/ );
301 void StringResourceImpl::setDefaultLocale( const Locale& locale )
303 std::unique_lock aGuard( m_aMutex );
304 implCheckReadOnly( "StringResourceImpl::setDefaultLocale(): Read only" );
306 LocaleItem* pLocaleItem = getItemForLocale( locale, true );
307 if( pLocaleItem && pLocaleItem != m_pDefaultLocaleItem )
309 if( m_pDefaultLocaleItem )
311 m_aChangedDefaultLocaleVector.push_back(
312 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
315 m_pDefaultLocaleItem = pLocaleItem;
316 m_bDefaultModified = true;
317 implModified(aGuard);
321 void StringResourceImpl::implSetString( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID,
322 const OUString& Str, LocaleItem* pLocaleItem )
324 if( !(pLocaleItem != nullptr && loadLocale( pLocaleItem )) )
325 return;
327 IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
329 IdToStringMap::iterator it = rHashMap.find( ResourceID );
330 bool bNew = ( it == rHashMap.end() );
331 if( bNew )
333 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
334 rIndexMap[ ResourceID ] = pLocaleItem->m_nNextIndex++;
335 implScanIdForNumber( ResourceID );
337 rHashMap[ ResourceID ] = Str;
338 pLocaleItem->m_bModified = true;
339 implModified(rGuard);
342 void StringResourceImpl::setString( const OUString& ResourceID, const OUString& Str )
344 std::unique_lock aGuard( m_aMutex );
345 implCheckReadOnly( "StringResourceImpl::setString(): Read only" );
346 implSetString( aGuard, ResourceID, Str, m_pCurrentLocaleItem );
349 void StringResourceImpl::setStringForLocale
350 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
352 std::unique_lock aGuard( m_aMutex );
353 implCheckReadOnly( "StringResourceImpl::setStringForLocale(): Read only" );
354 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
355 implSetString( aGuard, ResourceID, Str, pLocaleItem );
358 void StringResourceImpl::implRemoveId( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem )
360 if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
362 IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
363 IdToStringMap::iterator it = rHashMap.find( ResourceID );
364 if( it == rHashMap.end() )
366 throw css::resource::MissingResourceException( "StringResourceImpl: No entries for ResourceID: " + ResourceID );
368 rHashMap.erase( it );
369 pLocaleItem->m_bModified = true;
370 implModified(rGuard);
374 void StringResourceImpl::removeId( const OUString& ResourceID )
376 std::unique_lock aGuard( m_aMutex );
377 implCheckReadOnly( "StringResourceImpl::removeId(): Read only" );
378 implRemoveId( aGuard, ResourceID, m_pCurrentLocaleItem );
381 void StringResourceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
383 std::unique_lock aGuard( m_aMutex );
384 implCheckReadOnly( "StringResourceImpl::removeIdForLocale(): Read only" );
385 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
386 implRemoveId( aGuard, ResourceID, pLocaleItem );
389 void StringResourceImpl::newLocale( const Locale& locale )
391 std::unique_lock aGuard( m_aMutex );
392 implCheckReadOnly( "StringResourceImpl::newLocale(): Read only" );
394 if( getItemForLocale( locale, false ) != nullptr )
396 throw ElementExistException( "StringResourceImpl: locale already exists" );
399 // TODO?: Check if locale is valid? How?
400 //if (!bValid)
402 // OUString errorMsg("StringResourceImpl: Invalid locale");
403 // throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 0 );
406 LocaleItem* pLocaleItem = new LocaleItem( locale );
407 m_aLocaleItemVector.emplace_back( pLocaleItem );
408 pLocaleItem->m_bModified = true;
410 // Copy strings from default locale
411 LocaleItem* pCopyFromItem = m_pDefaultLocaleItem;
412 if( pCopyFromItem == nullptr )
413 pCopyFromItem = m_pCurrentLocaleItem;
414 if( pCopyFromItem != nullptr && loadLocale( pCopyFromItem ) )
416 const IdToStringMap& rSourceMap = pCopyFromItem->m_aIdToStringMap;
417 IdToStringMap& rTargetMap = pLocaleItem->m_aIdToStringMap;
418 for( const auto& rEntry : rSourceMap )
420 OUString aId = rEntry.first;
421 OUString aStr = rEntry.second;
422 rTargetMap[ aId ] = aStr;
425 const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap;
426 IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap;
427 for( const auto& rIndex : rSourceIndexMap )
429 OUString aId = rIndex.first;
430 sal_Int32 nIndex = rIndex.second;
431 rTargetIndexMap[ aId ] = nIndex;
433 pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex;
436 if( m_pCurrentLocaleItem == nullptr )
437 m_pCurrentLocaleItem = pLocaleItem;
439 if( m_pDefaultLocaleItem == nullptr )
441 m_pDefaultLocaleItem = pLocaleItem;
442 m_bDefaultModified = true;
445 implModified(aGuard);
448 void StringResourceImpl::removeLocale( const Locale& locale )
450 std::unique_lock aGuard( m_aMutex );
451 implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" );
453 LocaleItem* pRemoveItem = getItemForLocale( locale, true );
454 if( !pRemoveItem )
455 return;
457 // Last locale?
458 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
459 if( nLocaleCount > 1 )
461 if( m_pCurrentLocaleItem == pRemoveItem ||
462 m_pDefaultLocaleItem == pRemoveItem )
464 LocaleItem* pFallbackItem = nullptr;
465 for( const auto& pLocaleItem : m_aLocaleItemVector )
467 if( pLocaleItem.get() != pRemoveItem )
469 pFallbackItem = pLocaleItem.get();
470 break;
473 if( m_pCurrentLocaleItem == pRemoveItem )
475 setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ );
477 if( m_pDefaultLocaleItem == pRemoveItem )
479 setDefaultLocale( pFallbackItem->m_locale );
483 auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(),
484 [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; });
485 if (it == m_aLocaleItemVector.end())
486 return;
488 // Remember locale item to delete file while storing
489 m_aDeletedLocaleItemVector.push_back( std::move(*it) );
491 // Last locale?
492 if( nLocaleCount == 1 )
494 m_nNextUniqueNumericId = 0;
495 if( m_pDefaultLocaleItem )
497 m_aChangedDefaultLocaleVector.push_back(
498 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
500 m_pCurrentLocaleItem = nullptr;
501 m_pDefaultLocaleItem = nullptr;
504 m_aLocaleItemVector.erase( it );
506 implModified(aGuard);
509 void StringResourceImpl::implScanIdForNumber( const OUString& ResourceID )
511 const sal_Unicode* pSrc = ResourceID.getStr();
512 sal_Int32 nLen = ResourceID.getLength();
514 sal_Int32 nNumber = 0;
515 for( sal_Int32 i = 0 ; i < nLen ; i++ )
517 sal_Unicode c = pSrc[i];
518 if( c >= '0' && c <= '9' )
520 sal_uInt16 nDigitVal = c - '0';
521 nNumber = 10*nNumber + nDigitVal;
523 else
524 break;
527 if( m_nNextUniqueNumericId < nNumber + 1 )
528 m_nNextUniqueNumericId = nNumber + 1;
531 sal_Int32 StringResourceImpl::getUniqueNumericId( )
533 if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION )
535 implLoadAllLocales();
536 m_nNextUniqueNumericId = 0;
539 if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION )
541 throw NoSupportException( "getUniqueNumericId: Extended sal_Int32 range" );
543 return m_nNextUniqueNumericId;
547 // Private helper methods
549 LocaleItem* StringResourceImpl::getItemForLocale
550 ( const Locale& locale, bool bException )
552 LocaleItem* pRetItem = nullptr;
554 // Search for locale
555 for( auto& pLocaleItem : m_aLocaleItemVector )
557 if( pLocaleItem )
559 Locale& cmp_locale = pLocaleItem->m_locale;
560 if( cmp_locale.Language == locale.Language &&
561 cmp_locale.Country == locale.Country &&
562 cmp_locale.Variant == locale.Variant )
564 pRetItem = pLocaleItem.get();
565 break;
570 if( pRetItem == nullptr && bException )
572 throw IllegalArgumentException( "StringResourceImpl: Invalid locale", Reference< XInterface >(), 0 );
574 return pRetItem;
577 // Returns the LocaleItem for a given locale, if it exists, otherwise NULL.
578 // This method performs a closest match search, at least the language must match.
579 LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale )
581 LocaleItem* pRetItem = nullptr;
583 ::std::vector< Locale > aLocales( m_aLocaleItemVector.size());
584 size_t i = 0;
585 for( const auto& pLocaleItem : m_aLocaleItemVector )
587 aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale());
588 ++i;
590 ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale));
591 if (iFound != aLocales.end())
592 pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get();
594 return pRetItem;
597 void StringResourceImpl::implModified(std::unique_lock<std::mutex>& rGuard)
599 m_bModified = true;
600 implNotifyListeners(rGuard);
603 void StringResourceImpl::implNotifyListeners(std::unique_lock<std::mutex>& rGuard)
605 EventObject aEvent;
606 aEvent.Source = getXWeak();
607 m_aListenerContainer.forEach(rGuard,
608 [&aEvent](const css::uno::Reference<XModifyListener>& xListener)
610 xListener->modified(aEvent);
616 // Loading
618 bool StringResourceImpl::loadLocale( LocaleItem* )
620 // Base implementation has nothing to load
621 return true;
624 void StringResourceImpl::implLoadAllLocales()
626 // Base implementation has nothing to load
630 // StringResourcePersistenceImpl
633 StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext )
634 : StringResourcePersistenceImpl_BASE( rxContext )
639 StringResourcePersistenceImpl::~StringResourcePersistenceImpl()
644 // XServiceInfo
647 OUString StringResourcePersistenceImpl::getImplementationName( )
649 return "com.sun.star.comp.scripting.StringResource";
653 sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName )
655 return cppu::supportsService( this, rServiceName );
659 Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames( )
661 return StringResourceImpl::getSupportedServiceNames();
665 // XInitialization base functionality for derived classes
668 constexpr OUStringLiteral aNameBaseDefaultStr = u"strings";
670 void StringResourcePersistenceImpl::implInitializeCommonParameters
671 ( std::unique_lock<std::mutex>& rGuard, const Sequence< Any >& aArguments )
673 bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly);
674 if( !bReadOnlyOk )
676 throw IllegalArgumentException( "XInitialization::initialize: Expected ReadOnly flag", Reference< XInterface >(), 1 );
679 css::lang::Locale aCurrentLocale;
680 bool bLocaleOk = (aArguments[2] >>= aCurrentLocale);
681 if( !bLocaleOk )
683 throw IllegalArgumentException( "XInitialization::initialize: Expected Locale", Reference< XInterface >(), 2 );
686 bool bNameBaseOk = (aArguments[3] >>= m_aNameBase);
687 if( !bNameBaseOk )
689 throw IllegalArgumentException( "XInitialization::initialize: Expected NameBase string", Reference< XInterface >(), 3 );
691 if( m_aNameBase.isEmpty() )
692 m_aNameBase = aNameBaseDefaultStr;
694 bool bCommentOk = (aArguments[4] >>= m_aComment);
695 if( !bCommentOk )
697 throw IllegalArgumentException( "XInitialization::initialize: Expected Comment string", Reference< XInterface >(), 4 );
700 implScanLocales();
702 implSetCurrentLocale( rGuard, aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ );
706 // Forwarding calls to base class
708 // XModifyBroadcaster
709 void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
711 StringResourceImpl::addModifyListener( aListener );
713 void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
715 StringResourceImpl::removeModifyListener( aListener );
718 // XStringResourceResolver
719 OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID )
721 return StringResourceImpl::resolveString( ResourceID ) ;
723 OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
725 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
727 sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID )
729 return StringResourceImpl::hasEntryForId( ResourceID ) ;
731 sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
732 const Locale& locale )
734 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
736 Locale StringResourcePersistenceImpl::getCurrentLocale()
738 return StringResourceImpl::getCurrentLocale();
740 Locale StringResourcePersistenceImpl::getDefaultLocale( )
742 return StringResourceImpl::getDefaultLocale();
744 Sequence< Locale > StringResourcePersistenceImpl::getLocales( )
746 return StringResourceImpl::getLocales();
749 // XStringResourceManager
750 sal_Bool StringResourcePersistenceImpl::isReadOnly()
752 return StringResourceImpl::isReadOnly();
754 void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
756 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
758 void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale )
760 StringResourceImpl::setDefaultLocale( locale );
762 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs( )
764 return StringResourceImpl::getResourceIDs();
766 void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str )
768 StringResourceImpl::setString( ResourceID, Str );
770 void StringResourcePersistenceImpl::setStringForLocale
771 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
773 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
775 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale
776 ( const Locale& locale )
778 return StringResourceImpl::getResourceIDsForLocale( locale );
780 void StringResourcePersistenceImpl::removeId( const OUString& ResourceID )
782 StringResourceImpl::removeId( ResourceID );
784 void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
786 StringResourceImpl::removeIdForLocale( ResourceID, locale );
788 void StringResourcePersistenceImpl::newLocale( const Locale& locale )
790 StringResourceImpl::newLocale( locale );
792 void StringResourcePersistenceImpl::removeLocale( const Locale& locale )
794 StringResourceImpl::removeLocale( locale );
796 sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId( )
798 return StringResourceImpl::getUniqueNumericId();
802 // XStringResourcePersistence
804 void StringResourcePersistenceImpl::store()
808 sal_Bool StringResourcePersistenceImpl::isModified( )
810 std::unique_lock aGuard( m_aMutex );
812 return m_bModified;
815 void StringResourcePersistenceImpl::setComment( const OUString& Comment )
817 m_aComment = Comment;
820 void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage,
821 const OUString& NameBase, const OUString& Comment )
823 std::unique_lock aGuard( m_aMutex );
825 implStoreAtStorage( NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ );
828 void StringResourcePersistenceImpl::implStoreAtStorage
830 const OUString& aNameBase,
831 const OUString& aComment,
832 const Reference< css::embed::XStorage >& Storage,
833 bool bUsedForStore,
834 bool bStoreAll
837 // Delete files for deleted locales
838 if( bUsedForStore )
840 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
842 if( pLocaleItem )
844 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties";
848 Storage->removeElement( aStreamName );
850 catch( Exception& )
853 pLocaleItem.reset();
856 m_aDeletedLocaleItemVector.clear();
859 for( auto& pLocaleItem : m_aLocaleItemVector )
861 if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) &&
862 loadLocale( pLocaleItem.get() ) )
864 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties";
866 Reference< io::XStream > xElementStream =
867 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
869 uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY );
870 OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" );
871 if ( xProps.is() )
873 OUString aPropName("MediaType");
874 xProps->setPropertyValue( aPropName, uno::Any( OUString("text/plain") ) );
876 aPropName = "UseCommonStoragePasswordEncryption";
877 xProps->setPropertyValue( aPropName, uno::Any( true ) );
880 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
881 if( xOutputStream.is() )
882 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
883 xOutputStream->closeOutput();
885 if( bUsedForStore )
886 pLocaleItem->m_bModified = false;
890 // Delete files for changed defaults
891 if( bUsedForStore )
893 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
895 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default";
899 Storage->removeElement( aStreamName );
901 catch( Exception& )
904 pLocaleItem.reset();
906 m_aChangedDefaultLocaleVector.clear();
909 // Default locale
910 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) )
911 return;
913 OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default";
915 Reference< io::XStream > xElementStream =
916 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
918 // Only create stream without content
919 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
920 xOutputStream->closeOutput();
922 if( bUsedForStore )
923 m_bDefaultModified = false;
926 void StringResourcePersistenceImpl::storeToURL( const OUString& URL,
927 const OUString& NameBase, const OUString& Comment,
928 const Reference< css::task::XInteractionHandler >& Handler )
930 std::unique_lock aGuard( m_aMutex );
932 Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext);
933 if( xFileAccess.is() && Handler.is() )
934 xFileAccess->setInteractionHandler( Handler );
936 implStoreAtLocation( URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/ );
939 void StringResourcePersistenceImpl::implKillRemovedLocaleFiles
941 std::u16string_view Location,
942 const OUString& aNameBase,
943 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
946 // Delete files for deleted locales
947 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
949 if( pLocaleItem )
951 OUString aCompleteFileName =
952 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
953 if( xFileAccess->exists( aCompleteFileName ) )
954 xFileAccess->kill( aCompleteFileName );
956 pLocaleItem.reset();
959 m_aDeletedLocaleItemVector.clear();
962 void StringResourcePersistenceImpl::implKillChangedDefaultFiles
964 std::u16string_view Location,
965 const OUString& aNameBase,
966 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
969 // Delete files for changed defaults
970 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
972 OUString aCompleteFileName =
973 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true );
974 if( xFileAccess->exists( aCompleteFileName ) )
975 xFileAccess->kill( aCompleteFileName );
976 pLocaleItem.reset();
978 m_aChangedDefaultLocaleVector.clear();
981 void StringResourcePersistenceImpl::implStoreAtLocation
983 std::u16string_view Location,
984 const OUString& aNameBase,
985 const OUString& aComment,
986 const Reference< ucb::XSimpleFileAccess3 >& xFileAccess,
987 bool bUsedForStore,
988 bool bStoreAll,
989 bool bKillAll
992 // Delete files for deleted locales
993 if( bUsedForStore || bKillAll )
994 implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess );
996 for( auto& pLocaleItem : m_aLocaleItemVector )
998 if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) &&
999 loadLocale( pLocaleItem.get() ) )
1001 OUString aCompleteFileName =
1002 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
1003 if( xFileAccess->exists( aCompleteFileName ) )
1004 xFileAccess->kill( aCompleteFileName );
1006 if( !bKillAll )
1008 // Create Output stream
1009 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1010 if( xOutputStream.is() )
1012 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
1013 xOutputStream->closeOutput();
1015 if( bUsedForStore )
1016 pLocaleItem->m_bModified = false;
1021 // Delete files for changed defaults
1022 if( bUsedForStore || bKillAll )
1023 implKillChangedDefaultFiles( Location, aNameBase, xFileAccess );
1025 // Default locale
1026 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) )
1027 return;
1029 OUString aCompleteFileName =
1030 implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true );
1031 if( xFileAccess->exists( aCompleteFileName ) )
1032 xFileAccess->kill( aCompleteFileName );
1034 if( !bKillAll )
1036 // Create Output stream
1037 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1038 if( xOutputStream.is() )
1039 xOutputStream->closeOutput();
1041 if( bUsedForStore )
1042 m_bDefaultModified = false;
1047 // BinaryOutput, helper class for exportBinary
1049 class BinaryOutput
1051 rtl::Reference< utl::TempFileFastService > m_xTempFile;
1052 Reference< io::XOutputStream > m_xOutputStream;
1054 public:
1055 explicit BinaryOutput();
1057 const Reference< io::XOutputStream >& getOutputStream() const
1058 { return m_xOutputStream; }
1060 Sequence< ::sal_Int8 > closeAndGetData();
1062 // Template to be used with sal_Int16 and sal_Unicode
1063 template< class T >
1064 void write16BitInt( T n );
1065 void writeInt16( sal_Int16 n )
1066 { write16BitInt( n ); }
1067 void writeUnicodeChar( sal_Unicode n )
1068 { write16BitInt( n ); }
1069 void writeInt32( sal_Int32 n );
1070 void writeString( const OUString& aStr );
1073 BinaryOutput::BinaryOutput()
1075 m_xTempFile = new utl::TempFileFastService;
1076 m_xOutputStream = m_xTempFile;
1079 template< class T >
1080 void BinaryOutput::write16BitInt( T n )
1082 if( !m_xOutputStream.is() )
1083 return;
1085 Sequence< sal_Int8 > aSeq( 2 );
1086 sal_Int8* p = aSeq.getArray();
1088 sal_Int8 nLow = sal_Int8( n & 0xff );
1089 sal_Int8 nHigh = sal_Int8( n >> 8 );
1091 p[0] = nLow;
1092 p[1] = nHigh;
1093 m_xOutputStream->writeBytes( aSeq );
1096 void BinaryOutput::writeInt32( sal_Int32 n )
1098 if( !m_xOutputStream.is() )
1099 return;
1101 Sequence< sal_Int8 > aSeq( 4 );
1102 sal_Int8* p = aSeq.getArray();
1104 for( sal_Int16 i = 0 ; i < 4 ; i++ )
1106 p[i] = sal_Int8( n & 0xff );
1107 n >>= 8;
1109 m_xOutputStream->writeBytes( aSeq );
1112 void BinaryOutput::writeString( const OUString& aStr )
1114 sal_Int32 nLen = aStr.getLength();
1115 const sal_Unicode* pStr = aStr.getStr();
1117 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1118 writeUnicodeChar( pStr[i] );
1120 writeUnicodeChar( 0 );
1123 Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData()
1125 Sequence< ::sal_Int8 > aRetSeq;
1126 if( !m_xOutputStream.is() )
1127 return aRetSeq;
1129 m_xOutputStream->closeOutput();
1131 sal_Int32 nSize = static_cast<sal_Int32>(m_xTempFile->getPosition());
1133 m_xTempFile->seek( 0 );
1134 sal_Int32 nRead = m_xTempFile->readBytes( aRetSeq, nSize );
1135 OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" );
1137 return aRetSeq;
1141 // Binary format:
1143 // Header
1144 // Byte Content
1145 // 0 + 1 sal_Int16: Version, currently 0, low byte first
1146 // 2 + 3 sal_Int16: Locale count = n, low byte first
1147 // 4 + 5 sal_Int16: Default Locale position in Locale list, == n if none
1148 // 6 - 7 sal_Int32: Start index locale block 0, lowest byte first
1149 // (n-1) * sal_Int32: Start index locale block 1 to n, lowest byte first
1150 // 6 + 4*n sal_Int32: "Start index" non existing locale block n+1,
1151 // marks the first invalid index, kind of EOF
1153 // Locale block
1154 // All strings are stored as 2-Byte-0 terminated sequence
1155 // of 16 bit Unicode characters, each with low byte first
1156 // Empty strings only contain the 2-Byte-0
1158 // Members of com.sun.star.lang.Locale
1159 // with l1 = Locale.Language.getLength()
1160 // with l2 = Locale.Country.getLength()
1161 // with l3 = Locale.Variant.getLength()
1162 // pos0 = 0 Locale.Language
1163 // pos1 = 2 * (l1 + 1) Locale.Country
1164 // pos2 = pos1 + 2 * (l2 + 1) Locale.Variant
1165 // pos3 = pos2 + 2 * (l3 + 1)
1166 // pos3 Properties file written by implWritePropertiesFile
1168 Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary( )
1170 BinaryOutput aOut;
1172 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
1173 std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount);
1175 sal_Int32 iLocale = 0;
1176 sal_Int32 iDefault = 0;
1177 for( auto& pLocaleItem : m_aLocaleItemVector )
1179 if( pLocaleItem != nullptr && loadLocale( pLocaleItem.get() ) )
1181 if( m_pDefaultLocaleItem == pLocaleItem.get() )
1182 iDefault = iLocale;
1184 BinaryOutput aLocaleOut;
1185 implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut );
1187 aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData();
1189 ++iLocale;
1192 // Write header
1193 sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount);
1194 sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault);
1195 aOut.writeInt16( 0 ); // nVersion
1196 aOut.writeInt16( nLocaleCount16 );
1197 aOut.writeInt16( iDefault16 );
1199 // Write data positions
1200 sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1);
1201 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1203 aOut.writeInt32( nDataPos );
1205 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1206 sal_Int32 nSeqLen = rSeq.getLength();
1207 nDataPos += nSeqLen;
1209 // Write final position
1210 aOut.writeInt32( nDataPos );
1212 // Write data
1213 Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream();
1214 if( xOutputStream.is() )
1216 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1218 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1219 xOutputStream->writeBytes( rSeq );
1223 Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData();
1224 return aRetSeq;
1227 void StringResourcePersistenceImpl::implWriteLocaleBinary
1228 ( LocaleItem* pLocaleItem, BinaryOutput& rOut )
1230 Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream();
1231 if( !xOutputStream.is() )
1232 return;
1234 Locale& rLocale = pLocaleItem->m_locale;
1235 rOut.writeString( rLocale.Language );
1236 rOut.writeString( rLocale.Country );
1237 rOut.writeString( rLocale.Variant );
1238 implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment );
1242 // BinaryOutput, helper class for exportBinary
1244 namespace {
1246 class BinaryInput
1248 Sequence< sal_Int8 > m_aData;
1250 const sal_Int8* m_pData;
1251 sal_Int32 m_nCurPos;
1252 sal_Int32 m_nSize;
1254 public:
1255 BinaryInput( const Sequence< ::sal_Int8 >& aData );
1257 Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize );
1259 void seek( sal_Int32 nPos );
1260 sal_Int32 getPosition() const
1261 { return m_nCurPos; }
1263 sal_Int16 readInt16();
1264 sal_Int32 readInt32();
1265 sal_Unicode readUnicodeChar();
1266 OUString readString();
1271 BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData )
1272 : m_aData( aData )
1274 m_pData = m_aData.getConstArray();
1275 m_nCurPos = 0;
1276 m_nSize = m_aData.getLength();
1279 Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize )
1281 Reference< io::XInputStream > xIn;
1282 if( m_nCurPos + nSize <= m_nSize )
1284 rtl::Reference< utl::TempFileFastService > xTempOut = new utl::TempFileFastService;
1285 Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize );
1286 xTempOut->writeBytes( aSection );
1287 xTempOut->seek( 0 );
1288 xIn = xTempOut;
1290 else
1291 OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" );
1293 return xIn;
1296 void BinaryInput::seek( sal_Int32 nPos )
1298 if( nPos <= m_nSize )
1299 m_nCurPos = nPos;
1300 else
1301 OSL_FAIL( "BinaryInput::seek(): Position past end" );
1305 sal_Int16 BinaryInput::readInt16()
1307 sal_Int16 nRet = 0;
1308 if( m_nCurPos + 2 <= m_nSize )
1310 nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1311 nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1313 else
1314 OSL_FAIL( "BinaryInput::readInt16(): Read past end" );
1316 return nRet;
1319 sal_Int32 BinaryInput::readInt32()
1321 sal_Int32 nRet = 0;
1322 if( m_nCurPos + 4 <= m_nSize )
1324 sal_Int32 nFactor = 1;
1325 for( sal_Int16 i = 0; i < 4; i++ )
1327 nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor;
1328 nFactor *= 256;
1331 else
1332 OSL_FAIL( "BinaryInput::readInt32(): Read past end" );
1334 return nRet;
1337 sal_Unicode BinaryInput::readUnicodeChar()
1339 sal_uInt16 nRet = 0;
1340 if( m_nCurPos + 2 <= m_nSize )
1342 nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] );
1343 nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] );
1345 else
1346 OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" );
1348 sal_Unicode cRet = nRet;
1349 return cRet;
1352 OUString BinaryInput::readString()
1354 OUStringBuffer aBuf;
1355 sal_Unicode c;
1358 c = readUnicodeChar();
1359 if( c != 0 )
1360 aBuf.append( c );
1362 while( c != 0 );
1364 OUString aRetStr = aBuf.makeStringAndClear();
1365 return aRetStr;
1368 void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
1370 // Init: Remove all locales
1371 sal_Int32 nOldLocaleCount = 0;
1374 Sequence< Locale > aLocaleSeq = getLocales();
1375 nOldLocaleCount = aLocaleSeq.getLength();
1376 if( nOldLocaleCount > 0 )
1378 Locale aLocale = aLocaleSeq[0];
1379 removeLocale( aLocale );
1382 while( nOldLocaleCount > 0 );
1384 // Import data
1385 BinaryInput aIn( Data );
1387 aIn.readInt16(); // version
1388 sal_Int32 nLocaleCount = aIn.readInt16();
1389 sal_Int32 iDefault = aIn.readInt16();
1391 std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] );
1392 for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ )
1393 pPositions[i] = aIn.readInt32();
1395 // Import locales
1396 LocaleItem* pUseAsDefaultItem = nullptr;
1397 for( sal_Int32 i = 0; i < nLocaleCount; i++ )
1399 sal_Int32 nPos = pPositions[i];
1400 aIn.seek( nPos );
1402 Locale aLocale;
1403 aLocale.Language = aIn.readString();
1404 aLocale.Country = aIn.readString();
1405 aLocale.Variant = aIn.readString();
1407 sal_Int32 nAfterStringPos = aIn.getPosition();
1408 sal_Int32 nSize = pPositions[i+1] - nAfterStringPos;
1409 Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize );
1410 if( xInput.is() )
1412 LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale) );
1413 if( iDefault == i )
1414 pUseAsDefaultItem = pLocaleItem;
1415 m_aLocaleItemVector.emplace_back( pLocaleItem );
1416 implReadPropertiesFile( pLocaleItem, xInput );
1420 if( pUseAsDefaultItem != nullptr )
1421 setDefaultLocale( pUseAsDefaultItem->m_locale );
1425 // Private helper methods
1427 static bool checkNamingSceme( std::u16string_view aName, std::u16string_view aNameBase,
1428 Locale& aLocale )
1430 bool bSuccess = false;
1432 size_t nNameLen = aName.size();
1433 size_t nNameBaseLen = aNameBase.size();
1435 // Name has to start with NameBase followed
1436 // by a '_' and at least one more character
1437 if( o3tl::starts_with(aName, aNameBase) && nNameBaseLen < nNameLen-1 &&
1438 aName[nNameBaseLen] == '_' )
1440 bSuccess = true;
1442 /* FIXME-BCP47: this uses '_' underscore character as separator and
1443 * also appends Variant, which can't be blindly changed as it would
1444 * violate the naming scheme in use. */
1446 sal_Int32 iStart = nNameBaseLen + 1;
1447 size_t iNext_ = aName.find( '_', iStart );
1448 if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
1450 aLocale.Language = aName.substr( iStart, iNext_ - iStart );
1452 iStart = iNext_ + 1;
1453 iNext_ = aName.find( '_', iStart );
1454 if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
1456 aLocale.Country = aName.substr( iStart, iNext_ - iStart );
1457 aLocale.Variant = aName.substr( iNext_ + 1 );
1459 else
1460 aLocale.Country = aName.substr( iStart );
1462 else
1463 aLocale.Language = aName.substr( iStart );
1465 return bSuccess;
1468 void StringResourcePersistenceImpl::implLoadAllLocales()
1470 for( auto& pLocaleItem : m_aLocaleItemVector )
1471 if( pLocaleItem )
1472 loadLocale( pLocaleItem.get() );
1475 // Scan locale properties files helper
1476 void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq )
1478 Locale aDefaultLocale;
1479 bool bDefaultFound = false;
1481 for( const OUString& aCompleteName : aContentSeq )
1483 OUString aPureName;
1484 OUString aExtension;
1485 sal_Int32 iDot = aCompleteName.lastIndexOf( '.' );
1486 sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' );
1487 if( iDot != -1 && iDot > iSlash)
1489 sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0;
1490 aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom );
1491 aExtension = aCompleteName.copy( iDot + 1 );
1494 if ( aExtension == "properties" )
1496 //OUString aName = aInetObj.getBase();
1497 Locale aLocale;
1499 if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) )
1501 LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale), false );
1502 m_aLocaleItemVector.emplace_back( pLocaleItem );
1504 if( m_pCurrentLocaleItem == nullptr )
1505 m_pCurrentLocaleItem = pLocaleItem;
1507 if( m_pDefaultLocaleItem == nullptr )
1509 m_pDefaultLocaleItem = pLocaleItem;
1510 m_bDefaultModified = true;
1514 else if( !bDefaultFound && aExtension == "default" )
1516 if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) )
1517 bDefaultFound = true;
1520 if( bDefaultFound )
1522 LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false );
1523 if( pLocaleItem )
1525 m_pDefaultLocaleItem = pLocaleItem;
1526 m_bDefaultModified = false;
1531 // Scan locale properties files
1532 void StringResourcePersistenceImpl::implScanLocales()
1534 // Dummy implementation, method not called for this
1535 // base class, but pure virtual not possible-
1538 bool StringResourcePersistenceImpl::loadLocale( LocaleItem* pLocaleItem )
1540 bool bSuccess = false;
1542 OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" );
1543 if( pLocaleItem )
1545 if( pLocaleItem->m_bLoaded )
1547 bSuccess = true;
1549 else
1551 bSuccess = implLoadLocale( pLocaleItem );
1552 pLocaleItem->m_bLoaded = true; // = bSuccess??? -> leads to more tries
1555 return bSuccess;
1558 bool StringResourcePersistenceImpl::implLoadLocale( LocaleItem* )
1560 // Dummy implementation, method not called for this
1561 // base class, but pure virtual not possible-
1562 return false;
1565 static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem )
1567 /* FIXME-BCP47: this uses '_' underscore character as separator and
1568 * also appends Variant, which can't be blindly changed as it would
1569 * violate the naming scheme in use. */
1571 static const char aUnder[] = "_";
1573 OSL_ENSURE( pLocaleItem,
1574 "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL" );
1575 Locale aLocale = pLocaleItem->m_locale;
1577 OUString aRetStr = aUnder + aLocale.Language;
1579 OUString aCountry = aLocale.Country;
1580 if( !aCountry.isEmpty() )
1582 aRetStr += aUnder + aCountry;
1585 OUString aVariant = aLocale.Variant;
1586 if( !aVariant.isEmpty() )
1588 aRetStr += aUnder + aVariant;
1590 return aRetStr;
1593 OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem
1594 ( LocaleItem const * pLocaleItem, const OUString& aNameBase )
1596 OUString aFileName = aNameBase;
1597 if( aFileName.isEmpty() )
1598 aFileName = aNameBaseDefaultStr;
1600 aFileName += implGetNameScemeForLocaleItem( pLocaleItem );
1601 return aFileName;
1604 OUString StringResourcePersistenceImpl::implGetPathForLocaleItem
1605 ( LocaleItem const * pLocaleItem, const OUString& aNameBase,
1606 std::u16string_view aLocation, bool bDefaultFile )
1608 OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase );
1609 INetURLObject aInetObj( aLocation );
1610 aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All );
1611 if( bDefaultFile )
1612 aInetObj.setExtension( u"default" );
1613 else
1614 aInetObj.setExtension( u"properties" );
1615 OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1616 return aCompleteFileName;
1619 // White space according to Java property files specification in
1620 // http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)
1621 static bool isWhiteSpace( sal_Unicode c )
1623 bool bWhite = ( c == 0x0020 || // space
1624 c == 0x0009 || // tab
1625 c == 0x000a || // line feed, not always handled by TextInputStream
1626 c == 0x000d || // carriage return, not always handled by TextInputStream
1627 c == 0x000C ); // form feed
1628 return bWhite;
1631 static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1633 while( ri < nLen )
1635 if( !isWhiteSpace( pBuf[ri] ) )
1636 break;
1637 ri++;
1641 static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
1643 bool bRet = true;
1644 if( c >= '0' && c <= '9' )
1645 nDigitVal = c - '0';
1646 else if( c >= 'a' && c <= 'f' )
1647 nDigitVal = c - 'a' + 10;
1648 else if( c >= 'A' && c <= 'F' )
1649 nDigitVal = c - 'A' + 10;
1650 else
1651 bRet = false;
1652 return bRet;
1655 static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1657 sal_Int32 i = ri;
1659 sal_Unicode cRet = 0;
1660 sal_Unicode c = pBuf[i];
1661 switch( c )
1663 case 't':
1664 cRet = 0x0009;
1665 break;
1666 case 'n':
1667 cRet = 0x000a;
1668 break;
1669 case 'f':
1670 cRet = 0x000c;
1671 break;
1672 case 'r':
1673 cRet = 0x000d;
1674 break;
1675 case '\\':
1676 cRet = '\\';
1677 break;
1678 case 'u':
1680 // Skip multiple u
1681 i++;
1682 while( i < nLen && pBuf[i] == 'u' )
1683 i++;
1685 // Process hex digits
1686 sal_Int32 nDigitCount = 0;
1687 sal_uInt16 nDigitVal;
1688 while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
1690 cRet = 16 * cRet + nDigitVal;
1692 nDigitCount++;
1693 if( nDigitCount == 4 )
1695 // Write back position
1696 ri = i;
1697 break;
1699 i++;
1701 break;
1703 default:
1704 cRet = c;
1707 return cRet;
1710 static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream,
1711 OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf,
1712 sal_Int32& nLen, sal_Int32& i )
1714 if( !(i == nLen && bEscapePending) )
1715 return;
1717 bEscapePending = false;
1719 if( !xTextInputStream->isEOF() )
1721 aLine = xTextInputStream->readLine();
1722 nLen = aLine.getLength();
1723 pBuf = aLine.getStr();
1724 i = 0;
1726 skipWhites( pBuf, nLen, i );
1730 bool StringResourcePersistenceImpl::implReadPropertiesFile
1731 ( LocaleItem* pLocaleItem, const Reference< io::XInputStream >& xInputStream )
1733 if( !xInputStream.is() || pLocaleItem == nullptr )
1734 return false;
1736 Reference< io::XTextInputStream2 > xTextInputStream = io::TextInputStream::create( m_xContext );
1738 xTextInputStream->setInputStream( xInputStream );
1740 OUString aEncodingStr = OUString::createFromAscii
1741 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1742 xTextInputStream->setEncoding( aEncodingStr );
1744 OUString aLine;
1745 while( !xTextInputStream->isEOF() )
1747 aLine = xTextInputStream->readLine();
1749 sal_Int32 nLen = aLine.getLength();
1750 if( 0 == nLen )
1751 continue;
1752 const sal_Unicode* pBuf = aLine.getStr();
1753 OUStringBuffer aBuf;
1754 sal_Unicode c = 0;
1755 sal_Int32 i = 0;
1757 skipWhites( pBuf, nLen, i );
1758 if( i == nLen )
1759 continue; // line contains only white spaces
1761 // Comment?
1762 c = pBuf[i];
1763 if( c == '#' || c == '!' )
1764 continue;
1766 // Scan key
1767 OUString aResourceID;
1768 bool bEscapePending = false;
1769 bool bStrComplete = false;
1770 while( i < nLen && !bStrComplete )
1772 c = pBuf[i];
1773 if( bEscapePending )
1775 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1776 bEscapePending = false;
1778 else
1780 if( c == '\\' )
1782 bEscapePending = true;
1784 else
1786 if( c == ':' || c == '=' || isWhiteSpace( c ) )
1787 bStrComplete = true;
1788 else
1789 aBuf.append( c );
1792 i++;
1794 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1795 if( i == nLen )
1796 bStrComplete = true;
1798 if( bStrComplete )
1799 aResourceID = aBuf.makeStringAndClear();
1802 // Ignore lines with empty keys
1803 if( aResourceID.isEmpty() )
1804 continue;
1806 // Scan value
1807 skipWhites( pBuf, nLen, i );
1809 OUString aValueStr;
1810 bEscapePending = false;
1811 bStrComplete = false;
1812 while( i < nLen && !bStrComplete )
1814 c = pBuf[i];
1815 if( c == 0x000a || c == 0x000d ) // line feed/carriage return, not always handled by TextInputStream
1817 i++;
1819 else
1821 if( bEscapePending )
1823 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1824 bEscapePending = false;
1826 else if( c == '\\' )
1827 bEscapePending = true;
1828 else
1829 aBuf.append( c );
1830 i++;
1832 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1834 if( i == nLen )
1835 bStrComplete = true;
1837 if( bStrComplete )
1838 aValueStr = aBuf.makeStringAndClear();
1841 // Push into table
1842 pLocaleItem->m_aIdToStringMap[ aResourceID ] = aValueStr;
1843 implScanIdForNumber( aResourceID );
1844 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1845 rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++;
1848 return true;
1852 static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal )
1854 sal_Unicode cRet = ( nDigitVal < 10 ) ? ('0' + nDigitVal) : ('a' + (nDigitVal-10));
1855 return cRet;
1858 static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey )
1860 if( cu == '\\' )
1862 aBuf.append( '\\' );
1863 aBuf.append( '\\' );
1865 else if( cu == 0x000a )
1867 aBuf.append( '\\' );
1868 aBuf.append( 'n' );
1870 else if( cu == 0x000d )
1872 aBuf.append( '\\' );
1873 aBuf.append( 'r' );
1875 else if( bKey && cu == '=' )
1877 aBuf.append( '\\' );
1878 aBuf.append( '=' );
1880 else if( bKey && cu == ':' )
1882 aBuf.append( '\\' );
1883 aBuf.append( ':' );
1885 // ISO/IEC 8859-1 range according to:
1886 // http://en.wikipedia.org/wiki/ISO/IEC_8859-1
1887 else if( cu >= 0x20 && cu <= 0x7e )
1888 //TODO: Check why (cu >= 0xa0 && cu <= 0xFF)
1889 //is encoded in sample properties files
1890 //else if( (cu >= 0x20 && cu <= 0x7e) ||
1891 // (cu >= 0xa0 && cu <= 0xFF) )
1893 aBuf.append( cu );
1895 else
1897 // Unicode encoding
1898 aBuf.append( '\\' );
1899 aBuf.append( 'u' );
1901 sal_uInt16 nVal = cu;
1902 for( sal_uInt16 i = 0 ; i < 4 ; i++ )
1904 sal_uInt16 nDigit = nVal / 0x1000;
1905 nVal -= nDigit * 0x1000;
1906 nVal *= 0x10;
1907 aBuf.append( getHexCharForDigit( nDigit ) );
1912 static void implWriteStringWithEncoding( const OUString& aStr,
1913 Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey )
1915 static const sal_Unicode cLineFeed = 0xa;
1917 OUStringBuffer aBuf;
1918 sal_Int32 nLen = aStr.getLength();
1919 const sal_Unicode* pSrc = aStr.getStr();
1920 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1922 sal_Unicode cu = pSrc[i];
1923 implWriteCharToBuffer( aBuf, cu, bKey );
1924 // TODO?: split long lines
1926 if( !bKey )
1927 aBuf.append( cLineFeed );
1929 OUString aWriteStr = aBuf.makeStringAndClear();
1930 xTextOutputStream->writeString( aWriteStr );
1933 bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem,
1934 const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment )
1936 if( !xOutputStream.is() || pLocaleItem == nullptr )
1937 return false;
1939 bool bSuccess = false;
1940 Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext);
1942 xTextOutputStream->setOutputStream( xOutputStream );
1944 OUString aEncodingStr = OUString::createFromAscii
1945 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1946 xTextOutputStream->setEncoding( aEncodingStr );
1948 xTextOutputStream->writeString( aComment );
1949 xTextOutputStream->writeString( "\n" );
1951 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
1952 if( !rHashMap.empty() )
1954 // Sort ids according to read order
1955 const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1957 // Find max/min index
1958 auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(),
1959 [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; });
1960 sal_Int32 nMinIndex = itMinMax.first->second;
1961 sal_Int32 nMaxIndex = itMinMax.second->second;
1962 sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1;
1964 // Create sorted array of pointers to the id strings
1965 std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] );
1966 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
1967 pIdPtrs[i] = nullptr;
1968 for( const auto& rIndex : rIndexMap )
1970 sal_Int32 nIndex = rIndex.second;
1971 pIdPtrs[nIndex - nMinIndex] = &(rIndex.first);
1974 // Write lines in correct order
1975 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
1977 const OUString* pStr = pIdPtrs[i];
1978 if( pStr != nullptr )
1980 OUString aResourceID = *pStr;
1981 IdToStringMap::const_iterator it = rHashMap.find( aResourceID );
1982 if( it != rHashMap.end() )
1984 implWriteStringWithEncoding( aResourceID, xTextOutputStream, true );
1985 xTextOutputStream->writeString( "=" );
1986 OUString aValStr = (*it).second;
1987 implWriteStringWithEncoding( aValStr, xTextOutputStream, false );
1993 bSuccess = true;
1995 return bSuccess;
1999 // StringResourceWithStorageImpl
2001 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2002 scripting_StringResourceWithStorageImpl_get_implementation(
2003 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2005 return cppu::acquire(new StringResourceWithStorageImpl(context));
2009 StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext )
2010 : StringResourceWithStorageImpl_BASE( rxContext )
2011 , m_bStorageChanged( false )
2016 StringResourceWithStorageImpl::~StringResourceWithStorageImpl()
2021 // XServiceInfo
2024 OUString StringResourceWithStorageImpl::getImplementationName( )
2026 return "com.sun.star.comp.scripting.StringResourceWithStorage";
2029 sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName )
2031 return cppu::supportsService(this, rServiceName);
2034 Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames( )
2036 return { "com.sun.star.resource.StringResourceWithStorage" };
2040 // XInitialization
2043 void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments )
2045 std::unique_lock aGuard( m_aMutex );
2047 if ( aArguments.getLength() != 5 )
2049 throw RuntimeException(
2050 "StringResourceWithStorageImpl::initialize: invalid number of arguments!" );
2053 bool bOk = (aArguments[0] >>= m_xStorage);
2054 if( bOk && !m_xStorage.is() )
2055 bOk = false;
2057 if( !bOk )
2059 throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid storage", Reference< XInterface >(), 0 );
2062 implInitializeCommonParameters( aGuard, aArguments );
2066 // Forwarding calls to base class
2068 // XModifyBroadcaster
2069 void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2071 StringResourceImpl::addModifyListener( aListener );
2073 void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2075 StringResourceImpl::removeModifyListener( aListener );
2078 // XStringResourceResolver
2079 OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID )
2081 return StringResourceImpl::resolveString( ResourceID ) ;
2083 OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2085 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2087 sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID )
2089 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2091 sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2092 const Locale& locale )
2094 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2096 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs( )
2098 return StringResourceImpl::getResourceIDs();
2100 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale
2101 ( const Locale& locale )
2103 return StringResourceImpl::getResourceIDsForLocale( locale );
2105 Locale StringResourceWithStorageImpl::getCurrentLocale()
2107 return StringResourceImpl::getCurrentLocale();
2109 Locale StringResourceWithStorageImpl::getDefaultLocale( )
2111 return StringResourceImpl::getDefaultLocale();
2113 Sequence< Locale > StringResourceWithStorageImpl::getLocales( )
2115 return StringResourceImpl::getLocales();
2118 // XStringResourceManager
2119 sal_Bool StringResourceWithStorageImpl::isReadOnly()
2121 return StringResourceImpl::isReadOnly();
2123 void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2125 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2127 void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale )
2129 StringResourceImpl::setDefaultLocale( locale );
2131 void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str )
2133 StringResourceImpl::setString( ResourceID, Str );
2135 void StringResourceWithStorageImpl::setStringForLocale
2136 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2138 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2140 void StringResourceWithStorageImpl::removeId( const OUString& ResourceID )
2142 StringResourceImpl::removeId( ResourceID );
2144 void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2146 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2148 void StringResourceWithStorageImpl::newLocale( const Locale& locale )
2150 StringResourceImpl::newLocale( locale );
2152 void StringResourceWithStorageImpl::removeLocale( const Locale& locale )
2154 StringResourceImpl::removeLocale( locale );
2156 sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId( )
2158 return StringResourceImpl::getUniqueNumericId();
2161 // XStringResourcePersistence
2162 void StringResourceWithStorageImpl::store()
2164 std::unique_lock aGuard( m_aMutex );
2165 implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" );
2167 bool bStoreAll = m_bStorageChanged;
2168 m_bStorageChanged = false;
2169 if( !m_bModified && !bStoreAll )
2170 return;
2172 implStoreAtStorage( m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll );
2173 m_bModified = false;
2176 sal_Bool StringResourceWithStorageImpl::isModified( )
2178 return StringResourcePersistenceImpl::isModified();
2180 void StringResourceWithStorageImpl::setComment( const OUString& Comment )
2182 StringResourcePersistenceImpl::setComment( Comment );
2184 void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage,
2185 const OUString& NameBase, const OUString& Comment )
2187 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2189 void StringResourceWithStorageImpl::storeToURL( const OUString& URL,
2190 const OUString& NameBase, const OUString& Comment,
2191 const Reference< css::task::XInteractionHandler >& Handler )
2193 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2195 Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary( )
2197 return StringResourcePersistenceImpl::exportBinary();
2199 void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2201 StringResourcePersistenceImpl::importBinary( Data );
2205 // XStringResourceWithStorage
2207 void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage )
2209 setStorage( Storage );
2210 store();
2213 void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage )
2215 std::unique_lock aGuard( m_aMutex );
2217 if( !Storage.is() )
2219 throw IllegalArgumentException( "StringResourceWithStorageImpl::setStorage: invalid storage", Reference< XInterface >(), 0 );
2222 implLoadAllLocales();
2224 m_xStorage = Storage;
2225 m_bStorageChanged = true;
2229 // Private helper methods
2232 // Scan locale properties files
2233 void StringResourceWithStorageImpl::implScanLocales()
2235 if( m_xStorage.is() )
2237 Sequence< OUString > aContentSeq = m_xStorage->getElementNames();
2238 implScanLocaleNames( aContentSeq );
2241 implLoadAllLocales();
2244 // Loading
2245 bool StringResourceWithStorageImpl::implLoadLocale( LocaleItem* pLocaleItem )
2247 bool bSuccess = false;
2250 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties";
2252 Reference< io::XStream > xElementStream =
2253 m_xStorage->openStreamElement( aStreamName, ElementModes::READ );
2255 if( xElementStream.is() )
2257 Reference< io::XInputStream > xInputStream = xElementStream->getInputStream();
2258 if( xInputStream.is() )
2260 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2261 xInputStream->closeInput();
2265 catch( uno::Exception& )
2268 return bSuccess;
2272 // StringResourceWithLocationImpl
2275 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2276 scripting_StringResourceWithLocationImpl_get_implementation(
2277 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2279 return cppu::acquire(new StringResourceWithLocationImpl(context));
2284 StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext )
2285 : StringResourceWithLocationImpl_BASE( rxContext )
2286 , m_bLocationChanged( false )
2291 StringResourceWithLocationImpl::~StringResourceWithLocationImpl()
2296 // XServiceInfo
2299 OUString StringResourceWithLocationImpl::getImplementationName( )
2301 return "com.sun.star.comp.scripting.StringResourceWithLocation";
2304 sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName )
2306 return cppu::supportsService(this, rServiceName);
2309 Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames( )
2311 return { "com.sun.star.resource.StringResourceWithLocation" };
2315 // XInitialization
2318 void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments )
2320 std::unique_lock aGuard( m_aMutex );
2322 if ( aArguments.getLength() != 6 )
2324 throw RuntimeException(
2325 "XInitialization::initialize: invalid number of arguments!" );
2328 bool bOk = (aArguments[0] >>= m_aLocation);
2329 sal_Int32 nLen = m_aLocation.getLength();
2330 if( bOk && nLen == 0 )
2332 bOk = false;
2334 else
2336 if( m_aLocation[nLen - 1] != '/' )
2337 m_aLocation += "/";
2340 if( !bOk )
2342 throw IllegalArgumentException( "XInitialization::initialize: invalid URL", Reference< XInterface >(), 0 );
2346 bOk = (aArguments[5] >>= m_xInteractionHandler);
2347 if( !bOk )
2349 throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid type", Reference< XInterface >(), 5 );
2352 implInitializeCommonParameters( aGuard, aArguments );
2356 // Forwarding calls to base class
2358 // XModifyBroadcaster
2359 void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2361 StringResourceImpl::addModifyListener( aListener );
2363 void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2365 StringResourceImpl::removeModifyListener( aListener );
2368 // XStringResourceResolver
2369 OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID )
2371 return StringResourceImpl::resolveString( ResourceID ) ;
2373 OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2375 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2377 sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID )
2379 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2381 sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2382 const Locale& locale )
2384 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2386 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs( )
2388 return StringResourceImpl::getResourceIDs();
2390 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale
2391 ( const Locale& locale )
2393 return StringResourceImpl::getResourceIDsForLocale( locale );
2395 Locale StringResourceWithLocationImpl::getCurrentLocale()
2397 return StringResourceImpl::getCurrentLocale();
2399 Locale StringResourceWithLocationImpl::getDefaultLocale( )
2401 return StringResourceImpl::getDefaultLocale();
2403 Sequence< Locale > StringResourceWithLocationImpl::getLocales( )
2405 return StringResourceImpl::getLocales();
2408 // XStringResourceManager
2409 sal_Bool StringResourceWithLocationImpl::isReadOnly()
2411 return StringResourceImpl::isReadOnly();
2413 void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2415 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2417 void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale )
2419 StringResourceImpl::setDefaultLocale( locale );
2421 void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str )
2423 StringResourceImpl::setString( ResourceID, Str );
2425 void StringResourceWithLocationImpl::setStringForLocale
2426 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2428 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2430 void StringResourceWithLocationImpl::removeId( const OUString& ResourceID )
2432 StringResourceImpl::removeId( ResourceID );
2434 void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2436 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2438 void StringResourceWithLocationImpl::newLocale( const Locale& locale )
2440 StringResourceImpl::newLocale( locale );
2442 void StringResourceWithLocationImpl::removeLocale( const Locale& locale )
2444 StringResourceImpl::removeLocale( locale );
2446 sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId( )
2448 return StringResourceImpl::getUniqueNumericId();
2451 // XStringResourcePersistence
2452 void StringResourceWithLocationImpl::store()
2454 std::unique_lock aGuard( m_aMutex );
2455 implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" );
2457 bool bStoreAll = m_bLocationChanged;
2458 m_bLocationChanged = false;
2459 if( !m_bModified && !bStoreAll )
2460 return;
2462 Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2463 implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2464 xFileAccess, true/*bUsedForStore*/, bStoreAll );
2465 m_bModified = false;
2468 sal_Bool StringResourceWithLocationImpl::isModified( )
2470 return StringResourcePersistenceImpl::isModified();
2472 void StringResourceWithLocationImpl::setComment( const OUString& Comment )
2474 StringResourcePersistenceImpl::setComment( Comment );
2476 void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage,
2477 const OUString& NameBase, const OUString& Comment )
2479 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2481 void StringResourceWithLocationImpl::storeToURL( const OUString& URL,
2482 const OUString& NameBase, const OUString& Comment,
2483 const Reference< css::task::XInteractionHandler >& Handler )
2485 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2487 Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary( )
2489 return StringResourcePersistenceImpl::exportBinary();
2491 void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2493 StringResourcePersistenceImpl::importBinary( Data );
2497 // XStringResourceWithLocation
2499 // XStringResourceWithLocation
2500 void StringResourceWithLocationImpl::storeAsURL( const OUString& URL )
2502 setURL( URL );
2503 store();
2506 void StringResourceWithLocationImpl::setURL( const OUString& URL )
2508 std::unique_lock aGuard( m_aMutex );
2509 implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" );
2511 sal_Int32 nLen = URL.getLength();
2512 if( nLen == 0 )
2514 throw IllegalArgumentException( "StringResourceWithLocationImpl::setURL: invalid URL", Reference< XInterface >(), 0 );
2517 implLoadAllLocales();
2519 // Delete files at old location
2520 implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2521 getFileAccessImpl(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ );
2523 m_aLocation = URL;
2524 m_bLocationChanged = true;
2528 // Private helper methods
2531 // Scan locale properties files
2532 void StringResourceWithLocationImpl::implScanLocales()
2534 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2535 if( xFileAccess.is() && xFileAccess->isFolder( m_aLocation ) )
2537 Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false );
2538 implScanLocaleNames( aContentSeq );
2542 // Loading
2543 bool StringResourceWithLocationImpl::implLoadLocale( LocaleItem* pLocaleItem )
2545 bool bSuccess = false;
2547 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2548 if( xFileAccess.is() )
2550 OUString aCompleteFileName =
2551 implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation );
2553 Reference< io::XInputStream > xInputStream;
2556 xInputStream = xFileAccess->openFileRead( aCompleteFileName );
2558 catch( Exception& )
2560 if( xInputStream.is() )
2562 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2563 xInputStream->closeInput();
2567 return bSuccess;
2570 const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccessImpl()
2572 if( !m_xSFI.is() )
2574 m_xSFI = ucb::SimpleFileAccess::create(m_xContext);
2576 if( m_xSFI.is() && m_xInteractionHandler.is() )
2577 m_xSFI->setInteractionHandler( m_xInteractionHandler );
2579 return m_xSFI;
2582 } // namespace stringresource
2585 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */