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