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