Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
5 : : * This Source Code Form is subject to the terms of the Mozilla Public
6 : : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
11 : : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : : * contributor license agreements. See the NOTICE file distributed
13 : : * with this work for additional information regarding copyright
14 : : * ownership. The ASF licenses this file to you under the Apache
15 : : * License, Version 2.0 (the "License"); you may not use this file
16 : : * except in compliance with the License. You may obtain a copy of
17 : : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : : */
19 : :
20 : : #include "sal/config.h"
21 : :
22 : : #include <algorithm>
23 : : #include <cassert>
24 : : #include <cstddef>
25 : : #include <list>
26 : :
27 : : #include "com/sun/star/beans/Optional.hpp"
28 : : #include "com/sun/star/beans/UnknownPropertyException.hpp"
29 : : #include "com/sun/star/beans/XPropertySet.hpp"
30 : : #include "com/sun/star/container/NoSuchElementException.hpp"
31 : : #include "com/sun/star/lang/WrappedTargetException.hpp"
32 : : #include "com/sun/star/lang/XMultiComponentFactory.hpp"
33 : : #include "com/sun/star/uno/Any.hxx"
34 : : #include "com/sun/star/uno/Exception.hpp"
35 : : #include "com/sun/star/uno/Reference.hxx"
36 : : #include "com/sun/star/uno/RuntimeException.hpp"
37 : : #include "com/sun/star/uno/XComponentContext.hpp"
38 : : #include "com/sun/star/uno/XInterface.hpp"
39 : : #include "osl/conditn.hxx"
40 : : #include "osl/file.hxx"
41 : : #include "osl/mutex.hxx"
42 : : #include "rtl/bootstrap.hxx"
43 : : #include "rtl/logfile.h"
44 : : #include "rtl/oustringostreaminserter.hxx"
45 : : #include "rtl/ref.hxx"
46 : : #include "rtl/string.h"
47 : : #include "rtl/ustrbuf.hxx"
48 : : #include "rtl/ustring.h"
49 : : #include "rtl/ustring.hxx"
50 : : #include "rtl/instance.hxx"
51 : : #include "sal/log.hxx"
52 : : #include "sal/types.h"
53 : : #include "salhelper/thread.hxx"
54 : :
55 : : #include "additions.hxx"
56 : : #include "components.hxx"
57 : : #include "data.hxx"
58 : : #include "lock.hxx"
59 : : #include "modifications.hxx"
60 : : #include "node.hxx"
61 : : #include "nodemap.hxx"
62 : : #include "parsemanager.hxx"
63 : : #include "partial.hxx"
64 : : #include "rootaccess.hxx"
65 : : #include "writemodfile.hxx"
66 : : #include "xcdparser.hxx"
67 : : #include "xcuparser.hxx"
68 : : #include "xcsparser.hxx"
69 : :
70 : : namespace configmgr {
71 : :
72 : : namespace {
73 : :
74 : : namespace css = com::sun::star;
75 : :
76 [ + - ][ + - ]: 960 : struct UnresolvedListItem {
77 : : rtl::OUString name;
78 : : rtl::Reference< ParseManager > manager;
79 : :
80 : 320 : UnresolvedListItem(
81 : : rtl::OUString const & theName,
82 : : rtl::Reference< ParseManager > theManager):
83 [ + - ]: 320 : name(theName), manager(theManager) {}
84 : : };
85 : :
86 : : typedef std::list< UnresolvedListItem > UnresolvedList;
87 : :
88 : 12036 : void parseXcsFile(
89 : : rtl::OUString const & url, int layer, Data & data, Partial const * partial,
90 : : Modifications * modifications, Additions * additions)
91 : : SAL_THROW((
92 : : css::container::NoSuchElementException, css::uno::RuntimeException))
93 : : {
94 : : assert(partial == 0 && modifications == 0 && additions == 0);
95 : : (void) partial; (void) modifications; (void) additions;
96 : : bool ok = rtl::Reference< ParseManager >(
97 [ + - ][ + - ]: 12036 : new ParseManager(url, new XcsParser(layer, data)))->parse();
[ + - ][ + - ]
[ + - ][ + - ]
98 : : assert(ok);
99 : : (void) ok; // avoid warnings
100 : 12036 : }
101 : :
102 : 16875 : void parseXcuFile(
103 : : rtl::OUString const & url, int layer, Data & data, Partial const * partial,
104 : : Modifications * modifications, Additions * additions)
105 : : SAL_THROW((
106 : : css::container::NoSuchElementException, css::uno::RuntimeException))
107 : : {
108 : : bool ok = rtl::Reference< ParseManager >(
109 : : new ParseManager(
110 : : url,
111 [ + + ][ + - ]: 33750 : new XcuParser(layer, data, partial, modifications, additions)))->
112 [ + - ][ + - ]: 50625 : parse();
[ + - ][ + - ]
113 : : assert(ok);
114 : : (void) ok; // avoid warnings
115 : 16813 : }
116 : :
117 : 1835 : rtl::OUString expand(rtl::OUString const & str) {
118 : 1835 : rtl::OUString s(str);
119 : 1835 : rtl::Bootstrap::expandMacros(s); //TODO: detect failure
120 : 1835 : return s;
121 : : }
122 : :
123 : 0 : bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
124 : : assert(node.is());
125 [ # # ][ # # ]: 0 : if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
[ # # ]
126 : 0 : return false;
127 : : }
128 [ # # # ]: 0 : switch (node->kind()) {
129 : : case Node::KIND_LOCALIZED_PROPERTY:
130 : : case Node::KIND_GROUP:
131 [ # # ][ # # ]: 0 : for (NodeMap::const_iterator i(node->getMembers().begin());
132 [ # # ]: 0 : i != node->getMembers().end(); ++i)
133 : : {
134 [ # # ][ # # ]: 0 : if (!canRemoveFromLayer(layer, i->second)) {
135 : 0 : return false;
136 : : }
137 : : }
138 : 0 : return true;
139 : : case Node::KIND_SET:
140 : 0 : return node->getMembers().empty();
141 : : default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
142 : 0 : return true;
143 : : }
144 : : }
145 : :
146 : : }
147 : :
148 : : class Components::WriteThread: public salhelper::Thread {
149 : : public:
150 : : WriteThread(
151 : : rtl::Reference< WriteThread > * reference, Components & components,
152 : : rtl::OUString const & url, Data const & data);
153 : :
154 : 316 : void flush() { delay_.set(); }
155 : :
156 : : private:
157 [ + - ][ + - ]: 5152 : virtual ~WriteThread() {}
[ - + ]
158 : :
159 : : virtual void execute();
160 : :
161 : : rtl::Reference< WriteThread > * reference_;
162 : : Components & components_;
163 : : rtl::OUString url_;
164 : : Data const & data_;
165 : : osl::Condition delay_;
166 : : boost::shared_ptr<osl::Mutex> lock_;
167 : : };
168 : :
169 : 2576 : Components::WriteThread::WriteThread(
170 : : rtl::Reference< WriteThread > * reference, Components & components,
171 : : rtl::OUString const & url, Data const & data):
172 : : Thread("configmgrWriter"), reference_(reference), components_(components),
173 [ + - ][ + - ]: 2576 : url_(url), data_(data)
174 : : {
175 [ + - ][ + - ]: 2576 : lock_ = lock();
[ + - ]
176 : : assert(reference != 0);
177 : 2576 : }
178 : :
179 : 2576 : void Components::WriteThread::execute() {
180 : 2576 : TimeValue t = { 1, 0 }; // 1 sec
181 [ + - ]: 2576 : delay_.wait(&t); // must not throw; result_error is harmless and ignored
182 [ + - ]: 2576 : osl::MutexGuard g(*lock_); // must not throw
183 : : try {
184 : : try {
185 [ + - ]: 2576 : writeModFile(components_, url_, data_);
186 [ # # ]: 0 : } catch (css::uno::RuntimeException & e) {
187 : : // Ignore write errors, instead of aborting:
188 : : SAL_WARN(
189 : : "configmgr",
190 : : "error writing modifications: \"" << e.Message << '"');
191 : : }
192 : 0 : } catch (...) {
193 [ # # ]: 0 : reference_->clear();
194 : 0 : throw;
195 : : }
196 [ + - ][ + - ]: 2576 : reference_->clear();
197 [ # # ]: 2576 : }
198 : :
199 : : class theComponentsSingleton :
200 : : public rtl::StaticWithArg<
201 : : Components,
202 : : css::uno::Reference< css::uno::XComponentContext >,
203 : : theComponentsSingleton>
204 : : {
205 : : };
206 : :
207 : 105902 : Components & Components::getSingleton(
208 : : css::uno::Reference< css::uno::XComponentContext > const & context)
209 : : {
210 : : assert(context.is());
211 : 105902 : return theComponentsSingleton::get(context);
212 : : }
213 : :
214 : 724182 : bool Components::allLocales(rtl::OUString const & locale) {
215 : 724182 : return locale == "*";
216 : : }
217 : :
218 : 121648 : rtl::Reference< Node > Components::resolvePathRepresentation(
219 : : rtl::OUString const & pathRepresentation,
220 : : rtl::OUString * canonicRepresentation, Path * path, int * finalizedLayer)
221 : : const
222 : : {
223 : : return data_.resolvePathRepresentation(
224 : 121648 : pathRepresentation, canonicRepresentation, path, finalizedLayer);
225 : : }
226 : :
227 : 1838 : rtl::Reference< Node > Components::getTemplate(
228 : : int layer, rtl::OUString const & fullName) const
229 : : {
230 : 1838 : return data_.getTemplate(layer, fullName);
231 : : }
232 : :
233 : 105314 : void Components::addRootAccess(rtl::Reference< RootAccess > const & access) {
234 [ + - ]: 105314 : roots_.insert(access.get());
235 : 105314 : }
236 : :
237 : 103409 : void Components::removeRootAccess(RootAccess * access) {
238 : 103409 : roots_.erase(access);
239 : 103409 : }
240 : :
241 : 18631 : void Components::initGlobalBroadcaster(
242 : : Modifications const & modifications,
243 : : rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
244 : : {
245 : : //TODO: Iterate only over roots w/ listeners:
246 [ + + ]: 1363895 : for (WeakRootSet::iterator i(roots_.begin()); i != roots_.end(); ++i) {
247 : 1345264 : rtl::Reference< RootAccess > root;
248 [ + + ][ + - ]: 1345264 : if ((*i)->acquireCounting() > 1) {
249 : 1345262 : root.set(*i); // must not throw
250 : : }
251 [ + - ]: 1345264 : (*i)->releaseNondeleting();
252 [ + + ]: 1345264 : if (root.is()) {
253 [ + + ]: 1345262 : if (root != exclude) {
254 [ + - ]: 1326631 : Path path(root->getAbsolutePath());
255 [ + - ]: 1326631 : Modifications::Node const * mods = &modifications.getRoot();
256 [ + - ][ + + ]: 1387290 : for (Path::iterator j(path.begin()); j != path.end(); ++j) {
257 : : Modifications::Node::Children::const_iterator k(
258 [ + - ]: 1335759 : mods->children.find(*j));
259 [ + + ]: 1335759 : if (k == mods->children.end()) {
260 : 1275100 : mods = 0;
261 : : break;
262 : : }
263 : 60659 : mods = &k->second;
264 : : }
265 : : //TODO: If the complete tree of which root is a part is deleted,
266 : : // or replaced, mods will be null, but some of the listeners
267 : : // from within root should probably fire nonetheless:
268 [ + + ]: 1326631 : if (mods != 0) {
269 [ + - ]: 51531 : root->initBroadcaster(*mods, broadcaster);
270 : 1326631 : }
271 : : }
272 : : }
273 : 1345264 : }
274 : 18631 : }
275 : :
276 : 193473 : void Components::addModification(Path const & path) {
277 : 193473 : data_.modifications.add(path);
278 : 193473 : }
279 : :
280 : 18631 : bool Components::hasModifications() const
281 : : {
282 [ + - ]: 18631 : return data_.modifications.getRoot().children.begin() !=
283 : 37262 : data_.modifications.getRoot().children.end();
284 : : }
285 : :
286 : 18631 : void Components::writeModifications() {
287 : :
288 [ + - ][ + + ]: 18631 : if (!hasModifications() || modificationFileUrl_.isEmpty())
[ + + ]
289 : 18631 : return;
290 : :
291 [ + + ]: 15485 : if (!writeThread_.is()) {
292 : : writeThread_ = new WriteThread(
293 [ + - ]: 2576 : &writeThread_, *this, modificationFileUrl_, data_);
294 : 2576 : writeThread_->launch();
295 : : }
296 : : }
297 : :
298 : 856 : void Components::flushModifications() {
299 : 856 : rtl::Reference< WriteThread > thread;
300 : : {
301 [ + - ]: 856 : osl::MutexGuard g(*lock_);
302 [ + - ][ + - ]: 856 : thread = writeThread_;
303 : : }
304 [ + + ]: 856 : if (thread.is()) {
305 [ + - ]: 316 : thread->flush();
306 [ + - ]: 316 : thread->join();
307 [ + - ]: 856 : }
308 : 856 : }
309 : :
310 : 0 : void Components::insertExtensionXcsFile(
311 : : bool shared, rtl::OUString const & fileUri)
312 : : {
313 : 0 : int layer = getExtensionLayer(shared);
314 : : try {
315 [ # # ]: 0 : parseXcsFile(fileUri, layer, data_, 0, 0, 0);
316 [ # # ]: 0 : } catch (css::container::NoSuchElementException & e) {
317 : : throw css::uno::RuntimeException(
318 : : (rtl::OUString(
319 : : RTL_CONSTASCII_USTRINGPARAM(
320 : : "insertExtensionXcsFile does not exist: ")) +
321 : : e.Message),
322 [ # # # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
323 : : }
324 : 0 : }
325 : :
326 : 0 : void Components::insertExtensionXcuFile(
327 : : bool shared, rtl::OUString const & fileUri, Modifications * modifications)
328 : : {
329 : : assert(modifications != 0);
330 : 0 : int layer = getExtensionLayer(shared) + 1;
331 : 0 : Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
332 : : try {
333 [ # # ]: 0 : parseXcuFile(fileUri, layer, data_, 0, modifications, adds);
334 [ # # ]: 0 : } catch (css::container::NoSuchElementException & e) {
335 [ # # # # ]: 0 : data_.removeExtensionXcuAdditions(fileUri);
336 : : throw css::uno::RuntimeException(
337 : : (rtl::OUString(
338 : : RTL_CONSTASCII_USTRINGPARAM(
339 : : "insertExtensionXcuFile does not exist: ")) +
340 : : e.Message),
341 [ # # # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
342 : : }
343 : 0 : }
344 : :
345 : 0 : void Components::removeExtensionXcuFile(
346 : : rtl::OUString const & fileUri, Modifications * modifications)
347 : : {
348 : : //TODO: Ideally, exactly the data coming from the specified xcu file would
349 : : // be removed. However, not enough information is recorded in the in-memory
350 : : // data structures to do so. So, as a workaround, all those set elements
351 : : // that were freshly added by the xcu and have afterwards been left
352 : : // unchanged or have only had their properties changed in the user layer are
353 : : // removed (and nothing else). The heuristic to determine
354 : : // whether a node has been left unchanged is to check the layer ID (as
355 : : // usual) and additionally to check that the node does not recursively
356 : : // contain any non-empty sets (multiple extension xcu files are merged into
357 : : // one layer, so checking layer ID alone is not enough). Since
358 : : // item->additions records all additions of set members in textual order,
359 : : // the latter check works well when iterating through item->additions in
360 : : // reverse order.
361 : : assert(modifications != 0);
362 : : rtl::Reference< Data::ExtensionXcu > item(
363 [ # # ]: 0 : data_.removeExtensionXcuAdditions(fileUri));
364 [ # # ]: 0 : if (item.is()) {
365 [ # # # # ]: 0 : for (Additions::reverse_iterator i(item->additions.rbegin());
[ # # ]
366 : 0 : i != item->additions.rend(); ++i)
367 : : {
368 : 0 : rtl::Reference< Node > parent;
369 [ # # ]: 0 : NodeMap const * map = &data_.getComponents();
370 : 0 : rtl::Reference< Node > node;
371 [ # # ][ # # ]: 0 : for (Path::const_iterator j(i->begin()); j != i->end(); ++j) {
[ # # ][ # # ]
[ # # ]
372 [ # # ]: 0 : parent = node;
373 [ # # ][ # # ]: 0 : node = Data::findNode(Data::NO_LAYER, *map, *j);
[ # # ]
374 [ # # ]: 0 : if (!node.is()) {
375 : 0 : break;
376 : : }
377 [ # # ]: 0 : map = &node->getMembers();
378 : : }
379 [ # # ]: 0 : if (node.is()) {
380 : : assert(parent.is());
381 [ # # ][ # # ]: 0 : if (parent->kind() == Node::KIND_SET) {
382 : : assert(
383 : : node->kind() == Node::KIND_GROUP ||
384 : : node->kind() == Node::KIND_SET);
385 [ # # ][ # # ]: 0 : if (canRemoveFromLayer(item->layer, node)) {
386 [ # # ][ # # ]: 0 : parent->getMembers().erase(i->back());
[ # # ][ # # ]
387 [ # # ][ # # ]: 0 : data_.modifications.remove(*i);
388 [ # # ][ # # ]: 0 : modifications->add(*i);
389 : : }
390 : : }
391 : : }
392 [ # # ][ # # ]: 0 : }
393 [ # # ]: 0 : writeModifications();
394 [ # # ]: 0 : }
395 : 0 : }
396 : :
397 : 0 : void Components::insertModificationXcuFile(
398 : : rtl::OUString const & fileUri,
399 : : std::set< rtl::OUString > const & includedPaths,
400 : : std::set< rtl::OUString > const & excludedPaths,
401 : : Modifications * modifications)
402 : : {
403 : : assert(modifications != 0);
404 [ # # ]: 0 : Partial part(includedPaths, excludedPaths);
405 : : try {
406 : : parseFileLeniently(
407 : : &parseXcuFile, fileUri, Data::NO_LAYER, data_, &part, modifications,
408 [ # # ]: 0 : 0);
409 [ # # ]: 0 : } catch (css::container::NoSuchElementException & e) {
410 : : SAL_WARN(
411 : : "configmgr",
412 : : "error inserting non-existing \"" << fileUri << "\": \""
413 : : << e.Message << '"');
414 [ # # ]: 0 : }
415 [ # # ]: 0 : }
416 : :
417 : 2066 : css::beans::Optional< css::uno::Any > Components::getExternalValue(
418 : : rtl::OUString const & descriptor)
419 : : {
420 : 2066 : sal_Int32 i = descriptor.indexOf(' ');
421 [ - + ]: 2066 : if (i <= 0) {
422 : : throw css::uno::RuntimeException(
423 : : (rtl::OUString(
424 : : RTL_CONSTASCII_USTRINGPARAM("bad external value descriptor ")) +
425 : : descriptor),
426 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
427 : : }
428 : : //TODO: Do not make calls with mutex locked:
429 : 2066 : rtl::OUString name(descriptor.copy(0, i));
430 [ + - ]: 2066 : ExternalServices::iterator j(externalServices_.find(name));
431 [ + + ]: 2066 : if (j == externalServices_.end()) {
432 : 573 : css::uno::Reference< css::uno::XInterface > service;
433 : : try {
434 : : service = css::uno::Reference< css::lang::XMultiComponentFactory >(
435 [ + - ][ + - ]: 1146 : context_->getServiceManager(), css::uno::UNO_SET_THROW)->
[ + - ][ + - ]
436 [ + - ][ + - ]: 573 : createInstanceWithContext(name, context_);
437 [ # # # ]: 0 : } catch (css::uno::RuntimeException &) {
438 : : // Assuming these exceptions are real errors:
439 : 0 : throw;
440 [ # # ]: 0 : } catch (css::uno::Exception & e) {
441 : : // Assuming these exceptions indicate that the service is not
442 : : // installed:
443 : : SAL_WARN(
444 : : "configmgr",
445 : : "createInstance(" << name << ") failed with \"" << e.Message
446 : : << '"');
447 : : }
448 : 573 : css::uno::Reference< css::beans::XPropertySet > propset;
449 [ + + ]: 573 : if (service.is()) {
450 : : propset = css::uno::Reference< css::beans::XPropertySet >(
451 [ + - ][ + - ]: 318 : service, css::uno::UNO_QUERY_THROW);
452 : : }
453 : : j = externalServices_.insert(
454 [ + - ][ + - ]: 573 : ExternalServices::value_type(name, propset)).first;
[ + - ]
455 : : }
456 : 2066 : css::beans::Optional< css::uno::Any > value;
457 [ + + ]: 2066 : if (j->second.is()) {
458 : : try {
459 [ + - - + ]: 1940 : if (!(j->second->getPropertyValue(descriptor.copy(i + 1)) >>=
460 [ + - ][ + - ]: 970 : value))
461 : : {
462 : : throw css::uno::RuntimeException(
463 : : (rtl::OUString(
464 : : RTL_CONSTASCII_USTRINGPARAM(
465 : : "cannot obtain external value through ")) +
466 : : descriptor),
467 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
468 : : }
469 : 0 : } catch (css::beans::UnknownPropertyException & e) {
470 : : throw css::uno::RuntimeException(
471 : : (rtl::OUString(
472 : : RTL_CONSTASCII_USTRINGPARAM(
473 : : "unknown external value descriptor ID: ")) +
474 : : e.Message),
475 [ # # # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
476 [ # # # ]: 0 : } catch (css::lang::WrappedTargetException & e) {
477 : : throw css::uno::RuntimeException(
478 : : (rtl::OUString(
479 : : RTL_CONSTASCII_USTRINGPARAM(
480 : : "cannot obtain external value: ")) +
481 : : e.Message),
482 [ # # # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
483 : : }
484 : : }
485 : 2066 : return value;
486 : : }
487 : :
488 : 268 : Components::Components(
489 : : css::uno::Reference< css::uno::XComponentContext > const & context):
490 [ + - ][ + - ]: 268 : context_(context), sharedExtensionLayer_(-1), userExtensionLayer_(-1)
[ + - ][ + - ]
491 : : {
492 : : assert(context.is());
493 [ + - ][ + - ]: 268 : lock_ = lock();
[ + - ]
494 : : rtl::OUString conf(
495 : : expand(
496 : : rtl::OUString(
497 [ + - ]: 268 : RTL_CONSTASCII_USTRINGPARAM("${CONFIGURATION_LAYERS}"))));
498 : : RTL_LOGFILE_TRACE("configmgr : begin parsing");
499 : 268 : int layer = 0;
500 : 1773 : for (sal_Int32 i = 0;;) {
501 [ + + ][ + + ]: 3010 : while (i != conf.getLength() && conf[i] == ' ') {
[ + + ]
502 : 1237 : ++i;
503 : : }
504 [ + + ]: 1773 : if (i == conf.getLength()) {
505 : : break;
506 : : }
507 [ - + ]: 1505 : if (!modificationFileUrl_.isEmpty()) {
508 : : throw css::uno::RuntimeException(
509 : : rtl::OUString(
510 : : RTL_CONSTASCII_USTRINGPARAM(
511 : : "CONFIGURATION_LAYERS: \"user\" followed by further"
512 : : " layers")),
513 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
514 : : }
515 : 1505 : sal_Int32 c = i;
516 : 9510 : for (;; ++c) {
517 [ + - ][ - + ]: 11015 : if (c == conf.getLength() || conf[c] == ' ') {
[ - + ]
518 : : throw css::uno::RuntimeException(
519 : : rtl::OUString(
520 : : RTL_CONSTASCII_USTRINGPARAM(
521 : : "CONFIGURATION_LAYERS: missing \":\"")),
522 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
523 : : }
524 [ + + ]: 11015 : if (conf[c] == ':') {
525 : 1505 : break;
526 : : }
527 : : }
528 : 1505 : sal_Int32 n = conf.indexOf(' ', c + 1);
529 [ + + ]: 1505 : if (n == -1) {
530 : 268 : n = conf.getLength();
531 : : }
532 : 1505 : rtl::OUString type(conf.copy(i, c - i));
533 : 1505 : rtl::OUString url(expand(conf.copy(c + 1, n - c - 1)));
534 [ + + ]: 1505 : if ( type == "xcsxcu" ) {
535 [ + - ]: 482 : parseXcsXcuLayer(layer, url);
536 : 482 : layer += 2; //TODO: overflow
537 [ + + ]: 1023 : } else if ( type == "bundledext" )
538 : : {
539 [ + - ]: 160 : parseXcsXcuIniLayer(layer, url, false);
540 : 160 : layer += 2; //TODO: overflow
541 [ + + ]: 863 : } else if ( type == "sharedext" ) {
542 [ - + ]: 160 : if (sharedExtensionLayer_ != -1) {
543 : : throw css::uno::RuntimeException(
544 : : rtl::OUString(
545 : : RTL_CONSTASCII_USTRINGPARAM(
546 : : "CONFIGURATION_LAYERS: multiple \"sharedext\""
547 : : " layers")),
548 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
549 : : }
550 : 160 : sharedExtensionLayer_ = layer;
551 [ + - ]: 160 : parseXcsXcuIniLayer(layer, url, true);
552 : 160 : layer += 2; //TODO: overflow
553 [ + + ]: 703 : } else if ( type == "userext" ) {
554 [ - + ]: 160 : if (userExtensionLayer_ != -1) {
555 : : throw css::uno::RuntimeException(
556 : : rtl::OUString(
557 : : RTL_CONSTASCII_USTRINGPARAM(
558 : : "CONFIGURATION_LAYERS: multiple \"userext\""
559 : : " layers")),
560 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
561 : : }
562 : 160 : userExtensionLayer_ = layer;
563 [ + - ]: 160 : parseXcsXcuIniLayer(layer, url, true);
564 : 160 : layer += 2; //TODO: overflow
565 [ + + ]: 543 : } else if ( type == "module" ) {
566 [ + - ]: 223 : parseModuleLayer(layer, url);
567 : 223 : ++layer; //TODO: overflow
568 [ + + ]: 320 : } else if ( type == "res" ) {
569 [ + - ]: 160 : parseResLayer(layer, url);
570 : 160 : ++layer; //TODO: overflow
571 [ + - ]: 160 : } else if ( type == "user" ) {
572 [ - + ]: 160 : if (url.isEmpty()) {
573 : : throw css::uno::RuntimeException(
574 : : rtl::OUString(
575 : : RTL_CONSTASCII_USTRINGPARAM(
576 : : "CONFIGURATION_LAYERS: empty \"user\" URL")),
577 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
578 : : }
579 : 160 : modificationFileUrl_ = url;
580 [ + - ]: 160 : parseModificationLayer(url);
581 : : } else {
582 : : throw css::uno::RuntimeException(
583 : : (rtl::OUString(
584 : : RTL_CONSTASCII_USTRINGPARAM(
585 : : "CONFIGURATION_LAYERS: unknown layer type \"")) +
586 : : type +
587 : : rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\""))),
588 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
[ # # ]
589 : : }
590 : 1505 : i = n;
591 : 1505 : }
592 : 268 : RTL_LOGFILE_TRACE("configmgr : end parsing");
593 : 268 : }
594 : :
595 [ + - ][ + - ]: 268 : Components::~Components()
[ + - ]
596 : : {
597 [ + - ]: 268 : flushModifications();
598 [ + + ]: 2173 : for (WeakRootSet::iterator i(roots_.begin()); i != roots_.end(); ++i) {
599 [ + - ]: 1905 : (*i)->setAlive(false);
600 : : }
601 : 268 : }
602 : :
603 : 28911 : void Components::parseFileLeniently(
604 : : FileParser * parseFile, rtl::OUString const & url, int layer, Data & data,
605 : : Partial const * partial, Modifications * modifications,
606 : : Additions * additions)
607 : : {
608 : : assert(parseFile != 0);
609 : : try {
610 [ + + ]: 28911 : (*parseFile)(url, layer, data, partial, modifications, additions);
611 [ - + - ]: 124 : } catch (css::container::NoSuchElementException &) {
612 : 62 : throw;
613 : 0 : } catch (css::uno::Exception & e) { //TODO: more specific exception catching
614 : : // Ignore invalid XML files, instead of completely preventing OOo from
615 : : // starting:
616 : : SAL_WARN(
617 : : "configmgr",
618 : : "error reading \"" << url << "\": \"" << e.Message << '"');
619 : : }
620 : 28849 : }
621 : :
622 : 3515 : void Components::parseFiles(
623 : : int layer, rtl::OUString const & extension, FileParser * parseFile,
624 : : rtl::OUString const & url, bool recursive)
625 : : {
626 : 3515 : osl::Directory dir(url);
627 [ + + - ]: 3515 : switch (dir.open()) {
[ + - ]
628 : : case osl::FileBase::E_None:
629 : 2545 : break;
630 : : case osl::FileBase::E_NOENT:
631 [ + - ]: 970 : if (!recursive) {
632 : 3515 : return;
633 : : }
634 : : // fall through
635 : : default:
636 : : throw css::uno::RuntimeException(
637 : : (rtl::OUString(
638 : : RTL_CONSTASCII_USTRINGPARAM("cannot open directory ")) +
639 : : url),
640 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
641 : : }
642 : 30524 : for (;;) {
643 : 30524 : osl::DirectoryItem i;
644 [ + - ]: 30524 : osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
645 [ + + ]: 30524 : if (rc == osl::FileBase::E_NOENT) {
646 : : break;
647 : : }
648 [ - + ]: 27979 : if (rc != osl::FileBase::E_None) {
649 : : throw css::uno::RuntimeException(
650 : : (rtl::OUString(
651 : : RTL_CONSTASCII_USTRINGPARAM("cannot iterate directory ")) +
652 : : url),
653 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
654 : : }
655 : : osl::FileStatus stat(
656 : : osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
657 : 27979 : osl_FileStatus_Mask_FileURL);
658 [ - + ][ + - ]: 27979 : if (i.getFileStatus(stat) != osl::FileBase::E_None) {
659 : : throw css::uno::RuntimeException(
660 : : (rtl::OUString(
661 : : RTL_CONSTASCII_USTRINGPARAM("cannot stat in directory ")) +
662 : : url),
663 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
664 : : }
665 [ + - ][ + + ]: 27979 : if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
666 [ + - ][ + - ]: 2106 : parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
667 : : } else {
668 [ + - ]: 25873 : rtl::OUString file(stat.getFileName());
669 [ + - + + ]: 51746 : if (file.getLength() >= extension.getLength() &&
[ + + ]
670 : 25873 : file.match(extension, file.getLength() - extension.getLength()))
671 : : {
672 : : try {
673 : : parseFileLeniently(
674 [ + - ][ + - ]: 25713 : parseFile, stat.getFileURL(), layer, data_, 0, 0, 0);
675 [ # # ]: 0 : } catch (css::container::NoSuchElementException & e) {
676 : : throw css::uno::RuntimeException(
677 : : (rtl::OUString(
678 : : RTL_CONSTASCII_USTRINGPARAM(
679 : : "stat'ed file does not exist: ")) +
680 : : e.Message),
681 [ # # # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
682 : : }
683 : 25873 : }
684 : : }
685 [ + - ][ + + ]: 34039 : }
[ + - ][ + + ]
686 : : }
687 : :
688 : 196 : void Components::parseFileList(
689 : : int layer, FileParser * parseFile, rtl::OUString const & urls,
690 : : rtl::Bootstrap const & ini, bool recordAdditions)
691 : : {
692 : 3038 : for (sal_Int32 i = 0;;) {
693 : 3038 : rtl::OUString url(urls.getToken(0, ' ', i));
694 [ + - ]: 3038 : if (!url.isEmpty()) {
695 : 3038 : ini.expandMacrosFrom(url); //TODO: detect failure
696 : 3038 : Additions * adds = 0;
697 [ - + ]: 3038 : if (recordAdditions) {
698 [ # # ]: 0 : adds = data_.addExtensionXcuAdditions(url, layer);
699 : : }
700 : : try {
701 [ + - ]: 3038 : parseFileLeniently(parseFile, url, layer, data_, 0, 0, adds);
702 [ # # # # ]: 0 : } catch (css::container::NoSuchElementException & e) {
703 : : SAL_WARN(
704 : : "configmgr", "file does not exist: \"" << e.Message << '"');
705 [ # # ]: 0 : if (adds != 0) {
706 [ # # # # ]: 0 : data_.removeExtensionXcuAdditions(url);
707 : : }
708 : : }
709 : : }
710 [ + + ]: 3038 : if (i == -1) {
711 : : break;
712 : : }
713 [ + + ]: 3038 : }
714 : 196 : }
715 : :
716 : 642 : void Components::parseXcdFiles(int layer, rtl::OUString const & url) {
717 : 642 : osl::Directory dir(url);
718 [ + + - ]: 642 : switch (dir.open()) {
[ + - ]
719 : : case osl::FileBase::E_None:
720 : 642 : break;
721 : : case osl::FileBase::E_NOENT:
722 : 642 : return;
723 : : default:
724 : : throw css::uno::RuntimeException(
725 : : (rtl::OUString(
726 : : RTL_CONSTASCII_USTRINGPARAM("cannot open directory ")) +
727 : : url),
728 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
729 : : }
730 [ + - ]: 482 : UnresolvedList unres;
731 [ + - ]: 482 : XcdParser::Dependencies deps;
732 : 4062 : for (;;) {
733 : 4544 : osl::DirectoryItem i;
734 [ + - ]: 4544 : osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
735 [ + + ]: 4544 : if (rc == osl::FileBase::E_NOENT) {
736 : : break;
737 : : }
738 [ - + ]: 4062 : if (rc != osl::FileBase::E_None) {
739 : : throw css::uno::RuntimeException(
740 : : (rtl::OUString(
741 : : RTL_CONSTASCII_USTRINGPARAM("cannot iterate directory ")) +
742 : : url),
743 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
744 : : }
745 : : osl::FileStatus stat(
746 : : osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
747 : 4062 : osl_FileStatus_Mask_FileURL);
748 [ - + ][ + - ]: 4062 : if (i.getFileStatus(stat) != osl::FileBase::E_None) {
749 : : throw css::uno::RuntimeException(
750 : : (rtl::OUString(
751 : : RTL_CONSTASCII_USTRINGPARAM("cannot stat in directory ")) +
752 : : url),
753 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
754 : : }
755 [ + - ][ + + ]: 4062 : if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
756 [ + - ]: 3578 : rtl::OUString file(stat.getFileName());
757 [ + - + + ]: 7156 : if (file.getLength() >= RTL_CONSTASCII_LENGTH(".xcd") &&
[ + + ]
758 : : file.matchAsciiL(
759 : : RTL_CONSTASCII_STRINGPARAM(".xcd"),
760 : 3578 : file.getLength() - RTL_CONSTASCII_LENGTH(".xcd")))
761 : : {
762 : : rtl::OUString name(
763 : : file.copy(
764 : 2934 : 0, file.getLength() - RTL_CONSTASCII_LENGTH(".xcd")));
765 : 2934 : rtl::Reference< ParseManager > manager;
766 : : try {
767 : : manager = new ParseManager(
768 [ + - ][ + - ]: 2934 : stat.getFileURL(), new XcdParser(layer, deps, data_));
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
769 [ # # ]: 0 : } catch (css::container::NoSuchElementException & e) {
770 : : throw css::uno::RuntimeException(
771 : : (rtl::OUString(
772 : : RTL_CONSTASCII_USTRINGPARAM(
773 : : "stat'ed file does not exist: ")) +
774 : : e.Message),
775 [ # # # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
776 : : }
777 [ + + ][ + - ]: 2934 : if (manager->parse()) {
778 [ + - ]: 2614 : deps.insert(name);
779 : : } else {
780 [ + - ][ + - ]: 320 : unres.push_back(UnresolvedListItem(name, manager));
[ + - ][ + - ]
[ + - ]
781 [ + - ]: 2934 : }
782 : 3578 : }
783 : : }
784 [ + - ][ + + ]: 4544 : }
785 [ + + ]: 642 : while (!unres.empty()) {
786 : 160 : bool resolved = false;
787 [ + + ]: 480 : for (UnresolvedList::iterator i(unres.begin()); i != unres.end();) {
788 [ + - ][ + - ]: 320 : if (i->manager->parse()) {
789 [ + - ]: 320 : deps.insert(i->name);
790 [ + - ]: 320 : unres.erase(i++);
791 : 320 : resolved = true;
792 : : } else {
793 : 0 : ++i;
794 : : }
795 : : }
796 [ - + ]: 160 : if (!resolved) {
797 : : throw css::uno::RuntimeException(
798 : : (rtl::OUString(
799 : : RTL_CONSTASCII_USTRINGPARAM(
800 : : "xcd: unresolved dependencies in ")) +
801 : : url),
802 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
803 : : }
804 [ + - ][ + + ]: 642 : }
805 : : }
806 : :
807 : 482 : void Components::parseXcsXcuLayer(int layer, rtl::OUString const & url) {
808 : 482 : parseXcdFiles(layer, url);
809 : : parseFiles(
810 : : layer, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcs")),
811 : : &parseXcsFile,
812 [ + - ][ + - ]: 482 : url + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/schema")), false);
813 : : parseFiles(
814 : : layer + 1, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
815 : : &parseXcuFile,
816 [ + - ][ + - ]: 482 : url + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/data")), false);
817 : 482 : }
818 : :
819 : 480 : void Components::parseXcsXcuIniLayer(
820 : : int layer, rtl::OUString const & url, bool recordAdditions)
821 : : {
822 : : // Check if ini file exists (otherwise .override would still read global
823 : : // SCHEMA/DATA variables, which could interfere with unrelated environment
824 : : // variables):
825 : 480 : rtl::Bootstrap ini(url);
826 [ + + ]: 480 : if (ini.getHandle() != 0)
827 : : {
828 : 98 : rtl::OUStringBuffer prefix("${.override:");
829 [ + + ]: 19220 : for (sal_Int32 i = 0; i != url.getLength(); ++i) {
830 : 19122 : sal_Unicode c = url[i];
831 [ + + ]: 19122 : switch (c) {
832 : : case '$':
833 : : case ':':
834 : : case '\\':
835 [ + - ]: 98 : prefix.append('\\');
836 : : // fall through
837 : : default:
838 [ + - ]: 19122 : prefix.append(c);
839 : : }
840 : : }
841 [ + - ]: 98 : prefix.append(':');
842 : 98 : rtl::OUString urls(prefix.toString() + rtl::OUString("SCHEMA}"));
843 : 98 : rtl::Bootstrap::expandMacros(urls);
844 [ + - ]: 98 : if (!urls.isEmpty())
845 : : {
846 [ + - ]: 98 : parseFileList(layer, &parseXcsFile, urls, ini, false);
847 : : }
848 [ + - ]: 98 : urls = prefix.makeStringAndClear() + rtl::OUString("DATA}");
849 : 98 : rtl::Bootstrap::expandMacros(urls);
850 [ + - ]: 98 : if (!urls.isEmpty())
851 : : {
852 [ + - ]: 98 : parseFileList(layer + 1, &parseXcuFile, urls, ini, recordAdditions);
853 : 98 : }
854 : 480 : }
855 : 480 : }
856 : :
857 : 223 : void Components::parseModuleLayer(int layer, rtl::OUString const & url) {
858 : : parseFiles(
859 : : layer, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
860 [ + - ]: 223 : &parseXcuFile, url, false);
861 : 223 : }
862 : :
863 : 160 : void Components::parseResLayer(int layer, rtl::OUString const & url) {
864 : : rtl::OUString resUrl(
865 [ + - ]: 160 : url + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/res")));
866 [ + - ]: 160 : parseXcdFiles(layer, resUrl);
867 : : parseFiles(
868 : : layer, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
869 [ + - ][ + - ]: 160 : &parseXcuFile, resUrl, false);
870 : 160 : }
871 : :
872 : 160 : void Components::parseModificationLayer(rtl::OUString const & url) {
873 : : try {
874 [ + + ]: 160 : parseFileLeniently(&parseXcuFile, url, Data::NO_LAYER, data_, 0, 0, 0);
875 [ - + ]: 124 : } catch (css::container::NoSuchElementException &) {
876 : : SAL_INFO(
877 : : "configmgr", "user registrymodifications.xcu does not (yet) exist");
878 : : // Migrate old user layer data (can be removed once migration is no
879 : : // longer relevant, probably OOo 4; also see hack for xsi namespace in
880 : : // xmlreader::XmlReader::registerNamespaceIri):
881 : : parseFiles(
882 : : Data::NO_LAYER, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
883 : : &parseXcuFile,
884 : : expand(
885 : : rtl::OUString(
886 : : RTL_CONSTASCII_USTRINGPARAM(
887 : : "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap")
888 : : ":UserInstallation}/user/registry/data"))),
889 [ - + ][ - + ]: 62 : false);
[ - + ]
890 : : }
891 : 160 : }
892 : :
893 : 0 : int Components::getExtensionLayer(bool shared) {
894 [ # # ]: 0 : int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_;
895 [ # # ]: 0 : if (layer == -1) {
896 : : throw css::uno::RuntimeException(
897 : : rtl::OUString(
898 : : RTL_CONSTASCII_USTRINGPARAM(
899 : : "insert extension xcs/xcu file into undefined layer")),
900 [ # # ][ # # ]: 0 : css::uno::Reference< css::uno::XInterface >());
901 : : }
902 : 0 : return layer;
903 : : }
904 : :
905 : : }
906 : :
907 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|