/src/libreoffice/comphelper/source/misc/configuration.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | */ |
9 | | |
10 | | #include <sal/config.h> |
11 | | |
12 | | #include <cassert> |
13 | | #include <map> |
14 | | #include <memory> |
15 | | #include <mutex> |
16 | | #include <string_view> |
17 | | |
18 | | #include <com/sun/star/beans/PropertyAttribute.hpp> |
19 | | #include <com/sun/star/configuration/ReadOnlyAccess.hpp> |
20 | | #include <com/sun/star/configuration/ReadWriteAccess.hpp> |
21 | | #include <com/sun/star/configuration/XReadWriteAccess.hpp> |
22 | | #include <com/sun/star/configuration/theDefaultProvider.hpp> |
23 | | #include <com/sun/star/container/XHierarchicalNameAccess.hpp> |
24 | | #include <com/sun/star/container/XHierarchicalNameReplace.hpp> |
25 | | #include <com/sun/star/container/XNameAccess.hpp> |
26 | | #include <com/sun/star/container/XNameContainer.hpp> |
27 | | #include <com/sun/star/lang/XLocalizable.hpp> |
28 | | #include <com/sun/star/uno/Any.hxx> |
29 | | #include <com/sun/star/uno/Reference.hxx> |
30 | | #include <comphelper/solarmutex.hxx> |
31 | | #include <comphelper/configuration.hxx> |
32 | | #include <comphelper/configurationlistener.hxx> |
33 | | #include <rtl/ustring.hxx> |
34 | | #include <sal/log.hxx> |
35 | | #include <i18nlangtag/languagetag.hxx> |
36 | | |
37 | | namespace com::sun::star::uno { class XComponentContext; } |
38 | | |
39 | | namespace { |
40 | | |
41 | | OUString getDefaultLocale( |
42 | | css::uno::Reference< css::uno::XComponentContext > const & context) |
43 | 58.3k | { |
44 | 58.3k | return LanguageTag( |
45 | 58.3k | css::uno::Reference< css::lang::XLocalizable >( |
46 | 58.3k | css::configuration::theDefaultProvider::get(context), |
47 | 58.3k | css::uno::UNO_QUERY_THROW)-> |
48 | 58.3k | getLocale()).getBcp47(false); |
49 | 58.3k | } |
50 | | |
51 | 0 | OUString extendLocalizedPath(std::u16string_view path, OUString const & locale) { |
52 | 0 | SAL_WARN_IF( |
53 | 0 | locale.match("*"), "comphelper", |
54 | 0 | "Locale \"" << locale << "\" starts with \"*\""); |
55 | 0 | assert(locale.indexOf('&') == -1); |
56 | 0 | assert(locale.indexOf('"') == -1); |
57 | 0 | assert(locale.indexOf('\'') == -1); |
58 | 0 | return OUString::Concat(path) + "/['*" + locale + "']"; |
59 | 0 | } |
60 | | |
61 | | } |
62 | | |
63 | | std::shared_ptr< comphelper::ConfigurationChanges > |
64 | | comphelper::ConfigurationChanges::create( |
65 | | css::uno::Reference<css::uno::XComponentContext> const & context) |
66 | 0 | { |
67 | 0 | return detail::ConfigurationWrapper::get(context).createChanges(); |
68 | 0 | } |
69 | | |
70 | 0 | comphelper::ConfigurationChanges::~ConfigurationChanges() {} |
71 | | |
72 | 0 | void comphelper::ConfigurationChanges::commit() const { |
73 | 0 | access_->commitChanges(); |
74 | 0 | } |
75 | | |
76 | | comphelper::ConfigurationChanges::ConfigurationChanges( |
77 | | css::uno::Reference< css::uno::XComponentContext > const & context): |
78 | | access_( |
79 | 0 | css::configuration::ReadWriteAccess::create( |
80 | 0 | context, getDefaultLocale(context))) |
81 | 0 | {} |
82 | | |
83 | | void comphelper::ConfigurationChanges::setPropertyValue( |
84 | | OUString const & path, css::uno::Any const & value) const |
85 | 0 | { |
86 | 0 | access_->replaceByHierarchicalName(path, value); |
87 | 0 | } |
88 | | |
89 | | css::uno::Reference< css::container::XHierarchicalNameReplace > |
90 | | comphelper::ConfigurationChanges::getGroup(OUString const & path) const |
91 | 0 | { |
92 | 0 | return css::uno::Reference< css::container::XHierarchicalNameReplace >( |
93 | 0 | access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW); |
94 | 0 | } |
95 | | |
96 | | css::uno::Reference< css::container::XNameContainer > |
97 | | comphelper::ConfigurationChanges::getSet(OUString const & path) const |
98 | 0 | { |
99 | 0 | return css::uno::Reference< css::container::XNameContainer >( |
100 | 0 | access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW); |
101 | 0 | } |
102 | | |
103 | | comphelper::detail::ConfigurationWrapper const & |
104 | | comphelper::detail::ConfigurationWrapper::get( |
105 | | css::uno::Reference<css::uno::XComponentContext> const & context) |
106 | 58.3k | { |
107 | 58.3k | static comphelper::detail::ConfigurationWrapper WRAPPER(context); |
108 | 58.3k | return WRAPPER; |
109 | 58.3k | } |
110 | | |
111 | | comphelper::detail::ConfigurationWrapper::ConfigurationWrapper( |
112 | | css::uno::Reference<css::uno::XComponentContext> const & context): |
113 | 7 | context_(context.is() ? context : comphelper::getProcessComponentContext()), |
114 | 7 | access_(css::configuration::ReadWriteAccess::create(context_, u"*"_ustr)) |
115 | 7 | {} |
116 | | |
117 | 7 | comphelper::detail::ConfigurationWrapper::~ConfigurationWrapper() {} |
118 | | |
119 | | bool comphelper::detail::ConfigurationWrapper::isReadOnly(OUString const & path) |
120 | | const |
121 | 0 | { |
122 | 0 | return |
123 | 0 | (access_->getPropertyByHierarchicalName(path).Attributes |
124 | 0 | & css::beans::PropertyAttribute::READONLY) |
125 | 0 | != 0; |
126 | 0 | } |
127 | | |
128 | | css::uno::Any comphelper::detail::ConfigurationWrapper::getPropertyValue(std::u16string_view path) const |
129 | 0 | { |
130 | | // should be short-circuited in ConfigurationProperty::get() |
131 | 0 | assert(!comphelper::IsFuzzing()); |
132 | | |
133 | | // Cache the configuration access, since some of the keys are used in hot code. |
134 | | // Note that this cache is only used by the officecfg:: auto-generated code, using it for anything |
135 | | // else would be unwise because the cache could end up containing stale entries. |
136 | 0 | static std::mutex gMutex; |
137 | 0 | static std::map<OUString, css::uno::Reference< css::container::XNameAccess >> gAccessMap; |
138 | |
|
139 | 0 | sal_Int32 idx = path.rfind('/'); |
140 | 0 | assert(idx!=-1); |
141 | 0 | OUString parentPath(path.substr(0, idx)); |
142 | 0 | OUString childName(path.substr(idx+1)); |
143 | |
|
144 | 0 | std::scoped_lock aGuard(gMutex); |
145 | | |
146 | | // check cache |
147 | 0 | auto it = gAccessMap.find(parentPath); |
148 | 0 | if (it == gAccessMap.end()) |
149 | 0 | { |
150 | | // not in the cache, look it up |
151 | 0 | css::uno::Reference<css::container::XNameAccess> access( |
152 | 0 | access_->getByHierarchicalName(parentPath), css::uno::UNO_QUERY_THROW); |
153 | 0 | it = gAccessMap.emplace(parentPath, access).first; |
154 | 0 | } |
155 | 0 | return it->second->getByName(childName); |
156 | 0 | } |
157 | | |
158 | | void comphelper::detail::ConfigurationWrapper::setPropertyValue( |
159 | | std::shared_ptr< ConfigurationChanges > const & batch, |
160 | | OUString const & path, css::uno::Any const & value) |
161 | 0 | { |
162 | 0 | assert(batch); |
163 | 0 | batch->setPropertyValue(path, value); |
164 | 0 | } |
165 | | |
166 | | css::uno::Any |
167 | | comphelper::detail::ConfigurationWrapper::getLocalizedPropertyValue( |
168 | | std::u16string_view path) const |
169 | 0 | { |
170 | 0 | return access_->getByHierarchicalName( |
171 | 0 | extendLocalizedPath(path, getDefaultLocale(context_))); |
172 | 0 | } |
173 | | |
174 | | void comphelper::detail::ConfigurationWrapper::setLocalizedPropertyValue( |
175 | | std::shared_ptr< ConfigurationChanges > const & batch, |
176 | | OUString const & path, css::uno::Any const & value) |
177 | 0 | { |
178 | 0 | assert(batch); |
179 | 0 | batch->setPropertyValue(path, value); |
180 | 0 | } |
181 | | |
182 | | css::uno::Reference< css::container::XHierarchicalNameAccess > |
183 | | comphelper::detail::ConfigurationWrapper::getGroupReadOnly( |
184 | | OUString const & path) const |
185 | 0 | { |
186 | 0 | return css::uno::Reference< css::container::XHierarchicalNameAccess >( |
187 | 0 | (css::configuration::ReadOnlyAccess::create( |
188 | 0 | context_, getDefaultLocale(context_))-> |
189 | 0 | getByHierarchicalName(path)), |
190 | 0 | css::uno::UNO_QUERY_THROW); |
191 | 0 | } |
192 | | |
193 | | css::uno::Reference< css::container::XHierarchicalNameReplace > |
194 | | comphelper::detail::ConfigurationWrapper::getGroupReadWrite( |
195 | | std::shared_ptr< ConfigurationChanges > const & batch, |
196 | | OUString const & path) |
197 | 0 | { |
198 | 0 | assert(batch); |
199 | 0 | return batch->getGroup(path); |
200 | 0 | } |
201 | | |
202 | | css::uno::Reference< css::container::XNameAccess > |
203 | | comphelper::detail::ConfigurationWrapper::getSetReadOnly( |
204 | | OUString const & path) const |
205 | 58.3k | { |
206 | 58.3k | return css::uno::Reference< css::container::XNameAccess >( |
207 | 58.3k | (css::configuration::ReadOnlyAccess::create( |
208 | 58.3k | context_, getDefaultLocale(context_))-> |
209 | 58.3k | getByHierarchicalName(path)), |
210 | 58.3k | css::uno::UNO_QUERY_THROW); |
211 | 58.3k | } |
212 | | |
213 | | css::uno::Reference< css::container::XNameContainer > |
214 | | comphelper::detail::ConfigurationWrapper::getSetReadWrite( |
215 | | std::shared_ptr< ConfigurationChanges > const & batch, |
216 | | OUString const & path) |
217 | 0 | { |
218 | 0 | assert(batch); |
219 | 0 | return batch->getSet(path); |
220 | 0 | } |
221 | | |
222 | | std::shared_ptr< comphelper::ConfigurationChanges > |
223 | 0 | comphelper::detail::ConfigurationWrapper::createChanges() const { |
224 | 0 | return std::shared_ptr< ConfigurationChanges >( |
225 | 0 | new ConfigurationChanges(context_)); |
226 | 0 | } |
227 | | |
228 | | void comphelper::ConfigurationListener::addListener(ConfigurationListenerPropertyBase *pListener) |
229 | 0 | { |
230 | 0 | maListeners.push_back( pListener ); |
231 | 0 | mxConfig->addPropertyChangeListener( pListener->maName, this ); |
232 | 0 | pListener->setProperty( mxConfig->getPropertyValue( pListener->maName ) ); |
233 | 0 | } |
234 | | |
235 | | void comphelper::ConfigurationListener::removeListener(ConfigurationListenerPropertyBase *pListener) |
236 | 0 | { |
237 | 0 | auto it = std::find( maListeners.begin(), maListeners.end(), pListener ); |
238 | 0 | if ( it != maListeners.end() ) |
239 | 0 | { |
240 | 0 | maListeners.erase( it ); |
241 | 0 | mxConfig->removePropertyChangeListener( pListener->maName, this ); |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | | void comphelper::ConfigurationListener::dispose() |
246 | 0 | { |
247 | 0 | for (auto const& listener : maListeners) |
248 | 0 | { |
249 | 0 | mxConfig->removePropertyChangeListener( listener->maName, this ); |
250 | 0 | listener->dispose(); |
251 | 0 | } |
252 | 0 | maListeners.clear(); |
253 | 0 | mxConfig.clear(); |
254 | 0 | mbDisposed = true; |
255 | 0 | } |
256 | | |
257 | | void SAL_CALL comphelper::ConfigurationListener::disposing(css::lang::EventObject const &) |
258 | 0 | { |
259 | 0 | dispose(); |
260 | 0 | } |
261 | | |
262 | | void SAL_CALL comphelper::ConfigurationListener::propertyChange( |
263 | | css::beans::PropertyChangeEvent const &rEvt ) |
264 | 0 | { |
265 | | // Code is commonly used inside the SolarMutexGuard |
266 | | // so to avoid concurrent writes to the property, |
267 | | // and allow fast, lock-less access, guard here. |
268 | | // |
269 | | // Note that we are abusing rtl::Reference here to do acquire/release because, |
270 | | // unlike osl::Guard, it is tolerant of null pointers, and on some code paths, the |
271 | | // SolarMutex does not exist. |
272 | 0 | rtl::Reference<comphelper::SolarMutex> xMutexGuard( comphelper::SolarMutex::get() ); |
273 | |
|
274 | 0 | assert( rEvt.Source == mxConfig ); |
275 | 0 | for (auto const& listener : maListeners) |
276 | 0 | { |
277 | 0 | if ( listener->maName == rEvt.PropertyName ) |
278 | 0 | { |
279 | | // ignore rEvt.NewValue - in theory it could be stale => not set. |
280 | 0 | css::uno::Any aValue = mxConfig->getPropertyValue( listener->maName ); |
281 | 0 | listener->setProperty( aValue ); |
282 | 0 | } |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | namespace comphelper { |
287 | | |
288 | | static bool bIsFuzzing = false; |
289 | | |
290 | | #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
291 | | bool IsFuzzing() |
292 | | { |
293 | | return bIsFuzzing; |
294 | | } |
295 | | #endif |
296 | | |
297 | | void EnableFuzzing() |
298 | 106 | { |
299 | 106 | bIsFuzzing = true; |
300 | 106 | LanguageTag::disable_lt_tag_parse(); |
301 | 106 | } |
302 | | |
303 | | } |
304 | | |
305 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |