xref: /aoo41x/main/configmgr/source/xcuparser.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 
33 #include "com/sun/star/uno/Any.hxx"
34 #include "com/sun/star/uno/Reference.hxx"
35 #include "com/sun/star/uno/RuntimeException.hpp"
36 #include "com/sun/star/uno/XInterface.hpp"
37 #include "osl/diagnose.h"
38 #include "rtl/ref.hxx"
39 #include "rtl/strbuf.hxx"
40 #include "rtl/string.h"
41 #include "rtl/string.hxx"
42 #include "rtl/ustring.h"
43 #include "rtl/ustring.hxx"
44 #include "xmlreader/span.hxx"
45 #include "xmlreader/xmlreader.hxx"
46 
47 #include "data.hxx"
48 #include "localizedpropertynode.hxx"
49 #include "localizedvaluenode.hxx"
50 #include "groupnode.hxx"
51 #include "modifications.hxx"
52 #include "node.hxx"
53 #include "nodemap.hxx"
54 #include "parsemanager.hxx"
55 #include "partial.hxx"
56 #include "path.hxx"
57 #include "propertynode.hxx"
58 #include "setnode.hxx"
59 #include "xcuparser.hxx"
60 #include "xmldata.hxx"
61 
62 namespace configmgr {
63 
64 namespace {
65 
66 namespace css = com::sun::star;
67 
68 }
69 
70 XcuParser::XcuParser(
71     int layer, Data & data, Partial const * partial,
72     Modifications * broadcastModifications, Additions * additions):
73     valueParser_(layer), data_(data),
74     partial_(partial), broadcastModifications_(broadcastModifications),
75     additions_(additions), recordModifications_(layer == Data::NO_LAYER),
76     trackPath_(
77         partial_ != 0 || broadcastModifications_ != 0 || additions_ != 0 ||
78         recordModifications_)
79 {}
80 
81 XcuParser::~XcuParser() {}
82 
83 xmlreader::XmlReader::Text XcuParser::getTextMode() {
84     return valueParser_.getTextMode();
85 }
86 
87 bool XcuParser::startElement(
88     xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
89 {
90     if (valueParser_.startElement(reader, nsId, name)) {
91         return true;
92     }
93     if (state_.empty()) {
94         if (nsId == ParseManager::NAMESPACE_OOR &&
95             name.equals(RTL_CONSTASCII_STRINGPARAM("component-data")))
96         {
97             handleComponentData(reader);
98         } else if (nsId == ParseManager::NAMESPACE_OOR &&
99                    name.equals(RTL_CONSTASCII_STRINGPARAM("items")))
100         {
101             state_.push(State(rtl::Reference< Node >(), false));
102         } else {
103             throw css::uno::RuntimeException(
104                 (rtl::OUString(
105                     RTL_CONSTASCII_USTRINGPARAM("bad root element <")) +
106                  name.convertFromUtf8() +
107                  rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
108                  reader.getUrl()),
109                 css::uno::Reference< css::uno::XInterface >());
110         }
111     } else if (state_.top().ignore) {
112         state_.push(State(false));
113     } else if (!state_.top().node.is()) {
114         if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
115             name.equals(RTL_CONSTASCII_STRINGPARAM("item")))
116         {
117             handleItem(reader);
118         } else {
119             throw css::uno::RuntimeException(
120                 (rtl::OUString(
121                     RTL_CONSTASCII_USTRINGPARAM("bad items node member <")) +
122                  name.convertFromUtf8() +
123                  rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
124                  reader.getUrl()),
125                 css::uno::Reference< css::uno::XInterface >());
126         }
127     } else {
128         switch (state_.top().node->kind()) {
129         case Node::KIND_PROPERTY:
130             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
131                 name.equals(RTL_CONSTASCII_STRINGPARAM("value")))
132             {
133                 handlePropValue(
134                     reader,
135                     dynamic_cast< PropertyNode * >(state_.top().node.get()));
136             } else {
137                 throw css::uno::RuntimeException(
138                     (rtl::OUString(
139                         RTL_CONSTASCII_USTRINGPARAM(
140                             "bad property node member <")) +
141                      name.convertFromUtf8() +
142                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
143                      reader.getUrl()),
144                     css::uno::Reference< css::uno::XInterface >());
145             }
146             break;
147         case Node::KIND_LOCALIZED_PROPERTY:
148             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
149                 name.equals(RTL_CONSTASCII_STRINGPARAM("value")))
150             {
151                 handleLocpropValue(
152                     reader,
153                     dynamic_cast< LocalizedPropertyNode * >(
154                         state_.top().node.get()));
155             } else {
156                 throw css::uno::RuntimeException(
157                     (rtl::OUString(
158                         RTL_CONSTASCII_USTRINGPARAM(
159                             "bad localized property node member <")) +
160                      name.convertFromUtf8() +
161                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
162                      reader.getUrl()),
163                     css::uno::Reference< css::uno::XInterface >());
164             }
165             break;
166         case Node::KIND_LOCALIZED_VALUE:
167             throw css::uno::RuntimeException(
168                 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad member <")) +
169                  name.convertFromUtf8() +
170                  rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
171                  reader.getUrl()),
172                 css::uno::Reference< css::uno::XInterface >());
173         case Node::KIND_GROUP:
174             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
175                 name.equals(RTL_CONSTASCII_STRINGPARAM("prop")))
176             {
177                 handleGroupProp(
178                     reader,
179                     dynamic_cast< GroupNode * >(state_.top().node.get()));
180             } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
181                        name.equals(RTL_CONSTASCII_STRINGPARAM("node")))
182             {
183                 handleGroupNode(reader, state_.top().node);
184             } else {
185                 throw css::uno::RuntimeException(
186                     (rtl::OUString(
187                         RTL_CONSTASCII_USTRINGPARAM(
188                             "bad group node member <")) +
189                      name.convertFromUtf8() +
190                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
191                      reader.getUrl()),
192                     css::uno::Reference< css::uno::XInterface >());
193             }
194             break;
195         case Node::KIND_SET:
196             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
197                 name.equals(RTL_CONSTASCII_STRINGPARAM("node")))
198             {
199                 handleSetNode(
200                     reader, dynamic_cast< SetNode * >(state_.top().node.get()));
201             } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
202                        name.equals(RTL_CONSTASCII_STRINGPARAM("prop")))
203             {
204                 OSL_TRACE(
205                     "configmgr bad set node <prop> member in %s",
206                     rtl::OUStringToOString(
207                         reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
208                 state_.push(State(true)); // ignored
209             } else {
210                 throw css::uno::RuntimeException(
211                     (rtl::OUString(
212                         RTL_CONSTASCII_USTRINGPARAM("bad set node member <")) +
213                      name.convertFromUtf8() +
214                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) +
215                      reader.getUrl()),
216                     css::uno::Reference< css::uno::XInterface >());
217             }
218             break;
219         }
220     }
221     return true;
222 }
223 
224 void XcuParser::endElement(xmlreader::XmlReader const &) {
225     if (valueParser_.endElement()) {
226         return;
227     }
228     OSL_ASSERT(!state_.empty());
229     bool pop = state_.top().pop;
230     rtl::Reference< Node > insert;
231     rtl::OUString name;
232     if (state_.top().insert) {
233         insert = state_.top().node;
234         OSL_ASSERT(insert.is());
235         name = state_.top().name;
236     }
237     state_.pop();
238     if (insert.is()) {
239         OSL_ASSERT(!state_.empty() && state_.top().node.is());
240         state_.top().node->getMembers()[name] = insert;
241     }
242     if (pop && !path_.empty()) {
243         path_.pop_back();
244             // </item> will pop less than <item> pushed, but that is harmless,
245             // as the next <item> will reset path_
246     }
247 }
248 
249 void XcuParser::characters(xmlreader::Span const & text) {
250     valueParser_.characters(text);
251 }
252 
253 XcuParser::Operation XcuParser::parseOperation(xmlreader::Span const & text) {
254     OSL_ASSERT(text.is());
255     if (text.equals(RTL_CONSTASCII_STRINGPARAM("modify"))) {
256         return OPERATION_MODIFY;
257     }
258     if (text.equals(RTL_CONSTASCII_STRINGPARAM("replace"))) {
259         return OPERATION_REPLACE;
260     }
261     if (text.equals(RTL_CONSTASCII_STRINGPARAM("fuse"))) {
262         return OPERATION_FUSE;
263     }
264     if (text.equals(RTL_CONSTASCII_STRINGPARAM("remove"))) {
265         return OPERATION_REMOVE;
266     }
267     throw css::uno::RuntimeException(
268         (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("invalid op ")) +
269          text.convertFromUtf8()),
270         css::uno::Reference< css::uno::XInterface >());
271 }
272 
273 void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
274     rtl::OStringBuffer buf;
275     buf.append('.');
276     bool hasPackage = false;
277     bool hasName = false;
278     Operation op = OPERATION_MODIFY;
279     bool finalized = false;
280     for (;;) {
281         int attrNsId;
282         xmlreader::Span attrLn;
283         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
284             break;
285         }
286         if (attrNsId == ParseManager::NAMESPACE_OOR &&
287             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("package")))
288         {
289             if (hasPackage) {
290                 throw css::uno::RuntimeException(
291                     (rtl::OUString(
292                         RTL_CONSTASCII_USTRINGPARAM(
293                             "multiple component-update package attributes"
294                             " in ")) +
295                      reader.getUrl()),
296                     css::uno::Reference< css::uno::XInterface >());
297             }
298             hasPackage = true;
299             xmlreader::Span s(reader.getAttributeValue(false));
300             buf.insert(0, s.begin, s.length);
301         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
302                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
303         {
304             if (hasName) {
305                 throw css::uno::RuntimeException(
306                     (rtl::OUString(
307                         RTL_CONSTASCII_USTRINGPARAM(
308                             "multiple component-update name attributes in ")) +
309                      reader.getUrl()),
310                     css::uno::Reference< css::uno::XInterface >());
311             }
312             hasName = true;
313             xmlreader::Span s(reader.getAttributeValue(false));
314             buf.append(s.begin, s.length);
315         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
316                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
317         {
318             op = parseOperation(reader.getAttributeValue(true));
319         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
320                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
321         {
322             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
323         }
324     }
325     if (!hasPackage) {
326         throw css::uno::RuntimeException(
327             (rtl::OUString(
328                 RTL_CONSTASCII_USTRINGPARAM(
329                     "no component-data package attribute in ")) +
330              reader.getUrl()),
331             css::uno::Reference< css::uno::XInterface >());
332     }
333     if (!hasName) {
334         throw css::uno::RuntimeException(
335             (rtl::OUString(
336                 RTL_CONSTASCII_USTRINGPARAM(
337                     "no component-data name attribute in ")) +
338              reader.getUrl()),
339             css::uno::Reference< css::uno::XInterface >());
340     }
341     componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
342         convertFromUtf8();
343     if (trackPath_) {
344         OSL_ASSERT(path_.empty());
345         path_.push_back(componentName_);
346         if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
347         {
348             state_.push(State(true)); // ignored
349             return;
350         }
351     }
352     rtl::Reference< Node > node(
353         Data::findNode(
354             valueParser_.getLayer(), data_.components, componentName_));
355     if (!node.is()) {
356         OSL_TRACE(
357             "configmgr unknown component %s in %s",
358             rtl::OUStringToOString(
359                 componentName_, RTL_TEXTENCODING_UTF8).getStr(),
360             rtl::OUStringToOString(
361                 reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
362         state_.push(State(true)); // ignored
363         return;
364     }
365     switch (op) {
366     case OPERATION_MODIFY:
367     case OPERATION_FUSE:
368         break;
369     default:
370         throw css::uno::RuntimeException(
371             (rtl::OUString(
372                 RTL_CONSTASCII_USTRINGPARAM(
373                     "invalid operation on root node in ")) +
374              reader.getUrl()),
375             css::uno::Reference< css::uno::XInterface >());
376     }
377     int finalizedLayer = std::min(
378         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
379         node->getFinalized());
380     node->setFinalized(finalizedLayer);
381     state_.push(State(node, finalizedLayer < valueParser_.getLayer()));
382 }
383 
384 void XcuParser::handleItem(xmlreader::XmlReader & reader) {
385     xmlreader::Span attrPath;
386     for (;;) {
387         int attrNsId;
388         xmlreader::Span attrLn;
389         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
390             break;
391         }
392         if (attrNsId == ParseManager::NAMESPACE_OOR &&
393             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("path")))
394         {
395             attrPath = reader.getAttributeValue(false);
396         }
397     }
398     if (!attrPath.is()) {
399         throw css::uno::RuntimeException(
400             (rtl::OUString(
401                 RTL_CONSTASCII_USTRINGPARAM("missing path attribute in ")) +
402              reader.getUrl()),
403             css::uno::Reference< css::uno::XInterface >());
404     }
405     rtl::OUString path(attrPath.convertFromUtf8());
406     int finalizedLayer;
407     rtl::Reference< Node > node(
408         data_.resolvePathRepresentation(
409             path, 0, &path_, &finalizedLayer));
410     if (!node.is()) {
411         OSL_TRACE(
412             "configmgr unknown item %s in %s",
413             rtl::OUStringToOString(path, RTL_TEXTENCODING_UTF8).getStr(),
414             rtl::OUStringToOString(
415                 reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
416         state_.push(State(true)); // ignored
417         return;
418     }
419     OSL_ASSERT(!path_.empty());
420     componentName_ = path_.front();
421     if (trackPath_) {
422         if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
423         {
424             state_.push(State(true)); // ignored
425             return;
426         }
427     } else {
428         path_.clear();
429     }
430     switch (node->kind()) {
431     case Node::KIND_PROPERTY:
432     case Node::KIND_LOCALIZED_VALUE:
433         OSL_TRACE(
434             "configmgr item of bad type %s in %s",
435             rtl::OUStringToOString(path, RTL_TEXTENCODING_UTF8).getStr(),
436             rtl::OUStringToOString(
437                 reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
438         state_.push(State(true)); // ignored
439         return;
440     case Node::KIND_LOCALIZED_PROPERTY:
441         valueParser_.type_ = dynamic_cast< LocalizedPropertyNode * >(
442             node.get())->getStaticType();
443         break;
444     default:
445         break;
446     }
447     state_.push(State(node, finalizedLayer < valueParser_.getLayer()));
448 }
449 
450 void XcuParser::handlePropValue(
451     xmlreader::XmlReader & reader, PropertyNode * prop)
452  {
453     bool nil = false;
454     rtl::OString separator;
455     rtl::OUString external;
456     for (;;) {
457         int attrNsId;
458         xmlreader::Span attrLn;
459         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
460             break;
461         }
462         if (attrNsId == ParseManager::NAMESPACE_XSI &&
463             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("nil")))
464         {
465             nil = xmldata::parseBoolean(reader.getAttributeValue(true));
466         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
467             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
468         {
469             Type type = xmldata::parseType(
470                 reader, reader.getAttributeValue(true));
471             if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
472                 throw css::uno::RuntimeException(
473                     (rtl::OUString(
474                         RTL_CONSTASCII_USTRINGPARAM("invalid value type in ")) +
475                      reader.getUrl()),
476                     css::uno::Reference< css::uno::XInterface >());
477             }
478             valueParser_.type_ = type;
479         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
480             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("separator")))
481         {
482             xmlreader::Span s(reader.getAttributeValue(false));
483             if (s.length == 0) {
484                 throw css::uno::RuntimeException(
485                     (rtl::OUString(
486                         RTL_CONSTASCII_USTRINGPARAM(
487                             "bad oor:separator attribute in ")) +
488                      reader.getUrl()),
489                     css::uno::Reference< css::uno::XInterface >());
490             }
491             separator = rtl::OString(s.begin, s.length);
492         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
493             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("external")))
494         {
495             external = reader.getAttributeValue(true).convertFromUtf8();
496             if (external.getLength() == 0) {
497                 throw css::uno::RuntimeException(
498                     (rtl::OUString(
499                         RTL_CONSTASCII_USTRINGPARAM(
500                             "bad oor:external attribute value in ")) +
501                      reader.getUrl()),
502                     css::uno::Reference< css::uno::XInterface >());
503             }
504         }
505     }
506     if (nil) {
507         if (!prop->isNillable()) {
508             throw css::uno::RuntimeException(
509                 (rtl::OUString(
510                     RTL_CONSTASCII_USTRINGPARAM(
511                         "xsi:nil attribute for non-nillable prop in ")) +
512                  reader.getUrl()),
513                 css::uno::Reference< css::uno::XInterface >());
514         }
515         if (external.getLength() != 0) {
516             throw css::uno::RuntimeException(
517                 (rtl::OUString(
518                     RTL_CONSTASCII_USTRINGPARAM(
519                         "xsi:nil and oor:external attributes for prop in ")) +
520                  reader.getUrl()),
521                 css::uno::Reference< css::uno::XInterface >());
522         }
523         prop->setValue(valueParser_.getLayer(), css::uno::Any());
524         state_.push(State(false));
525     } else if (external.getLength() == 0) {
526         valueParser_.separator_ = separator;
527         valueParser_.start(prop);
528     } else {
529         prop->setExternal(valueParser_.getLayer(), external);
530         state_.push(State(false));
531     }
532 }
533 
534 void XcuParser::handleLocpropValue(
535     xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
536 {
537     rtl::OUString name;
538     bool nil = false;
539     rtl::OString separator;
540     Operation op = OPERATION_FUSE;
541     for (;;) {
542         int attrNsId;
543         xmlreader::Span attrLn;
544         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
545             break;
546         }
547         if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
548             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("lang")))
549         {
550             name = reader.getAttributeValue(false).convertFromUtf8();
551         } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
552             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("nil")))
553         {
554             nil = xmldata::parseBoolean(reader.getAttributeValue(true));
555         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
556             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
557         {
558             Type type = xmldata::parseType(
559                 reader, reader.getAttributeValue(true));
560             if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
561                 throw css::uno::RuntimeException(
562                     (rtl::OUString(
563                         RTL_CONSTASCII_USTRINGPARAM("invalid value type in ")) +
564                      reader.getUrl()),
565                     css::uno::Reference< css::uno::XInterface >());
566             }
567             valueParser_.type_ = type;
568         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
569             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("separator")))
570         {
571             xmlreader::Span s(reader.getAttributeValue(false));
572             if (s.length == 0) {
573                 throw css::uno::RuntimeException(
574                     (rtl::OUString(
575                         RTL_CONSTASCII_USTRINGPARAM(
576                             "bad oor:separator attribute in ")) +
577                      reader.getUrl()),
578                     css::uno::Reference< css::uno::XInterface >());
579             }
580             separator = rtl::OString(s.begin, s.length);
581         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
582             attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
583         {
584             op = parseOperation(reader.getAttributeValue(true));
585         }
586     }
587     if (trackPath_) {
588         path_.push_back(name);
589         if (partial_ != 0 &&
590             partial_->contains(path_) != Partial::CONTAINS_NODE)
591         {
592             state_.push(State(true)); // ignored
593             return;
594         }
595     }
596     NodeMap::iterator i(locprop->getMembers().find(name));
597     if (i != locprop->getMembers().end() &&
598         i->second->getLayer() > valueParser_.getLayer())
599     {
600         state_.push(State(true)); // ignored
601         return;
602     }
603     if (nil && !locprop->isNillable()) {
604         throw css::uno::RuntimeException(
605             (rtl::OUString(
606                 RTL_CONSTASCII_USTRINGPARAM(
607                     "xsi:nil attribute for non-nillable prop in ")) +
608              reader.getUrl()),
609             css::uno::Reference< css::uno::XInterface >());
610     }
611     switch (op) {
612     case OPERATION_FUSE:
613         {
614             bool pop = false;
615             if (nil) {
616                 if (i == locprop->getMembers().end()) {
617                     locprop->getMembers()[name] = new LocalizedValueNode(
618                         valueParser_.getLayer(), css::uno::Any());
619                 } else {
620                     dynamic_cast< LocalizedValueNode * >(
621                         i->second.get())->setValue(
622                             valueParser_.getLayer(), css::uno::Any());
623                 }
624                 state_.push(State(true));
625             } else {
626                 valueParser_.separator_ = separator;
627                 valueParser_.start(locprop, name);
628                 pop = true;
629             }
630             if (trackPath_) {
631                 recordModification(false);
632                 if (pop) {
633                     path_.pop_back();
634                 }
635             }
636         }
637         break;
638     case OPERATION_REMOVE:
639         //TODO: only allow if parent.op == OPERATION_FUSE
640         //TODO: disallow removing when e.g. lang=""?
641         if (i != locprop->getMembers().end()) {
642             locprop->getMembers().erase(i);
643         }
644         state_.push(State(true));
645         recordModification(false);
646         break;
647     default:
648         throw css::uno::RuntimeException(
649             (rtl::OUString(
650                 RTL_CONSTASCII_USTRINGPARAM(
651                     "bad op attribute for value element in ")) +
652              reader.getUrl()),
653             css::uno::Reference< css::uno::XInterface >());
654     }
655 }
656 
657 void XcuParser::handleGroupProp(
658     xmlreader::XmlReader & reader, GroupNode * group)
659 {
660     bool hasName = false;
661     rtl::OUString name;
662     Type type = TYPE_ERROR;
663     Operation op = OPERATION_MODIFY;
664     bool finalized = false;
665     for (;;) {
666         int attrNsId;
667         xmlreader::Span attrLn;
668         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
669             break;
670         }
671         if (attrNsId == ParseManager::NAMESPACE_OOR &&
672                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
673         {
674             hasName = true;
675             name = reader.getAttributeValue(false).convertFromUtf8();
676         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
677                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("type")))
678         {
679             type = xmldata::parseType(reader, reader.getAttributeValue(true));
680         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
681                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
682         {
683             op = parseOperation(reader.getAttributeValue(true));
684         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
685                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
686         {
687             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
688         }
689     }
690     if (!hasName) {
691         throw css::uno::RuntimeException(
692             (rtl::OUString(
693                 RTL_CONSTASCII_USTRINGPARAM("no prop name attribute in ")) +
694              reader.getUrl()),
695             css::uno::Reference< css::uno::XInterface >());
696     }
697     if (trackPath_) {
698         path_.push_back(name);
699         //TODO: This ignores locprop values for which specific include paths
700         // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
701         if (partial_ != 0 &&
702             partial_->contains(path_) != Partial::CONTAINS_NODE)
703         {
704             state_.push(State(true)); // ignored
705             return;
706         }
707     }
708     NodeMap::iterator i(group->getMembers().find(name));
709     if (i == group->getMembers().end()) {
710         handleUnknownGroupProp(reader, group, name, type, op, finalized);
711     } else {
712         switch (i->second->kind()) {
713         case Node::KIND_PROPERTY:
714             handlePlainGroupProp(reader, group, i, name, type, op, finalized);
715             break;
716         case Node::KIND_LOCALIZED_PROPERTY:
717             handleLocalizedGroupProp(
718                 reader,
719                 dynamic_cast< LocalizedPropertyNode * >(i->second.get()), name,
720                 type, op, finalized);
721             break;
722         default:
723             throw css::uno::RuntimeException(
724                 (rtl::OUString(
725                     RTL_CONSTASCII_USTRINGPARAM("inappropriate prop ")) +
726                  name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
727                  reader.getUrl()),
728                 css::uno::Reference< css::uno::XInterface >());
729         }
730     }
731 }
732 
733 void XcuParser::handleUnknownGroupProp(
734     xmlreader::XmlReader const & reader, GroupNode * group,
735     rtl::OUString const & name, Type type, Operation operation, bool finalized)
736 {
737     switch (operation) {
738     case OPERATION_REPLACE:
739     case OPERATION_FUSE:
740         if (group->isExtensible()) {
741             if (type == TYPE_ERROR) {
742                 throw css::uno::RuntimeException(
743                     (rtl::OUString(
744                         RTL_CONSTASCII_USTRINGPARAM(
745                             "missing type attribute for prop ")) +
746                  name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
747                  reader.getUrl()),
748                 css::uno::Reference< css::uno::XInterface >());
749             }
750             valueParser_.type_ = type;
751             rtl::Reference< Node > prop(
752                 new PropertyNode(
753                     valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
754                     true));
755             if (finalized) {
756                 prop->setFinalized(valueParser_.getLayer());
757             }
758             state_.push(State(prop, name, state_.top().locked));
759             recordModification(false);
760             break;
761         }
762         // fall through
763     default:
764         OSL_TRACE(
765             "configmgr unknown property %s in %s",
766             rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr(),
767             rtl::OUStringToOString(
768                 reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
769         state_.push(State(true)); // ignored
770         break;
771     }
772 }
773 
774 void XcuParser::handlePlainGroupProp(
775     xmlreader::XmlReader const & reader, GroupNode * group,
776     NodeMap::iterator const & propertyIndex, rtl::OUString const & name,
777     Type type, Operation operation, bool finalized)
778 {
779     PropertyNode * property = dynamic_cast< PropertyNode * >(
780         propertyIndex->second.get());
781     if (property->getLayer() > valueParser_.getLayer()) {
782         state_.push(State(true)); // ignored
783         return;
784     }
785     int finalizedLayer = std::min(
786         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
787         property->getFinalized());
788     property->setFinalized(finalizedLayer);
789     if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
790         type != property->getStaticType())
791     {
792         throw css::uno::RuntimeException(
793             (rtl::OUString(
794                 RTL_CONSTASCII_USTRINGPARAM("invalid type for prop ")) +
795              name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
796              reader.getUrl()),
797             css::uno::Reference< css::uno::XInterface >());
798     }
799     valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
800     switch (operation) {
801     case OPERATION_MODIFY:
802     case OPERATION_REPLACE:
803     case OPERATION_FUSE:
804         state_.push(
805             State(
806                 property,
807                 (state_.top().locked ||
808                  finalizedLayer < valueParser_.getLayer())));
809         recordModification(false);
810         break;
811     case OPERATION_REMOVE:
812         if (!property->isExtension()) {
813             throw css::uno::RuntimeException(
814                 (rtl::OUString(
815                     RTL_CONSTASCII_USTRINGPARAM(
816                         "invalid remove of non-extension prop ")) +
817                  name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
818                  reader.getUrl()),
819                 css::uno::Reference< css::uno::XInterface >());
820         }
821         group->getMembers().erase(propertyIndex);
822         state_.push(State(true)); // ignore children
823         recordModification(false);
824         break;
825     }
826 }
827 
828 void XcuParser::handleLocalizedGroupProp(
829     xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
830     rtl::OUString const & name, Type type, Operation operation, bool finalized)
831 {
832     if (property->getLayer() > valueParser_.getLayer()) {
833         state_.push(State(true)); // ignored
834         return;
835     }
836     int finalizedLayer = std::min(
837         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
838         property->getFinalized());
839     property->setFinalized(finalizedLayer);
840     if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
841         type != property->getStaticType())
842     {
843         throw css::uno::RuntimeException(
844             (rtl::OUString(
845                 RTL_CONSTASCII_USTRINGPARAM("invalid type for prop ")) +
846              name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
847              reader.getUrl()),
848             css::uno::Reference< css::uno::XInterface >());
849     }
850     valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
851     switch (operation) {
852     case OPERATION_MODIFY:
853     case OPERATION_FUSE:
854         state_.push(
855             State(
856                 property,
857                 (state_.top().locked ||
858                  finalizedLayer < valueParser_.getLayer())));
859         break;
860     case OPERATION_REPLACE:
861         {
862             rtl::Reference< Node > replacement(
863                 new LocalizedPropertyNode(
864                     valueParser_.getLayer(), property->getStaticType(),
865                     property->isNillable()));
866             replacement->setFinalized(property->getFinalized());
867             state_.push(
868                 State(
869                     replacement, name,
870                     (state_.top().locked ||
871                      finalizedLayer < valueParser_.getLayer())));
872             recordModification(false);
873         }
874         break;
875     case OPERATION_REMOVE:
876         throw css::uno::RuntimeException(
877             (rtl::OUString(
878                 RTL_CONSTASCII_USTRINGPARAM(
879                     "invalid remove of non-extension prop ")) +
880              name + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
881              reader.getUrl()),
882             css::uno::Reference< css::uno::XInterface >());
883     }
884 }
885 
886 void XcuParser::handleGroupNode(
887     xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
888 {
889     bool hasName = false;
890     rtl::OUString name;
891     Operation op = OPERATION_MODIFY;
892     bool finalized = false;
893     for (;;) {
894         int attrNsId;
895         xmlreader::Span attrLn;
896         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
897             break;
898         }
899         if (attrNsId == ParseManager::NAMESPACE_OOR &&
900                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
901         {
902             hasName = true;
903             name = reader.getAttributeValue(false).convertFromUtf8();
904         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
905                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
906         {
907             op = parseOperation(reader.getAttributeValue(true));
908         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
909                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
910         {
911             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
912         }
913     }
914     if (!hasName) {
915         throw css::uno::RuntimeException(
916             (rtl::OUString(
917                 RTL_CONSTASCII_USTRINGPARAM("no node name attribute in ")) +
918              reader.getUrl()),
919             css::uno::Reference< css::uno::XInterface >());
920     }
921     if (trackPath_) {
922         path_.push_back(name);
923         if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
924         {
925             state_.push(State(true)); // ignored
926             return;
927         }
928     }
929     rtl::Reference< Node > child(
930         Data::findNode(valueParser_.getLayer(), group->getMembers(), name));
931     if (!child.is()) {
932         OSL_TRACE(
933             "configmgr unknown node %s in %s",
934             rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr(),
935             rtl::OUStringToOString(
936                 reader.getUrl(), RTL_TEXTENCODING_UTF8).getStr());
937         state_.push(State(true)); // ignored
938         return;
939     }
940     if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
941         throw css::uno::RuntimeException(
942             (rtl::OUString(
943                 RTL_CONSTASCII_USTRINGPARAM(
944                     "invalid operation on group node in ")) +
945              reader.getUrl()),
946             css::uno::Reference< css::uno::XInterface >());
947     }
948     int finalizedLayer = std::min(
949         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
950         child->getFinalized());
951     child->setFinalized(finalizedLayer);
952     state_.push(
953         State(
954             child,
955             state_.top().locked || finalizedLayer < valueParser_.getLayer()));
956 }
957 
958 void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
959     bool hasName = false;
960     rtl::OUString name;
961     rtl::OUString component(componentName_);
962     bool hasNodeType = false;
963     rtl::OUString nodeType;
964     Operation op = OPERATION_MODIFY;
965     bool finalized = false;
966     bool mandatory = false;
967     for (;;) {
968         int attrNsId;
969         xmlreader::Span attrLn;
970         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
971             break;
972         }
973         if (attrNsId == ParseManager::NAMESPACE_OOR &&
974                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("name")))
975         {
976             hasName = true;
977             name = reader.getAttributeValue(false).convertFromUtf8();
978         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
979                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("component")))
980         {
981             component = reader.getAttributeValue(false).convertFromUtf8();
982         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
983                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("node-type")))
984         {
985             hasNodeType = true;
986             nodeType = reader.getAttributeValue(false).convertFromUtf8();
987         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
988                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("op")))
989         {
990             op = parseOperation(reader.getAttributeValue(true));
991         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
992                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("finalized")))
993         {
994             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
995         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
996                    attrLn.equals(RTL_CONSTASCII_STRINGPARAM("mandatory")))
997         {
998             mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
999         }
1000     }
1001     if (!hasName) {
1002         throw css::uno::RuntimeException(
1003             (rtl::OUString(
1004                 RTL_CONSTASCII_USTRINGPARAM("no node name attribute in ")) +
1005              reader.getUrl()),
1006             css::uno::Reference< css::uno::XInterface >());
1007     }
1008     if (trackPath_) {
1009         path_.push_back(name);
1010         if (partial_ != 0 && partial_->contains(path_) == Partial::CONTAINS_NOT)
1011         {
1012             state_.push(State(true)); // ignored
1013             return;
1014         }
1015     }
1016     rtl::OUString templateName(
1017         xmldata::parseTemplateReference(
1018             component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
1019     if (!set->isValidTemplate(templateName)) {
1020         throw css::uno::RuntimeException(
1021             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("set member node ")) +
1022              name +
1023              rtl::OUString(
1024                  RTL_CONSTASCII_USTRINGPARAM(" references invalid template ")) +
1025              templateName + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
1026              reader.getUrl()),
1027             css::uno::Reference< css::uno::XInterface >());
1028     }
1029     rtl::Reference< Node > tmpl(
1030         data_.getTemplate(valueParser_.getLayer(), templateName));
1031     if (!tmpl.is()) {
1032         throw css::uno::RuntimeException(
1033             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("set member node ")) +
1034              name +
1035              rtl::OUString(
1036                  RTL_CONSTASCII_USTRINGPARAM(
1037                      " references undefined template ")) +
1038              templateName + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" in ")) +
1039              reader.getUrl()),
1040             css::uno::Reference< css::uno::XInterface >());
1041     }
1042     int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
1043     int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
1044     NodeMap::iterator i(set->getMembers().find(name));
1045     if (i != set->getMembers().end()) {
1046         finalizedLayer = std::min(finalizedLayer, i->second->getFinalized());
1047         i->second->setFinalized(finalizedLayer);
1048         mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
1049         i->second->setMandatory(mandatoryLayer);
1050         if (i->second->getLayer() > valueParser_.getLayer()) {
1051             state_.push(State(true)); // ignored
1052             return;
1053         }
1054     }
1055     switch (op) {
1056     case OPERATION_MODIFY:
1057         if (i == set->getMembers().end()) {
1058             OSL_TRACE("ignoring modify of unknown set member node");
1059             state_.push(State(true)); // ignored
1060         } else {
1061             state_.push(
1062                 State(
1063                     i->second,
1064                     (state_.top().locked ||
1065                      finalizedLayer < valueParser_.getLayer())));
1066         }
1067         break;
1068     case OPERATION_REPLACE:
1069         if (state_.top().locked || finalizedLayer < valueParser_.getLayer()) {
1070             state_.push(State(true)); // ignored
1071         } else {
1072             rtl::Reference< Node > member(tmpl->clone(true));
1073             member->setLayer(valueParser_.getLayer());
1074             member->setFinalized(finalizedLayer);
1075             member->setMandatory(mandatoryLayer);
1076             state_.push(State(member, name, false));
1077             recordModification(i == set->getMembers().end());
1078         }
1079         break;
1080     case OPERATION_FUSE:
1081         if (i == set->getMembers().end()) {
1082             if (state_.top().locked || finalizedLayer < valueParser_.getLayer())
1083             {
1084                 state_.push(State(true)); // ignored
1085             } else {
1086                 rtl::Reference< Node > member(tmpl->clone(true));
1087                 member->setLayer(valueParser_.getLayer());
1088                 member->setFinalized(finalizedLayer);
1089                 member->setMandatory(mandatoryLayer);
1090                 state_.push(State(member, name, false));
1091                 recordModification(true);
1092             }
1093         } else {
1094             state_.push(
1095                 State(
1096                     i->second,
1097                     (state_.top().locked ||
1098                      finalizedLayer < valueParser_.getLayer())));
1099         }
1100         break;
1101     case OPERATION_REMOVE:
1102         {
1103             // Ignore removal of unknown members, members finalized in a lower
1104             // layer, and members made mandatory in this or a lower layer;
1105             // forget about user-layer removals that no longer remove anything
1106             // (so that paired additions/removals in the user layer do not grow
1107             // registrymodifications.xcu unbounded):
1108             bool known = i != set->getMembers().end();
1109             if (known && !state_.top().locked &&
1110                 finalizedLayer >= valueParser_.getLayer() &&
1111                 mandatoryLayer > valueParser_.getLayer())
1112             {
1113                 set->getMembers().erase(i);
1114             }
1115             state_.push(State(true));
1116             if (known) {
1117                 recordModification(false);
1118             }
1119             break;
1120         }
1121     }
1122 }
1123 
1124 void XcuParser::recordModification(bool addition) {
1125     if (broadcastModifications_ != 0) {
1126         broadcastModifications_->add(path_);
1127     }
1128     if (addition && additions_ != 0) {
1129         additions_->push_back(path_);
1130     }
1131     if (recordModifications_) {
1132         data_.modifications.add(path_);
1133     }
1134 }
1135 
1136 }
1137