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