nss: upgrade to release 3.73
[LibreOffice.git] / scripting / source / stringresource / stringresource.cxx
blob455174f2eff80a0319a89b250658608476f9678f
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/implementationentry.hxx>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/container/ElementExistException.hpp>
34 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
36 #include <osl/diagnose.h>
37 #include <rtl/tencinfo.h>
38 #include <rtl/ustrbuf.hxx>
39 #include <tools/urlobj.hxx>
40 #include <i18nlangtag/languagetag.hxx>
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::lang;
44 using namespace ::com::sun::star::uno;
45 using namespace ::com::sun::star::ucb;
46 using namespace ::com::sun::star::util;
47 using namespace ::com::sun::star::embed;
48 using namespace ::com::sun::star::container;
51 namespace stringresource
55 // mutex
58 ::osl::Mutex& getMutex()
60 static ::osl::Mutex s_aMutex;
62 return s_aMutex;
66 // StringResourceImpl
68 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
69 scripting_StringResourcePersistenceImpl_implementation(
70 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
72 return cppu::acquire(new StringResourcePersistenceImpl(context));
76 StringResourceImpl::StringResourceImpl( const Reference< XComponentContext >& rxContext )
77 : m_xContext( rxContext )
78 , m_pCurrentLocaleItem( nullptr )
79 , m_pDefaultLocaleItem( nullptr )
80 , m_bDefaultModified( false )
81 , m_aListenerContainer( getMutex() )
82 , m_bModified( false )
83 , m_bReadOnly( false )
84 , m_nNextUniqueNumericId( UNIQUE_NUMBER_NEEDS_INITIALISATION )
89 StringResourceImpl::~StringResourceImpl()
94 // XServiceInfo
96 OUString StringResourceImpl::getImplementationName( )
98 return "com.sun.star.comp.scripting.StringResource";
101 sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName )
103 return cppu::supportsService(this, rServiceName);
106 Sequence< OUString > StringResourceImpl::getSupportedServiceNames( )
108 return { "com.sun.star.resource.StringResource" };
112 // XModifyBroadcaster
114 void StringResourceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
116 if( !aListener.is() )
117 throw RuntimeException();
119 ::osl::MutexGuard aGuard( getMutex() );
120 m_aListenerContainer.addInterface( Reference<XInterface>( aListener, UNO_QUERY ) );
123 void StringResourceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
125 if( !aListener.is() )
126 throw RuntimeException();
128 ::osl::MutexGuard aGuard( getMutex() );
129 m_aListenerContainer.removeInterface( Reference<XInterface>( aListener, UNO_QUERY ) );
133 // XStringResourceResolver
135 OUString StringResourceImpl::implResolveString
136 ( const OUString& ResourceID, LocaleItem* pLocaleItem )
138 OUString aRetStr;
139 bool bSuccess = false;
140 if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
142 IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
143 if( it != pLocaleItem->m_aIdToStringMap.end() )
145 aRetStr = (*it).second;
146 bSuccess = true;
149 if( !bSuccess )
151 throw css::resource::MissingResourceException( "StringResourceImpl: No entry for ResourceID: " + ResourceID );
153 return aRetStr;
156 OUString StringResourceImpl::resolveString( const OUString& ResourceID )
158 ::osl::MutexGuard aGuard( getMutex() );
159 return implResolveString( ResourceID, m_pCurrentLocaleItem );
162 OUString StringResourceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
164 ::osl::MutexGuard aGuard( getMutex() );
165 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
166 return implResolveString( ResourceID, pLocaleItem );
169 bool StringResourceImpl::implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem )
171 bool bSuccess = false;
172 if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
174 IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
175 if( it != pLocaleItem->m_aIdToStringMap.end() )
176 bSuccess = true;
178 return bSuccess;
181 sal_Bool StringResourceImpl::hasEntryForId( const OUString& ResourceID )
183 ::osl::MutexGuard aGuard( getMutex() );
184 return implHasEntryForId( ResourceID, m_pCurrentLocaleItem );
187 sal_Bool StringResourceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
188 const Locale& locale )
190 ::osl::MutexGuard aGuard( getMutex() );
191 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
192 return implHasEntryForId( ResourceID, pLocaleItem );
195 Sequence< OUString > StringResourceImpl::implGetResourceIDs( LocaleItem* pLocaleItem )
197 Sequence< OUString > aIDSeq( 0 );
198 if( pLocaleItem && loadLocale( pLocaleItem ) )
200 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
201 sal_Int32 nResourceIDCount = rHashMap.size();
202 aIDSeq.realloc( nResourceIDCount );
203 OUString* pStrings = aIDSeq.getArray();
205 int iTarget = 0;
206 for( const auto& rEntry : rHashMap )
208 OUString aStr = rEntry.first;
209 pStrings[iTarget] = aStr;
210 iTarget++;
213 return aIDSeq;
216 Sequence< OUString > StringResourceImpl::getResourceIDsForLocale
217 ( const Locale& locale )
219 ::osl::MutexGuard aGuard( getMutex() );
220 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
221 return implGetResourceIDs( pLocaleItem );
224 Sequence< OUString > StringResourceImpl::getResourceIDs( )
226 ::osl::MutexGuard aGuard( getMutex() );
227 return implGetResourceIDs( m_pCurrentLocaleItem );
230 Locale StringResourceImpl::getCurrentLocale()
232 ::osl::MutexGuard aGuard( getMutex() );
234 Locale aRetLocale;
235 if( m_pCurrentLocaleItem != nullptr )
236 aRetLocale = m_pCurrentLocaleItem->m_locale;
237 return aRetLocale;
240 Locale StringResourceImpl::getDefaultLocale( )
242 ::osl::MutexGuard aGuard( getMutex() );
244 Locale aRetLocale;
245 if( m_pDefaultLocaleItem != nullptr )
246 aRetLocale = m_pDefaultLocaleItem->m_locale;
247 return aRetLocale;
250 Sequence< Locale > StringResourceImpl::getLocales( )
252 ::osl::MutexGuard aGuard( getMutex() );
254 sal_Int32 nSize = m_aLocaleItemVector.size();
255 Sequence< Locale > aLocalSeq( nSize );
256 Locale* pLocales = aLocalSeq.getArray();
257 int iTarget = 0;
258 for( const auto& pLocaleItem : m_aLocaleItemVector )
260 pLocales[iTarget] = pLocaleItem->m_locale;
261 iTarget++;
263 return aLocalSeq;
267 // XStringResourceManager
269 void StringResourceImpl::implCheckReadOnly( const char* pExceptionMsg )
271 if( m_bReadOnly )
273 OUString errorMsg = OUString::createFromAscii( pExceptionMsg );
274 throw NoSupportException( errorMsg );
278 sal_Bool StringResourceImpl::isReadOnly()
280 return m_bReadOnly;
283 void StringResourceImpl::implSetCurrentLocale( const Locale& locale,
284 bool FindClosestMatch, bool bUseDefaultIfNoMatch )
286 ::osl::MutexGuard aGuard( getMutex() );
288 LocaleItem* pLocaleItem = nullptr;
289 if( FindClosestMatch )
290 pLocaleItem = getClosestMatchItemForLocale( locale );
291 else
292 pLocaleItem = getItemForLocale( locale, true );
294 if( pLocaleItem == nullptr && bUseDefaultIfNoMatch )
295 pLocaleItem = m_pDefaultLocaleItem;
297 if( pLocaleItem != nullptr )
299 (void)loadLocale( pLocaleItem );
300 m_pCurrentLocaleItem = pLocaleItem;
302 // Only notify without modifying
303 implNotifyListeners();
307 void StringResourceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
309 implSetCurrentLocale( locale, FindClosestMatch, false/*bUseDefaultIfNoMatch*/ );
312 void StringResourceImpl::setDefaultLocale( const Locale& locale )
314 ::osl::MutexGuard aGuard( getMutex() );
315 implCheckReadOnly( "StringResourceImpl::setDefaultLocale(): Read only" );
317 LocaleItem* pLocaleItem = getItemForLocale( locale, true );
318 if( pLocaleItem && pLocaleItem != m_pDefaultLocaleItem )
320 if( m_pDefaultLocaleItem )
322 m_aChangedDefaultLocaleVector.push_back(
323 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
326 m_pDefaultLocaleItem = pLocaleItem;
327 m_bDefaultModified = true;
328 implModified();
332 void StringResourceImpl::implSetString( const OUString& ResourceID,
333 const OUString& Str, LocaleItem* pLocaleItem )
335 if( !(pLocaleItem != nullptr && loadLocale( pLocaleItem )) )
336 return;
338 IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
340 IdToStringMap::iterator it = rHashMap.find( ResourceID );
341 bool bNew = ( it == rHashMap.end() );
342 if( bNew )
344 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
345 rIndexMap[ ResourceID ] = pLocaleItem->m_nNextIndex++;
346 implScanIdForNumber( ResourceID );
348 rHashMap[ ResourceID ] = Str;
349 pLocaleItem->m_bModified = true;
350 implModified();
353 void StringResourceImpl::setString( const OUString& ResourceID, const OUString& Str )
355 ::osl::MutexGuard aGuard( getMutex() );
356 implCheckReadOnly( "StringResourceImpl::setString(): Read only" );
357 implSetString( ResourceID, Str, m_pCurrentLocaleItem );
360 void StringResourceImpl::setStringForLocale
361 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
363 ::osl::MutexGuard aGuard( getMutex() );
364 implCheckReadOnly( "StringResourceImpl::setStringForLocale(): Read only" );
365 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
366 implSetString( ResourceID, Str, pLocaleItem );
369 void StringResourceImpl::implRemoveId( const OUString& ResourceID, LocaleItem* pLocaleItem )
371 if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
373 IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
374 IdToStringMap::iterator it = rHashMap.find( ResourceID );
375 if( it == rHashMap.end() )
377 throw css::resource::MissingResourceException( "StringResourceImpl: No entries for ResourceID: " + ResourceID );
379 rHashMap.erase( it );
380 pLocaleItem->m_bModified = true;
381 implModified();
385 void StringResourceImpl::removeId( const OUString& ResourceID )
387 ::osl::MutexGuard aGuard( getMutex() );
388 implCheckReadOnly( "StringResourceImpl::removeId(): Read only" );
389 implRemoveId( ResourceID, m_pCurrentLocaleItem );
392 void StringResourceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
394 ::osl::MutexGuard aGuard( getMutex() );
395 implCheckReadOnly( "StringResourceImpl::removeIdForLocale(): Read only" );
396 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
397 implRemoveId( ResourceID, pLocaleItem );
400 void StringResourceImpl::newLocale( const Locale& locale )
402 ::osl::MutexGuard aGuard( getMutex() );
403 implCheckReadOnly( "StringResourceImpl::newLocale(): Read only" );
405 if( getItemForLocale( locale, false ) != nullptr )
407 throw ElementExistException( "StringResourceImpl: locale already exists" );
410 // TODO?: Check if locale is valid? How?
411 //if (!bValid)
413 // OUString errorMsg("StringResourceImpl: Invalid locale");
414 // throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 0 );
417 LocaleItem* pLocaleItem = new LocaleItem( locale );
418 m_aLocaleItemVector.emplace_back( pLocaleItem );
419 pLocaleItem->m_bModified = true;
421 // Copy strings from default locale
422 LocaleItem* pCopyFromItem = m_pDefaultLocaleItem;
423 if( pCopyFromItem == nullptr )
424 pCopyFromItem = m_pCurrentLocaleItem;
425 if( pCopyFromItem != nullptr && loadLocale( pCopyFromItem ) )
427 const IdToStringMap& rSourceMap = pCopyFromItem->m_aIdToStringMap;
428 IdToStringMap& rTargetMap = pLocaleItem->m_aIdToStringMap;
429 for( const auto& rEntry : rSourceMap )
431 OUString aId = rEntry.first;
432 OUString aStr = rEntry.second;
433 rTargetMap[ aId ] = aStr;
436 const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap;
437 IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap;
438 for( const auto& rIndex : rSourceIndexMap )
440 OUString aId = rIndex.first;
441 sal_Int32 nIndex = rIndex.second;
442 rTargetIndexMap[ aId ] = nIndex;
444 pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex;
447 if( m_pCurrentLocaleItem == nullptr )
448 m_pCurrentLocaleItem = pLocaleItem;
450 if( m_pDefaultLocaleItem == nullptr )
452 m_pDefaultLocaleItem = pLocaleItem;
453 m_bDefaultModified = true;
456 implModified();
459 void StringResourceImpl::removeLocale( const Locale& locale )
461 ::osl::MutexGuard aGuard( getMutex() );
462 implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" );
464 LocaleItem* pRemoveItem = getItemForLocale( locale, true );
465 if( !pRemoveItem )
466 return;
468 // Last locale?
469 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
470 if( nLocaleCount > 1 )
472 if( m_pCurrentLocaleItem == pRemoveItem ||
473 m_pDefaultLocaleItem == pRemoveItem )
475 LocaleItem* pFallbackItem = nullptr;
476 for( const auto& pLocaleItem : m_aLocaleItemVector )
478 if( pLocaleItem.get() != pRemoveItem )
480 pFallbackItem = pLocaleItem.get();
481 break;
484 if( m_pCurrentLocaleItem == pRemoveItem )
486 setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ );
488 if( m_pDefaultLocaleItem == pRemoveItem )
490 setDefaultLocale( pFallbackItem->m_locale );
494 auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(),
495 [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; });
496 if (it == m_aLocaleItemVector.end())
497 return;
499 // Remember locale item to delete file while storing
500 m_aDeletedLocaleItemVector.push_back( std::move(*it) );
502 // Last locale?
503 if( nLocaleCount == 1 )
505 m_nNextUniqueNumericId = 0;
506 if( m_pDefaultLocaleItem )
508 m_aChangedDefaultLocaleVector.push_back(
509 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
511 m_pCurrentLocaleItem = nullptr;
512 m_pDefaultLocaleItem = nullptr;
515 m_aLocaleItemVector.erase( it );
517 implModified();
520 void StringResourceImpl::implScanIdForNumber( const OUString& ResourceID )
522 const sal_Unicode* pSrc = ResourceID.getStr();
523 sal_Int32 nLen = ResourceID.getLength();
525 sal_Int32 nNumber = 0;
526 for( sal_Int32 i = 0 ; i < nLen ; i++ )
528 sal_Unicode c = pSrc[i];
529 if( c >= '0' && c <= '9' )
531 sal_uInt16 nDigitVal = c - '0';
532 nNumber = 10*nNumber + nDigitVal;
534 else
535 break;
538 if( m_nNextUniqueNumericId < nNumber + 1 )
539 m_nNextUniqueNumericId = nNumber + 1;
542 sal_Int32 StringResourceImpl::getUniqueNumericId( )
544 if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION )
546 implLoadAllLocales();
547 m_nNextUniqueNumericId = 0;
550 if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION )
552 throw NoSupportException( "getUniqueNumericId: Extended sal_Int32 range" );
554 return m_nNextUniqueNumericId;
558 // Private helper methods
560 LocaleItem* StringResourceImpl::getItemForLocale
561 ( const Locale& locale, bool bException )
563 LocaleItem* pRetItem = nullptr;
565 // Search for locale
566 for( auto& pLocaleItem : m_aLocaleItemVector )
568 if( pLocaleItem )
570 Locale& cmp_locale = pLocaleItem->m_locale;
571 if( cmp_locale.Language == locale.Language &&
572 cmp_locale.Country == locale.Country &&
573 cmp_locale.Variant == locale.Variant )
575 pRetItem = pLocaleItem.get();
576 break;
581 if( pRetItem == nullptr && bException )
583 throw IllegalArgumentException( "StringResourceImpl: Invalid locale", Reference< XInterface >(), 0 );
585 return pRetItem;
588 // Returns the LocaleItem for a given locale, if it exists, otherwise NULL.
589 // This method performs a closest match search, at least the language must match.
590 LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale )
592 LocaleItem* pRetItem = nullptr;
594 ::std::vector< Locale > aLocales( m_aLocaleItemVector.size());
595 size_t i = 0;
596 for( const auto& pLocaleItem : m_aLocaleItemVector )
598 aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale());
599 ++i;
601 ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale));
602 if (iFound != aLocales.end())
603 pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get();
605 return pRetItem;
608 void StringResourceImpl::implModified()
610 m_bModified = true;
611 implNotifyListeners();
614 void StringResourceImpl::implNotifyListeners()
616 EventObject aEvent;
617 aEvent.Source = static_cast< XInterface* >( static_cast<OWeakObject*>(this) );
619 ::comphelper::OInterfaceIteratorHelper2 it( m_aListenerContainer );
620 while( it.hasMoreElements() )
622 Reference< XInterface > xIface = it.next();
623 Reference< XModifyListener > xListener( xIface, UNO_QUERY );
626 xListener->modified( aEvent );
628 catch(RuntimeException&)
630 it.remove();
636 // Loading
638 bool StringResourceImpl::loadLocale( LocaleItem* )
640 // Base implementation has nothing to load
641 return true;
644 void StringResourceImpl::implLoadAllLocales()
646 // Base implementation has nothing to load
650 // StringResourcePersistenceImpl
653 StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext )
654 : StringResourcePersistenceImpl_BASE( rxContext )
659 StringResourcePersistenceImpl::~StringResourcePersistenceImpl()
664 // XServiceInfo
667 OUString StringResourcePersistenceImpl::getImplementationName( )
669 return "com.sun.star.comp.scripting.StringResource";
673 sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName )
675 return cppu::supportsService( this, rServiceName );
679 Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames( )
681 return StringResourceImpl::getSupportedServiceNames();
685 // XInitialization base functionality for derived classes
688 const char aNameBaseDefaultStr[] = "strings";
690 void StringResourcePersistenceImpl::implInitializeCommonParameters
691 ( const Sequence< Any >& aArguments )
693 bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly);
694 if( !bReadOnlyOk )
696 throw IllegalArgumentException( "XInitialization::initialize: Expected ReadOnly flag", Reference< XInterface >(), 1 );
699 css::lang::Locale aCurrentLocale;
700 bool bLocaleOk = (aArguments[2] >>= aCurrentLocale);
701 if( !bLocaleOk )
703 throw IllegalArgumentException( "XInitialization::initialize: Expected Locale", Reference< XInterface >(), 2 );
706 bool bNameBaseOk = (aArguments[3] >>= m_aNameBase);
707 if( !bNameBaseOk )
709 throw IllegalArgumentException( "XInitialization::initialize: Expected NameBase string", Reference< XInterface >(), 3 );
711 if( m_aNameBase.isEmpty() )
712 m_aNameBase = aNameBaseDefaultStr;
714 bool bCommentOk = (aArguments[4] >>= m_aComment);
715 if( !bCommentOk )
717 throw IllegalArgumentException( "XInitialization::initialize: Expected Comment string", Reference< XInterface >(), 4 );
720 implScanLocales();
722 implSetCurrentLocale( aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ );
726 // Forwarding calls to base class
728 // XModifyBroadcaster
729 void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
731 StringResourceImpl::addModifyListener( aListener );
733 void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
735 StringResourceImpl::removeModifyListener( aListener );
738 // XStringResourceResolver
739 OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID )
741 return StringResourceImpl::resolveString( ResourceID ) ;
743 OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
745 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
747 sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID )
749 return StringResourceImpl::hasEntryForId( ResourceID ) ;
751 sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
752 const Locale& locale )
754 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
756 Locale StringResourcePersistenceImpl::getCurrentLocale()
758 return StringResourceImpl::getCurrentLocale();
760 Locale StringResourcePersistenceImpl::getDefaultLocale( )
762 return StringResourceImpl::getDefaultLocale();
764 Sequence< Locale > StringResourcePersistenceImpl::getLocales( )
766 return StringResourceImpl::getLocales();
769 // XStringResourceManager
770 sal_Bool StringResourcePersistenceImpl::isReadOnly()
772 return StringResourceImpl::isReadOnly();
774 void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
776 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
778 void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale )
780 StringResourceImpl::setDefaultLocale( locale );
782 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs( )
784 return StringResourceImpl::getResourceIDs();
786 void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str )
788 StringResourceImpl::setString( ResourceID, Str );
790 void StringResourcePersistenceImpl::setStringForLocale
791 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
793 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
795 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale
796 ( const Locale& locale )
798 return StringResourceImpl::getResourceIDsForLocale( locale );
800 void StringResourcePersistenceImpl::removeId( const OUString& ResourceID )
802 StringResourceImpl::removeId( ResourceID );
804 void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
806 StringResourceImpl::removeIdForLocale( ResourceID, locale );
808 void StringResourcePersistenceImpl::newLocale( const Locale& locale )
810 StringResourceImpl::newLocale( locale );
812 void StringResourcePersistenceImpl::removeLocale( const Locale& locale )
814 StringResourceImpl::removeLocale( locale );
816 sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId( )
818 return StringResourceImpl::getUniqueNumericId();
822 // XStringResourcePersistence
824 void StringResourcePersistenceImpl::store()
828 sal_Bool StringResourcePersistenceImpl::isModified( )
830 ::osl::MutexGuard aGuard( getMutex() );
832 return m_bModified;
835 void StringResourcePersistenceImpl::setComment( const OUString& Comment )
837 m_aComment = Comment;
840 void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage,
841 const OUString& NameBase, const OUString& Comment )
843 ::osl::MutexGuard aGuard( getMutex() );
845 implStoreAtStorage( NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ );
848 void StringResourcePersistenceImpl::implStoreAtStorage
850 const OUString& aNameBase,
851 const OUString& aComment,
852 const Reference< css::embed::XStorage >& Storage,
853 bool bUsedForStore,
854 bool bStoreAll
857 // Delete files for deleted locales
858 if( bUsedForStore )
860 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
862 if( pLocaleItem )
864 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties";
868 Storage->removeElement( aStreamName );
870 catch( Exception& )
873 pLocaleItem.reset();
876 m_aDeletedLocaleItemVector.clear();
879 for( auto& pLocaleItem : m_aLocaleItemVector )
881 if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) &&
882 loadLocale( pLocaleItem.get() ) )
884 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties";
886 Reference< io::XStream > xElementStream =
887 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
889 uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY );
890 OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" );
891 if ( xProps.is() )
893 OUString aPropName("MediaType");
894 xProps->setPropertyValue( aPropName, uno::makeAny( OUString("text/plain") ) );
896 aPropName = "UseCommonStoragePasswordEncryption";
897 xProps->setPropertyValue( aPropName, uno::makeAny( true ) );
900 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
901 if( xOutputStream.is() )
902 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
903 xOutputStream->closeOutput();
905 if( bUsedForStore )
906 pLocaleItem->m_bModified = false;
910 // Delete files for changed defaults
911 if( bUsedForStore )
913 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
915 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default";
919 Storage->removeElement( aStreamName );
921 catch( Exception& )
924 pLocaleItem.reset();
926 m_aChangedDefaultLocaleVector.clear();
929 // Default locale
930 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) )
931 return;
933 OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default";
935 Reference< io::XStream > xElementStream =
936 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
938 // Only create stream without content
939 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
940 xOutputStream->closeOutput();
942 if( bUsedForStore )
943 m_bDefaultModified = false;
946 void StringResourcePersistenceImpl::storeToURL( const OUString& URL,
947 const OUString& NameBase, const OUString& Comment,
948 const Reference< css::task::XInteractionHandler >& Handler )
950 ::osl::MutexGuard aGuard( getMutex() );
952 Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext);
953 if( xFileAccess.is() && Handler.is() )
954 xFileAccess->setInteractionHandler( Handler );
956 implStoreAtLocation( URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/ );
959 void StringResourcePersistenceImpl::implKillRemovedLocaleFiles
961 const OUString& Location,
962 const OUString& aNameBase,
963 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
966 // Delete files for deleted locales
967 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
969 if( pLocaleItem )
971 OUString aCompleteFileName =
972 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
973 if( xFileAccess->exists( aCompleteFileName ) )
974 xFileAccess->kill( aCompleteFileName );
976 pLocaleItem.reset();
979 m_aDeletedLocaleItemVector.clear();
982 void StringResourcePersistenceImpl::implKillChangedDefaultFiles
984 const OUString& Location,
985 const OUString& aNameBase,
986 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
989 // Delete files for changed defaults
990 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
992 OUString aCompleteFileName =
993 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true );
994 if( xFileAccess->exists( aCompleteFileName ) )
995 xFileAccess->kill( aCompleteFileName );
996 pLocaleItem.reset();
998 m_aChangedDefaultLocaleVector.clear();
1001 void StringResourcePersistenceImpl::implStoreAtLocation
1003 const OUString& Location,
1004 const OUString& aNameBase,
1005 const OUString& aComment,
1006 const Reference< ucb::XSimpleFileAccess3 >& xFileAccess,
1007 bool bUsedForStore,
1008 bool bStoreAll,
1009 bool bKillAll
1012 // Delete files for deleted locales
1013 if( bUsedForStore || bKillAll )
1014 implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess );
1016 for( auto& pLocaleItem : m_aLocaleItemVector )
1018 if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) &&
1019 loadLocale( pLocaleItem.get() ) )
1021 OUString aCompleteFileName =
1022 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
1023 if( xFileAccess->exists( aCompleteFileName ) )
1024 xFileAccess->kill( aCompleteFileName );
1026 if( !bKillAll )
1028 // Create Output stream
1029 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1030 if( xOutputStream.is() )
1032 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
1033 xOutputStream->closeOutput();
1035 if( bUsedForStore )
1036 pLocaleItem->m_bModified = false;
1041 // Delete files for changed defaults
1042 if( bUsedForStore || bKillAll )
1043 implKillChangedDefaultFiles( Location, aNameBase, xFileAccess );
1045 // Default locale
1046 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) )
1047 return;
1049 OUString aCompleteFileName =
1050 implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true );
1051 if( xFileAccess->exists( aCompleteFileName ) )
1052 xFileAccess->kill( aCompleteFileName );
1054 if( !bKillAll )
1056 // Create Output stream
1057 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1058 if( xOutputStream.is() )
1059 xOutputStream->closeOutput();
1061 if( bUsedForStore )
1062 m_bDefaultModified = false;
1067 // BinaryOutput, helper class for exportBinary
1069 class BinaryOutput
1071 Reference< XComponentContext > m_xContext;
1072 Reference< XInterface > m_xTempFile;
1073 Reference< io::XOutputStream > m_xOutputStream;
1075 public:
1076 explicit BinaryOutput( Reference< XComponentContext > const & xContext );
1078 const Reference< io::XOutputStream >& getOutputStream() const
1079 { return m_xOutputStream; }
1081 Sequence< ::sal_Int8 > closeAndGetData();
1083 // Template to be used with sal_Int16 and sal_Unicode
1084 template< class T >
1085 void write16BitInt( T n );
1086 void writeInt16( sal_Int16 n )
1087 { write16BitInt( n ); }
1088 void writeUnicodeChar( sal_Unicode n )
1089 { write16BitInt( n ); }
1090 void writeInt32( sal_Int32 n );
1091 void writeString( const OUString& aStr );
1094 BinaryOutput::BinaryOutput( Reference< XComponentContext > const & xContext )
1095 : m_xContext( xContext )
1097 m_xTempFile = io::TempFile::create( m_xContext );
1098 m_xOutputStream.set( m_xTempFile, UNO_QUERY_THROW );
1101 template< class T >
1102 void BinaryOutput::write16BitInt( T n )
1104 if( !m_xOutputStream.is() )
1105 return;
1107 Sequence< sal_Int8 > aSeq( 2 );
1108 sal_Int8* p = aSeq.getArray();
1110 sal_Int8 nLow = sal_Int8( n & 0xff );
1111 sal_Int8 nHigh = sal_Int8( n >> 8 );
1113 p[0] = nLow;
1114 p[1] = nHigh;
1115 m_xOutputStream->writeBytes( aSeq );
1118 void BinaryOutput::writeInt32( sal_Int32 n )
1120 if( !m_xOutputStream.is() )
1121 return;
1123 Sequence< sal_Int8 > aSeq( 4 );
1124 sal_Int8* p = aSeq.getArray();
1126 for( sal_Int16 i = 0 ; i < 4 ; i++ )
1128 p[i] = sal_Int8( n & 0xff );
1129 n >>= 8;
1131 m_xOutputStream->writeBytes( aSeq );
1134 void BinaryOutput::writeString( const OUString& aStr )
1136 sal_Int32 nLen = aStr.getLength();
1137 const sal_Unicode* pStr = aStr.getStr();
1139 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1140 writeUnicodeChar( pStr[i] );
1142 writeUnicodeChar( 0 );
1145 Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData()
1147 Sequence< ::sal_Int8 > aRetSeq;
1148 if( !m_xOutputStream.is() )
1149 return aRetSeq;
1151 m_xOutputStream->closeOutput();
1153 Reference< io::XSeekable> xSeekable( m_xTempFile, UNO_QUERY );
1154 if( !xSeekable.is() )
1155 return aRetSeq;
1157 sal_Int32 nSize = static_cast<sal_Int32>(xSeekable->getPosition());
1159 Reference< io::XInputStream> xInputStream( m_xTempFile, UNO_QUERY );
1160 if( !xInputStream.is() )
1161 return aRetSeq;
1163 xSeekable->seek( 0 );
1164 sal_Int32 nRead = xInputStream->readBytes( aRetSeq, nSize );
1165 OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" );
1167 return aRetSeq;
1171 // Binary format:
1173 // Header
1174 // Byte Content
1175 // 0 + 1 sal_Int16: Version, currently 0, low byte first
1176 // 2 + 3 sal_Int16: Locale count = n, low byte first
1177 // 4 + 5 sal_Int16: Default Locale position in Locale list, == n if none
1178 // 6 - 7 sal_Int32: Start index locale block 0, lowest byte first
1179 // (n-1) * sal_Int32: Start index locale block 1 to n, lowest byte first
1180 // 6 + 4*n sal_Int32: "Start index" non existing locale block n+1,
1181 // marks the first invalid index, kind of EOF
1183 // Locale block
1184 // All strings are stored as 2-Byte-0 terminated sequence
1185 // of 16 bit Unicode characters, each with low byte first
1186 // Empty strings only contain the 2-Byte-0
1188 // Members of com.sun.star.lang.Locale
1189 // with l1 = Locale.Language.getLength()
1190 // with l2 = Locale.Country.getLength()
1191 // with l3 = Locale.Variant.getLength()
1192 // pos0 = 0 Locale.Language
1193 // pos1 = 2 * (l1 + 1) Locale.Country
1194 // pos2 = pos1 + 2 * (l2 + 1) Locale.Variant
1195 // pos3 = pos2 + 2 * (l3 + 1)
1196 // pos3 Properties file written by implWritePropertiesFile
1198 Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary( )
1200 BinaryOutput aOut( m_xContext );
1202 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
1203 std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount);
1205 sal_Int32 iLocale = 0;
1206 sal_Int32 iDefault = 0;
1207 for( auto& pLocaleItem : m_aLocaleItemVector )
1209 if( pLocaleItem != nullptr && loadLocale( pLocaleItem.get() ) )
1211 if( m_pDefaultLocaleItem == pLocaleItem.get() )
1212 iDefault = iLocale;
1214 BinaryOutput aLocaleOut( m_xContext );
1215 implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut );
1217 aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData();
1219 ++iLocale;
1222 // Write header
1223 sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount);
1224 sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault);
1225 aOut.writeInt16( 0 ); // nVersion
1226 aOut.writeInt16( nLocaleCount16 );
1227 aOut.writeInt16( iDefault16 );
1229 // Write data positions
1230 sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1);
1231 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1233 aOut.writeInt32( nDataPos );
1235 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1236 sal_Int32 nSeqLen = rSeq.getLength();
1237 nDataPos += nSeqLen;
1239 // Write final position
1240 aOut.writeInt32( nDataPos );
1242 // Write data
1243 Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream();
1244 if( xOutputStream.is() )
1246 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1248 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1249 xOutputStream->writeBytes( rSeq );
1253 Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData();
1254 return aRetSeq;
1257 void StringResourcePersistenceImpl::implWriteLocaleBinary
1258 ( LocaleItem* pLocaleItem, BinaryOutput& rOut )
1260 Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream();
1261 if( !xOutputStream.is() )
1262 return;
1264 Locale& rLocale = pLocaleItem->m_locale;
1265 rOut.writeString( rLocale.Language );
1266 rOut.writeString( rLocale.Country );
1267 rOut.writeString( rLocale.Variant );
1268 implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment );
1272 // BinaryOutput, helper class for exportBinary
1274 namespace {
1276 class BinaryInput
1278 Sequence< sal_Int8 > m_aData;
1279 Reference< XComponentContext > m_xContext;
1281 const sal_Int8* m_pData;
1282 sal_Int32 m_nCurPos;
1283 sal_Int32 m_nSize;
1285 public:
1286 BinaryInput( const Sequence< ::sal_Int8 >& aData, Reference< XComponentContext > const & xContext );
1288 Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize );
1290 void seek( sal_Int32 nPos );
1291 sal_Int32 getPosition() const
1292 { return m_nCurPos; }
1294 sal_Int16 readInt16();
1295 sal_Int32 readInt32();
1296 sal_Unicode readUnicodeChar();
1297 OUString readString();
1302 BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData, Reference< XComponentContext > const & xContext )
1303 : m_aData( aData )
1304 , m_xContext( xContext )
1306 m_pData = m_aData.getConstArray();
1307 m_nCurPos = 0;
1308 m_nSize = m_aData.getLength();
1311 Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize )
1313 Reference< io::XInputStream > xIn;
1314 if( m_nCurPos + nSize <= m_nSize )
1316 Reference< io::XOutputStream > xTempOut( io::TempFile::create(m_xContext), UNO_QUERY_THROW );
1317 Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize );
1318 xTempOut->writeBytes( aSection );
1320 Reference< io::XSeekable> xSeekable( xTempOut, UNO_QUERY );
1321 if( xSeekable.is() )
1322 xSeekable->seek( 0 );
1324 xIn.set( xTempOut, UNO_QUERY );
1326 else
1327 OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" );
1329 return xIn;
1332 void BinaryInput::seek( sal_Int32 nPos )
1334 if( nPos <= m_nSize )
1335 m_nCurPos = nPos;
1336 else
1337 OSL_FAIL( "BinaryInput::seek(): Position past end" );
1341 sal_Int16 BinaryInput::readInt16()
1343 sal_Int16 nRet = 0;
1344 if( m_nCurPos + 2 <= m_nSize )
1346 nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1347 nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1349 else
1350 OSL_FAIL( "BinaryInput::readInt16(): Read past end" );
1352 return nRet;
1355 sal_Int32 BinaryInput::readInt32()
1357 sal_Int32 nRet = 0;
1358 if( m_nCurPos + 4 <= m_nSize )
1360 sal_Int32 nFactor = 1;
1361 for( sal_Int16 i = 0; i < 4; i++ )
1363 nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor;
1364 nFactor *= 256;
1367 else
1368 OSL_FAIL( "BinaryInput::readInt32(): Read past end" );
1370 return nRet;
1373 sal_Unicode BinaryInput::readUnicodeChar()
1375 sal_uInt16 nRet = 0;
1376 if( m_nCurPos + 2 <= m_nSize )
1378 nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] );
1379 nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] );
1381 else
1382 OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" );
1384 sal_Unicode cRet = nRet;
1385 return cRet;
1388 OUString BinaryInput::readString()
1390 OUStringBuffer aBuf;
1391 sal_Unicode c;
1394 c = readUnicodeChar();
1395 if( c != 0 )
1396 aBuf.append( c );
1398 while( c != 0 );
1400 OUString aRetStr = aBuf.makeStringAndClear();
1401 return aRetStr;
1404 void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
1406 // Init: Remove all locales
1407 sal_Int32 nOldLocaleCount = 0;
1410 Sequence< Locale > aLocaleSeq = getLocales();
1411 nOldLocaleCount = aLocaleSeq.getLength();
1412 if( nOldLocaleCount > 0 )
1414 Locale aLocale = aLocaleSeq[0];
1415 removeLocale( aLocale );
1418 while( nOldLocaleCount > 0 );
1420 // Import data
1421 BinaryInput aIn( Data, m_xContext );
1423 aIn.readInt16(); // version
1424 sal_Int32 nLocaleCount = aIn.readInt16();
1425 sal_Int32 iDefault = aIn.readInt16();
1427 std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] );
1428 for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ )
1429 pPositions[i] = aIn.readInt32();
1431 // Import locales
1432 LocaleItem* pUseAsDefaultItem = nullptr;
1433 for( sal_Int32 i = 0; i < nLocaleCount; i++ )
1435 sal_Int32 nPos = pPositions[i];
1436 aIn.seek( nPos );
1438 Locale aLocale;
1439 aLocale.Language = aIn.readString();
1440 aLocale.Country = aIn.readString();
1441 aLocale.Variant = aIn.readString();
1443 sal_Int32 nAfterStringPos = aIn.getPosition();
1444 sal_Int32 nSize = pPositions[i+1] - nAfterStringPos;
1445 Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize );
1446 if( xInput.is() )
1448 LocaleItem* pLocaleItem = new LocaleItem( aLocale );
1449 if( iDefault == i )
1450 pUseAsDefaultItem = pLocaleItem;
1451 m_aLocaleItemVector.emplace_back( pLocaleItem );
1452 implReadPropertiesFile( pLocaleItem, xInput );
1456 if( pUseAsDefaultItem != nullptr )
1457 setDefaultLocale( pUseAsDefaultItem->m_locale );
1461 // Private helper methods
1463 static bool checkNamingSceme( const OUString& aName, const OUString& aNameBase,
1464 Locale& aLocale )
1466 bool bSuccess = false;
1468 sal_Int32 nNameLen = aName.getLength();
1469 sal_Int32 nNameBaseLen = aNameBase.getLength();
1471 // Name has to start with NameBase followed
1472 // by a '_' and at least one more character
1473 if( aName.startsWith( aNameBase ) && nNameBaseLen < nNameLen-1 &&
1474 aName[nNameBaseLen] == '_' )
1476 bSuccess = true;
1478 /* FIXME-BCP47: this uses '_' underscore character as separator and
1479 * also appends Variant, which can't be blindly changed as it would
1480 * violate the naming scheme in use. */
1482 sal_Int32 iStart = nNameBaseLen + 1;
1483 sal_Int32 iNext_ = aName.indexOf( '_', iStart );
1484 if( iNext_ != -1 && iNext_ < nNameLen-1 )
1486 aLocale.Language = aName.copy( iStart, iNext_ - iStart );
1488 iStart = iNext_ + 1;
1489 iNext_ = aName.indexOf( '_', iStart );
1490 if( iNext_ != -1 && iNext_ < nNameLen-1 )
1492 aLocale.Country = aName.copy( iStart, iNext_ - iStart );
1493 aLocale.Variant = aName.copy( iNext_ + 1 );
1495 else
1496 aLocale.Country = aName.copy( iStart );
1498 else
1499 aLocale.Language = aName.copy( iStart );
1501 return bSuccess;
1504 void StringResourcePersistenceImpl::implLoadAllLocales()
1506 for( auto& pLocaleItem : m_aLocaleItemVector )
1507 if( pLocaleItem )
1508 loadLocale( pLocaleItem.get() );
1511 // Scan locale properties files helper
1512 void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq )
1514 Locale aDefaultLocale;
1515 bool bDefaultFound = false;
1517 for( const OUString& aCompleteName : aContentSeq )
1519 OUString aPureName;
1520 OUString aExtension;
1521 sal_Int32 iDot = aCompleteName.lastIndexOf( '.' );
1522 sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' );
1523 if( iDot != -1 && iDot > iSlash)
1525 sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0;
1526 aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom );
1527 aExtension = aCompleteName.copy( iDot + 1 );
1530 if ( aExtension == "properties" )
1532 //OUString aName = aInetObj.getBase();
1533 Locale aLocale;
1535 if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) )
1537 LocaleItem* pLocaleItem = new LocaleItem( aLocale, false );
1538 m_aLocaleItemVector.emplace_back( pLocaleItem );
1540 if( m_pCurrentLocaleItem == nullptr )
1541 m_pCurrentLocaleItem = pLocaleItem;
1543 if( m_pDefaultLocaleItem == nullptr )
1545 m_pDefaultLocaleItem = pLocaleItem;
1546 m_bDefaultModified = true;
1550 else if( !bDefaultFound && aExtension == "default" )
1552 if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) )
1553 bDefaultFound = true;
1556 if( bDefaultFound )
1558 LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false );
1559 if( pLocaleItem )
1561 m_pDefaultLocaleItem = pLocaleItem;
1562 m_bDefaultModified = false;
1567 // Scan locale properties files
1568 void StringResourcePersistenceImpl::implScanLocales()
1570 // Dummy implementation, method not called for this
1571 // base class, but pure virtual not possible-
1574 bool StringResourcePersistenceImpl::loadLocale( LocaleItem* pLocaleItem )
1576 bool bSuccess = false;
1578 OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" );
1579 if( pLocaleItem )
1581 if( pLocaleItem->m_bLoaded )
1583 bSuccess = true;
1585 else
1587 bSuccess = implLoadLocale( pLocaleItem );
1588 pLocaleItem->m_bLoaded = true; // = bSuccess??? -> leads to more tries
1591 return bSuccess;
1594 bool StringResourcePersistenceImpl::implLoadLocale( LocaleItem* )
1596 // Dummy implementation, method not called for this
1597 // base class, but pure virtual not possible-
1598 return false;
1601 static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem )
1603 /* FIXME-BCP47: this uses '_' underscore character as separator and
1604 * also appends Variant, which can't be blindly changed as it would
1605 * violate the naming scheme in use. */
1607 static const char aUnder[] = "_";
1609 OSL_ENSURE( pLocaleItem,
1610 "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL" );
1611 Locale aLocale = pLocaleItem->m_locale;
1613 OUString aRetStr = aUnder + aLocale.Language;
1615 OUString aCountry = aLocale.Country;
1616 if( !aCountry.isEmpty() )
1618 aRetStr += aUnder + aCountry;
1621 OUString aVariant = aLocale.Variant;
1622 if( !aVariant.isEmpty() )
1624 aRetStr += aUnder + aVariant;
1626 return aRetStr;
1629 OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem
1630 ( LocaleItem const * pLocaleItem, const OUString& aNameBase )
1632 OUString aFileName = aNameBase;
1633 if( aFileName.isEmpty() )
1634 aFileName = aNameBaseDefaultStr;
1636 aFileName += implGetNameScemeForLocaleItem( pLocaleItem );
1637 return aFileName;
1640 OUString StringResourcePersistenceImpl::implGetPathForLocaleItem
1641 ( LocaleItem const * pLocaleItem, const OUString& aNameBase,
1642 const OUString& aLocation, bool bDefaultFile )
1644 OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase );
1645 INetURLObject aInetObj( aLocation );
1646 aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All );
1647 if( bDefaultFile )
1648 aInetObj.setExtension( "default" );
1649 else
1650 aInetObj.setExtension( "properties" );
1651 OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1652 return aCompleteFileName;
1655 // White space according to Java property files specification in
1656 // http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)
1657 static bool isWhiteSpace( sal_Unicode c )
1659 bool bWhite = ( c == 0x0020 || // space
1660 c == 0x0009 || // tab
1661 c == 0x000a || // line feed, not always handled by TextInputStream
1662 c == 0x000d || // carriage return, not always handled by TextInputStream
1663 c == 0x000C ); // form feed
1664 return bWhite;
1667 static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1669 while( ri < nLen )
1671 if( !isWhiteSpace( pBuf[ri] ) )
1672 break;
1673 ri++;
1677 static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
1679 bool bRet = true;
1680 if( c >= '0' && c <= '9' )
1681 nDigitVal = c - '0';
1682 else if( c >= 'a' && c <= 'f' )
1683 nDigitVal = c - 'a' + 10;
1684 else if( c >= 'A' && c <= 'F' )
1685 nDigitVal = c - 'A' + 10;
1686 else
1687 bRet = false;
1688 return bRet;
1691 static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1693 sal_Int32 i = ri;
1695 sal_Unicode cRet = 0;
1696 sal_Unicode c = pBuf[i];
1697 switch( c )
1699 case 't':
1700 cRet = 0x0009;
1701 break;
1702 case 'n':
1703 cRet = 0x000a;
1704 break;
1705 case 'f':
1706 cRet = 0x000c;
1707 break;
1708 case 'r':
1709 cRet = 0x000d;
1710 break;
1711 case '\\':
1712 cRet = '\\';
1713 break;
1714 case 'u':
1716 // Skip multiple u
1717 i++;
1718 while( i < nLen && pBuf[i] == 'u' )
1719 i++;
1721 // Process hex digits
1722 sal_Int32 nDigitCount = 0;
1723 sal_uInt16 nDigitVal;
1724 while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
1726 cRet = 16 * cRet + nDigitVal;
1728 nDigitCount++;
1729 if( nDigitCount == 4 )
1731 // Write back position
1732 ri = i;
1733 break;
1735 i++;
1737 break;
1739 default:
1740 cRet = c;
1743 return cRet;
1746 static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream,
1747 OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf,
1748 sal_Int32& nLen, sal_Int32& i )
1750 if( !(i == nLen && bEscapePending) )
1751 return;
1753 bEscapePending = false;
1755 if( !xTextInputStream->isEOF() )
1757 aLine = xTextInputStream->readLine();
1758 nLen = aLine.getLength();
1759 pBuf = aLine.getStr();
1760 i = 0;
1762 skipWhites( pBuf, nLen, i );
1766 bool StringResourcePersistenceImpl::implReadPropertiesFile
1767 ( LocaleItem* pLocaleItem, const Reference< io::XInputStream >& xInputStream )
1769 if( !xInputStream.is() || pLocaleItem == nullptr )
1770 return false;
1772 Reference< io::XTextInputStream2 > xTextInputStream = io::TextInputStream::create( m_xContext );
1774 xTextInputStream->setInputStream( xInputStream );
1776 OUString aEncodingStr = OUString::createFromAscii
1777 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1778 xTextInputStream->setEncoding( aEncodingStr );
1780 OUString aLine;
1781 while( !xTextInputStream->isEOF() )
1783 aLine = xTextInputStream->readLine();
1785 sal_Int32 nLen = aLine.getLength();
1786 if( 0 == nLen )
1787 continue;
1788 const sal_Unicode* pBuf = aLine.getStr();
1789 OUStringBuffer aBuf;
1790 sal_Unicode c = 0;
1791 sal_Int32 i = 0;
1793 skipWhites( pBuf, nLen, i );
1794 if( i == nLen )
1795 continue; // line contains only white spaces
1797 // Comment?
1798 c = pBuf[i];
1799 if( c == '#' || c == '!' )
1800 continue;
1802 // Scan key
1803 OUString aResourceID;
1804 bool bEscapePending = false;
1805 bool bStrComplete = false;
1806 while( i < nLen && !bStrComplete )
1808 c = pBuf[i];
1809 if( bEscapePending )
1811 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1812 bEscapePending = false;
1814 else
1816 if( c == '\\' )
1818 bEscapePending = true;
1820 else
1822 if( c == ':' || c == '=' || isWhiteSpace( c ) )
1823 bStrComplete = true;
1824 else
1825 aBuf.append( c );
1828 i++;
1830 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1831 if( i == nLen )
1832 bStrComplete = true;
1834 if( bStrComplete )
1835 aResourceID = aBuf.makeStringAndClear();
1838 // Ignore lines with empty keys
1839 if( aResourceID.isEmpty() )
1840 continue;
1842 // Scan value
1843 skipWhites( pBuf, nLen, i );
1845 OUString aValueStr;
1846 bEscapePending = false;
1847 bStrComplete = false;
1848 while( i < nLen && !bStrComplete )
1850 c = pBuf[i];
1851 if( c == 0x000a || c == 0x000d ) // line feed/carriage return, not always handled by TextInputStream
1853 i++;
1855 else
1857 if( bEscapePending )
1859 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1860 bEscapePending = false;
1862 else if( c == '\\' )
1863 bEscapePending = true;
1864 else
1865 aBuf.append( c );
1866 i++;
1868 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1870 if( i == nLen )
1871 bStrComplete = true;
1873 if( bStrComplete )
1874 aValueStr = aBuf.makeStringAndClear();
1877 // Push into table
1878 pLocaleItem->m_aIdToStringMap[ aResourceID ] = aValueStr;
1879 implScanIdForNumber( aResourceID );
1880 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1881 rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++;
1884 return true;
1888 static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal )
1890 sal_Unicode cRet = ( nDigitVal < 10 ) ? ('0' + nDigitVal) : ('a' + (nDigitVal-10));
1891 return cRet;
1894 static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey )
1896 if( cu == '\\' )
1898 aBuf.append( '\\' );
1899 aBuf.append( '\\' );
1901 else if( cu == 0x000a )
1903 aBuf.append( '\\' );
1904 aBuf.append( 'n' );
1906 else if( cu == 0x000d )
1908 aBuf.append( '\\' );
1909 aBuf.append( 'r' );
1911 else if( bKey && cu == '=' )
1913 aBuf.append( '\\' );
1914 aBuf.append( '=' );
1916 else if( bKey && cu == ':' )
1918 aBuf.append( '\\' );
1919 aBuf.append( ':' );
1921 // ISO/IEC 8859-1 range according to:
1922 // http://en.wikipedia.org/wiki/ISO/IEC_8859-1
1923 else if( cu >= 0x20 && cu <= 0x7e )
1924 //TODO: Check why (cu >= 0xa0 && cu <= 0xFF)
1925 //is encoded in sample properties files
1926 //else if( (cu >= 0x20 && cu <= 0x7e) ||
1927 // (cu >= 0xa0 && cu <= 0xFF) )
1929 aBuf.append( cu );
1931 else
1933 // Unicode encoding
1934 aBuf.append( '\\' );
1935 aBuf.append( 'u' );
1937 sal_uInt16 nVal = cu;
1938 for( sal_uInt16 i = 0 ; i < 4 ; i++ )
1940 sal_uInt16 nDigit = nVal / 0x1000;
1941 nVal -= nDigit * 0x1000;
1942 nVal *= 0x10;
1943 aBuf.append( getHexCharForDigit( nDigit ) );
1948 static void implWriteStringWithEncoding( const OUString& aStr,
1949 Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey )
1951 static const sal_Unicode cLineFeed = 0xa;
1953 OUStringBuffer aBuf;
1954 sal_Int32 nLen = aStr.getLength();
1955 const sal_Unicode* pSrc = aStr.getStr();
1956 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1958 sal_Unicode cu = pSrc[i];
1959 implWriteCharToBuffer( aBuf, cu, bKey );
1960 // TODO?: split long lines
1962 if( !bKey )
1963 aBuf.append( cLineFeed );
1965 OUString aWriteStr = aBuf.makeStringAndClear();
1966 xTextOutputStream->writeString( aWriteStr );
1969 bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem,
1970 const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment )
1972 if( !xOutputStream.is() || pLocaleItem == nullptr )
1973 return false;
1975 bool bSuccess = false;
1976 Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext);
1978 xTextOutputStream->setOutputStream( xOutputStream );
1980 OUString aEncodingStr = OUString::createFromAscii
1981 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1982 xTextOutputStream->setEncoding( aEncodingStr );
1984 xTextOutputStream->writeString( aComment );
1985 xTextOutputStream->writeString( "\n" );
1987 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
1988 if( !rHashMap.empty() )
1990 // Sort ids according to read order
1991 const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1993 // Find max/min index
1994 auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(),
1995 [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; });
1996 sal_Int32 nMinIndex = itMinMax.first->second;
1997 sal_Int32 nMaxIndex = itMinMax.second->second;
1998 sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1;
2000 // Create sorted array of pointers to the id strings
2001 std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] );
2002 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
2003 pIdPtrs[i] = nullptr;
2004 for( const auto& rIndex : rIndexMap )
2006 sal_Int32 nIndex = rIndex.second;
2007 pIdPtrs[nIndex - nMinIndex] = &(rIndex.first);
2010 // Write lines in correct order
2011 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
2013 const OUString* pStr = pIdPtrs[i];
2014 if( pStr != nullptr )
2016 OUString aResourceID = *pStr;
2017 IdToStringMap::const_iterator it = rHashMap.find( aResourceID );
2018 if( it != rHashMap.end() )
2020 implWriteStringWithEncoding( aResourceID, xTextOutputStream, true );
2021 xTextOutputStream->writeString( "=" );
2022 OUString aValStr = (*it).second;
2023 implWriteStringWithEncoding( aValStr, xTextOutputStream, false );
2029 bSuccess = true;
2031 return bSuccess;
2035 // StringResourceWithStorageImpl
2037 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2038 scripting_StringResourceWithStorageImpl_get_implementation(
2039 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2041 return cppu::acquire(new StringResourceWithStorageImpl(context));
2045 StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext )
2046 : StringResourceWithStorageImpl_BASE( rxContext )
2047 , m_bStorageChanged( false )
2052 StringResourceWithStorageImpl::~StringResourceWithStorageImpl()
2057 // XServiceInfo
2060 OUString StringResourceWithStorageImpl::getImplementationName( )
2062 return "com.sun.star.comp.scripting.StringResourceWithStorage";
2065 sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName )
2067 return cppu::supportsService(this, rServiceName);
2070 Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames( )
2072 return { "com.sun.star.resource.StringResourceWithStorage" };
2076 // XInitialization
2079 void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments )
2081 ::osl::MutexGuard aGuard( getMutex() );
2083 if ( aArguments.getLength() != 5 )
2085 throw RuntimeException(
2086 "StringResourceWithStorageImpl::initialize: invalid number of arguments!" );
2089 bool bOk = (aArguments[0] >>= m_xStorage);
2090 if( bOk && !m_xStorage.is() )
2091 bOk = false;
2093 if( !bOk )
2095 throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid storage", Reference< XInterface >(), 0 );
2098 implInitializeCommonParameters( aArguments );
2102 // Forwarding calls to base class
2104 // XModifyBroadcaster
2105 void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2107 StringResourceImpl::addModifyListener( aListener );
2109 void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2111 StringResourceImpl::removeModifyListener( aListener );
2114 // XStringResourceResolver
2115 OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID )
2117 return StringResourceImpl::resolveString( ResourceID ) ;
2119 OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2121 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2123 sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID )
2125 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2127 sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2128 const Locale& locale )
2130 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2132 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs( )
2134 return StringResourceImpl::getResourceIDs();
2136 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale
2137 ( const Locale& locale )
2139 return StringResourceImpl::getResourceIDsForLocale( locale );
2141 Locale StringResourceWithStorageImpl::getCurrentLocale()
2143 return StringResourceImpl::getCurrentLocale();
2145 Locale StringResourceWithStorageImpl::getDefaultLocale( )
2147 return StringResourceImpl::getDefaultLocale();
2149 Sequence< Locale > StringResourceWithStorageImpl::getLocales( )
2151 return StringResourceImpl::getLocales();
2154 // XStringResourceManager
2155 sal_Bool StringResourceWithStorageImpl::isReadOnly()
2157 return StringResourceImpl::isReadOnly();
2159 void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2161 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2163 void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale )
2165 StringResourceImpl::setDefaultLocale( locale );
2167 void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str )
2169 StringResourceImpl::setString( ResourceID, Str );
2171 void StringResourceWithStorageImpl::setStringForLocale
2172 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2174 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2176 void StringResourceWithStorageImpl::removeId( const OUString& ResourceID )
2178 StringResourceImpl::removeId( ResourceID );
2180 void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2182 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2184 void StringResourceWithStorageImpl::newLocale( const Locale& locale )
2186 StringResourceImpl::newLocale( locale );
2188 void StringResourceWithStorageImpl::removeLocale( const Locale& locale )
2190 StringResourceImpl::removeLocale( locale );
2192 sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId( )
2194 return StringResourceImpl::getUniqueNumericId();
2197 // XStringResourcePersistence
2198 void StringResourceWithStorageImpl::store()
2200 ::osl::MutexGuard aGuard( getMutex() );
2201 implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" );
2203 bool bStoreAll = m_bStorageChanged;
2204 m_bStorageChanged = false;
2205 if( !m_bModified && !bStoreAll )
2206 return;
2208 implStoreAtStorage( m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll );
2209 m_bModified = false;
2212 sal_Bool StringResourceWithStorageImpl::isModified( )
2214 return StringResourcePersistenceImpl::isModified();
2216 void StringResourceWithStorageImpl::setComment( const OUString& Comment )
2218 StringResourcePersistenceImpl::setComment( Comment );
2220 void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage,
2221 const OUString& NameBase, const OUString& Comment )
2223 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2225 void StringResourceWithStorageImpl::storeToURL( const OUString& URL,
2226 const OUString& NameBase, const OUString& Comment,
2227 const Reference< css::task::XInteractionHandler >& Handler )
2229 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2231 Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary( )
2233 return StringResourcePersistenceImpl::exportBinary();
2235 void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2237 StringResourcePersistenceImpl::importBinary( Data );
2241 // XStringResourceWithStorage
2243 void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage )
2245 setStorage( Storage );
2246 store();
2249 void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage )
2251 ::osl::MutexGuard aGuard( getMutex() );
2253 if( !Storage.is() )
2255 throw IllegalArgumentException( "StringResourceWithStorageImpl::setStorage: invalid storage", Reference< XInterface >(), 0 );
2258 implLoadAllLocales();
2260 m_xStorage = Storage;
2261 m_bStorageChanged = true;
2265 // Private helper methods
2268 // Scan locale properties files
2269 void StringResourceWithStorageImpl::implScanLocales()
2271 if( m_xStorage.is() )
2273 Sequence< OUString > aContentSeq = m_xStorage->getElementNames();
2274 implScanLocaleNames( aContentSeq );
2277 implLoadAllLocales();
2280 // Loading
2281 bool StringResourceWithStorageImpl::implLoadLocale( LocaleItem* pLocaleItem )
2283 bool bSuccess = false;
2286 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties";
2288 Reference< io::XStream > xElementStream =
2289 m_xStorage->openStreamElement( aStreamName, ElementModes::READ );
2291 if( xElementStream.is() )
2293 Reference< io::XInputStream > xInputStream = xElementStream->getInputStream();
2294 if( xInputStream.is() )
2296 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2297 xInputStream->closeInput();
2301 catch( uno::Exception& )
2304 return bSuccess;
2308 // StringResourceWithLocationImpl
2311 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2312 scripting_StringResourceWithLocationImpl_get_implementation(
2313 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2315 return cppu::acquire(new StringResourceWithLocationImpl(context));
2320 StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext )
2321 : StringResourceWithLocationImpl_BASE( rxContext )
2322 , m_bLocationChanged( false )
2327 StringResourceWithLocationImpl::~StringResourceWithLocationImpl()
2332 // XServiceInfo
2335 OUString StringResourceWithLocationImpl::getImplementationName( )
2337 return "com.sun.star.comp.scripting.StringResourceWithLocation";
2340 sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName )
2342 return cppu::supportsService(this, rServiceName);
2345 Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames( )
2347 return { "com.sun.star.resource.StringResourceWithLocation" };
2351 // XInitialization
2354 void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments )
2356 ::osl::MutexGuard aGuard( getMutex() );
2358 if ( aArguments.getLength() != 6 )
2360 throw RuntimeException(
2361 "XInitialization::initialize: invalid number of arguments!" );
2364 bool bOk = (aArguments[0] >>= m_aLocation);
2365 sal_Int32 nLen = m_aLocation.getLength();
2366 if( bOk && nLen == 0 )
2368 bOk = false;
2370 else
2372 if( m_aLocation[nLen - 1] != '/' )
2373 m_aLocation += "/";
2376 if( !bOk )
2378 throw IllegalArgumentException( "XInitialization::initialize: invalid URL", Reference< XInterface >(), 0 );
2382 bOk = (aArguments[5] >>= m_xInteractionHandler);
2383 if( !bOk )
2385 throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid type", Reference< XInterface >(), 5 );
2388 implInitializeCommonParameters( aArguments );
2392 // Forwarding calls to base class
2394 // XModifyBroadcaster
2395 void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2397 StringResourceImpl::addModifyListener( aListener );
2399 void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2401 StringResourceImpl::removeModifyListener( aListener );
2404 // XStringResourceResolver
2405 OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID )
2407 return StringResourceImpl::resolveString( ResourceID ) ;
2409 OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2411 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2413 sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID )
2415 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2417 sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2418 const Locale& locale )
2420 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2422 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs( )
2424 return StringResourceImpl::getResourceIDs();
2426 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale
2427 ( const Locale& locale )
2429 return StringResourceImpl::getResourceIDsForLocale( locale );
2431 Locale StringResourceWithLocationImpl::getCurrentLocale()
2433 return StringResourceImpl::getCurrentLocale();
2435 Locale StringResourceWithLocationImpl::getDefaultLocale( )
2437 return StringResourceImpl::getDefaultLocale();
2439 Sequence< Locale > StringResourceWithLocationImpl::getLocales( )
2441 return StringResourceImpl::getLocales();
2444 // XStringResourceManager
2445 sal_Bool StringResourceWithLocationImpl::isReadOnly()
2447 return StringResourceImpl::isReadOnly();
2449 void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2451 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2453 void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale )
2455 StringResourceImpl::setDefaultLocale( locale );
2457 void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str )
2459 StringResourceImpl::setString( ResourceID, Str );
2461 void StringResourceWithLocationImpl::setStringForLocale
2462 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2464 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2466 void StringResourceWithLocationImpl::removeId( const OUString& ResourceID )
2468 StringResourceImpl::removeId( ResourceID );
2470 void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2472 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2474 void StringResourceWithLocationImpl::newLocale( const Locale& locale )
2476 StringResourceImpl::newLocale( locale );
2478 void StringResourceWithLocationImpl::removeLocale( const Locale& locale )
2480 StringResourceImpl::removeLocale( locale );
2482 sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId( )
2484 return StringResourceImpl::getUniqueNumericId();
2487 // XStringResourcePersistence
2488 void StringResourceWithLocationImpl::store()
2490 ::osl::MutexGuard aGuard( getMutex() );
2491 implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" );
2493 bool bStoreAll = m_bLocationChanged;
2494 m_bLocationChanged = false;
2495 if( !m_bModified && !bStoreAll )
2496 return;
2498 Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccess();
2499 implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2500 xFileAccess, true/*bUsedForStore*/, bStoreAll );
2501 m_bModified = false;
2504 sal_Bool StringResourceWithLocationImpl::isModified( )
2506 return StringResourcePersistenceImpl::isModified();
2508 void StringResourceWithLocationImpl::setComment( const OUString& Comment )
2510 StringResourcePersistenceImpl::setComment( Comment );
2512 void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage,
2513 const OUString& NameBase, const OUString& Comment )
2515 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2517 void StringResourceWithLocationImpl::storeToURL( const OUString& URL,
2518 const OUString& NameBase, const OUString& Comment,
2519 const Reference< css::task::XInteractionHandler >& Handler )
2521 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2523 Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary( )
2525 return StringResourcePersistenceImpl::exportBinary();
2527 void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2529 StringResourcePersistenceImpl::importBinary( Data );
2533 // XStringResourceWithLocation
2535 // XStringResourceWithLocation
2536 void StringResourceWithLocationImpl::storeAsURL( const OUString& URL )
2538 setURL( URL );
2539 store();
2542 void StringResourceWithLocationImpl::setURL( const OUString& URL )
2544 ::osl::MutexGuard aGuard( getMutex() );
2545 implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" );
2547 sal_Int32 nLen = URL.getLength();
2548 if( nLen == 0 )
2550 throw IllegalArgumentException( "StringResourceWithLocationImpl::setURL: invalid URL", Reference< XInterface >(), 0 );
2553 implLoadAllLocales();
2555 // Delete files at old location
2556 implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2557 getFileAccess(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ );
2559 m_aLocation = URL;
2560 m_bLocationChanged = true;
2564 // Private helper methods
2567 // Scan locale properties files
2568 void StringResourceWithLocationImpl::implScanLocales()
2570 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccess();
2571 if( xFileAccess.is() && xFileAccess->isFolder( m_aLocation ) )
2573 Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false );
2574 implScanLocaleNames( aContentSeq );
2578 // Loading
2579 bool StringResourceWithLocationImpl::implLoadLocale( LocaleItem* pLocaleItem )
2581 bool bSuccess = false;
2583 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccess();
2584 if( xFileAccess.is() )
2586 OUString aCompleteFileName =
2587 implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation );
2589 Reference< io::XInputStream > xInputStream;
2592 xInputStream = xFileAccess->openFileRead( aCompleteFileName );
2594 catch( Exception& )
2596 if( xInputStream.is() )
2598 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2599 xInputStream->closeInput();
2603 return bSuccess;
2606 const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccess()
2608 ::osl::MutexGuard aGuard( getMutex() );
2610 if( !m_xSFI.is() )
2612 m_xSFI = ucb::SimpleFileAccess::create(m_xContext);
2614 if( m_xSFI.is() && m_xInteractionHandler.is() )
2615 m_xSFI->setInteractionHandler( m_xInteractionHandler );
2617 return m_xSFI;
2620 } // namespace stringresource
2623 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */