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