LibreOffice Module configmgr (master)  1
xcsparser.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cassert>
23 #include <set>
24 
25 #include <com/sun/star/uno/Any.hxx>
26 #include <com/sun/star/uno/RuntimeException.hpp>
27 #include <rtl/ref.hxx>
28 #include <rtl/strbuf.hxx>
29 #include <rtl/string.hxx>
30 #include <rtl/ustring.hxx>
31 #include <xmlreader/span.hxx>
32 #include <xmlreader/xmlreader.hxx>
33 
34 #include "data.hxx"
36 #include "groupnode.hxx"
37 #include "node.hxx"
38 #include "nodemap.hxx"
39 #include "parsemanager.hxx"
40 #include "propertynode.hxx"
41 #include "setnode.hxx"
42 #include "xcsparser.hxx"
43 #include "xmldata.hxx"
44 
45 namespace configmgr {
46 
47 namespace {
48 
49 // Conservatively merge a template or component (and its recursive parts) into
50 // an existing instance:
51 void merge(
52  rtl::Reference< Node > const & original,
53  rtl::Reference< Node > const & update)
54 {
55  assert(
56  original.is() && update.is() && original->kind() == update->kind() &&
57  update->getFinalized() == Data::NO_LAYER);
58  if (update->getLayer() < original->getLayer() ||
59  update->getLayer() > original->getFinalized())
60  return;
61 
62  switch (original->kind()) {
66  break; //TODO: merge certain parts?
67  case Node::KIND_GROUP:
68  for (auto const& updateMember : update->getMembers())
69  {
70  NodeMap & members = original->getMembers();
71  NodeMap::iterator i1(members.find(updateMember.first));
72  if (i1 == members.end()) {
73  if (updateMember.second->kind() == Node::KIND_PROPERTY &&
74  static_cast< GroupNode * >(
75  original.get())->isExtensible())
76  {
77  members.insert(updateMember);
78  }
79  } else if (updateMember.second->kind() == i1->second->kind()) {
80  merge(i1->second, updateMember.second);
81  }
82  }
83  break;
84  case Node::KIND_SET:
85  for (auto const& updateMember : update->getMembers())
86  {
87  NodeMap & members = original->getMembers();
88  NodeMap::iterator i1(members.find(updateMember.first));
89  if (i1 == members.end()) {
90  if (static_cast< SetNode * >(original.get())->
91  isValidTemplate(updateMember.second->getTemplateName()))
92  {
93  members.insert(updateMember);
94  }
95  } else if (updateMember.second->kind() == i1->second->kind() &&
96  (updateMember.second->getTemplateName() ==
97  i1->second->getTemplateName()))
98  {
99  merge(i1->second, updateMember.second);
100  }
101  }
102  break;
103  case Node::KIND_ROOT:
104  assert(false); // this cannot happen
105  break;
106  }
107 }
108 
109 }
110 
111 XcsParser::XcsParser(int layer, Data & data):
112  valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
113 {}
114 
116 
118  return valueParser_.getTextMode();
119 }
120 
122  xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
123  std::set< OUString > const * /*existingDependencies*/)
124 {
125  if (valueParser_.startElement(reader, nsId, name)) {
126  return true;
127  }
128  if (state_ == STATE_START) {
129  if (nsId == ParseManager::NAMESPACE_OOR &&
130  name == "component-schema")
131  {
132  handleComponentSchema(reader);
134  ignoring_ = 0;
135  return true;
136  }
137  } else {
138  //TODO: ignoring component-schema import, component-schema uses, and
139  // prop constraints; accepting all four at illegal places (and with
140  // illegal content):
141  if (ignoring_ > 0 ||
143  (name == "info" || name == "import" ||
144  name == "uses" || name == "constraints")))
145  {
146  assert(ignoring_ < LONG_MAX);
147  ++ignoring_;
148  return true;
149  }
150  switch (state_) {
153  name == "templates")
154  {
156  return true;
157  }
158  [[fallthrough]];
161  name == "component")
162  {
164  assert(elements_.empty());
165  elements_.push(
166  Element(
167  new GroupNode(valueParser_.getLayer(), false, ""),
168  componentName_));
169  return true;
170  }
171  break;
172  case STATE_TEMPLATES:
173  if (elements_.empty()) {
175  name == "group")
176  {
177  handleGroup(reader, true);
178  return true;
179  }
181  name == "set")
182  {
183  handleSet(reader, true);
184  return true;
185  }
186  break;
187  }
188  [[fallthrough]];
189  case STATE_COMPONENT:
190  assert(!elements_.empty());
191  switch (elements_.top().node->kind()) {
192  case Node::KIND_PROPERTY:
195  name == "value")
196  {
197  handlePropValue(reader, elements_.top().node);
198  return true;
199  }
200  break;
201  case Node::KIND_GROUP:
203  name == "prop")
204  {
205  handleProp(reader);
206  return true;
207  }
209  name == "node-ref")
210  {
211  handleNodeRef(reader);
212  return true;
213  }
215  name == "group")
216  {
217  handleGroup(reader, false);
218  return true;
219  }
221  name == "set")
222  {
223  handleSet(reader, false);
224  return true;
225  }
226  break;
227  case Node::KIND_SET:
229  name == "item")
230  {
232  reader,
233  static_cast< SetNode * >(elements_.top().node.get()));
234  return true;
235  }
236  break;
237  default: // Node::KIND_LOCALIZED_VALUE
238  assert(false); // this cannot happen
239  break;
240  }
241  break;
243  break;
244  default: // STATE_START
245  assert(false); // this cannot happen
246  break;
247  }
248  }
249  throw css::uno::RuntimeException(
250  "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
251 }
252 
254  if (valueParser_.endElement()) {
255  return;
256  }
257  if (ignoring_ > 0) {
258  --ignoring_;
259  } else if (!elements_.empty()) {
260  Element top(std::move(elements_.top()));
261  elements_.pop();
262  if (top.node.is()) {
263  if (elements_.empty()) {
264  switch (state_) {
265  case STATE_TEMPLATES:
266  {
267  auto itPair = data_.templates.insert({top.name, top.node});
268  if (!itPair.second) {
269  merge(itPair.first->second, top.node);
270  }
271  }
272  break;
273  case STATE_COMPONENT:
274  {
275  NodeMap & components = data_.getComponents();
276  auto itPair = components.insert({top.name, top.node});
277  if (!itPair.second) {
278  merge(itPair.first->second, top.node);
279  }
281  }
282  break;
283  default:
284  assert(false);
285  throw css::uno::RuntimeException(
286  "this cannot happen");
287  }
288  } else {
289  if (!elements_.top().node->getMembers().insert(
290  NodeMap::value_type(top.name, top.node)).second)
291  {
292  throw css::uno::RuntimeException(
293  "duplicate " + top.name + " in " + reader.getUrl());
294  }
295  }
296  }
297  } else {
298  switch (state_) {
300  // To support old, broken extensions with .xcs files that contain
301  // empty <component-schema> elements:
303  break;
304  case STATE_TEMPLATES:
306  break;
308  throw css::uno::RuntimeException(
309  "no component element in " + reader.getUrl());
311  break;
312  default:
313  assert(false); // this cannot happen
314  }
315  }
316 }
317 
319  valueParser_.characters(text);
320 }
321 
323  //TODO: oor:version, xml:lang attributes
324  OStringBuffer buf(256);
325  buf.append('.');
326  bool hasPackage = false;
327  bool hasName = false;
328  for (;;) {
329  int attrNsId;
330  xmlreader::Span attrLn;
331  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
332  break;
333  }
334  if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
335  {
336  if (hasPackage) {
337  throw css::uno::RuntimeException(
338  "multiple component-schema package attributes in " +
339  reader.getUrl());
340  }
341  hasPackage = true;
342  xmlreader::Span s(reader.getAttributeValue(false));
343  buf.insert(0, s.begin, s.length);
344  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
345  attrLn == "name")
346  {
347  if (hasName) {
348  throw css::uno::RuntimeException(
349  "multiple component-schema name attributes in " +
350  reader.getUrl());
351  }
352  hasName = true;
353  xmlreader::Span s(reader.getAttributeValue(false));
354  buf.append(s.begin, s.length);
355  }
356  }
357  if (!hasPackage) {
358  throw css::uno::RuntimeException(
359  "no component-schema package attribute in " + reader.getUrl());
360  }
361  if (!hasName) {
362  throw css::uno::RuntimeException(
363  "no component-schema name attribute in " + reader.getUrl());
364  }
365  componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
366  convertFromUtf8();
367 }
368 
370  bool hasName = false;
371  OUString name;
372  OUString component(componentName_);
373  bool hasNodeType = false;
374  OUString nodeType;
375  for (;;) {
376  int attrNsId;
377  xmlreader::Span attrLn;
378  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
379  break;
380  }
381  if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
382  hasName = true;
383  name = reader.getAttributeValue(false).convertFromUtf8();
384  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
385  attrLn == "component")
386  {
387  component = reader.getAttributeValue(false).convertFromUtf8();
388  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
389  attrLn == "node-type")
390  {
391  hasNodeType = true;
392  nodeType = reader.getAttributeValue(false).convertFromUtf8();
393  }
394  }
395  if (!hasName) {
396  throw css::uno::RuntimeException(
397  "no node-ref name attribute in " + reader.getUrl());
398  }
403  component, hasNodeType, nodeType, nullptr)));
404  if (!tmpl.is()) {
405  //TODO: this can erroneously happen as long as import/uses attributes
406  // are not correctly processed
407  throw css::uno::RuntimeException(
408  "unknown node-ref " + name + " in " + reader.getUrl());
409  }
410  rtl::Reference< Node > node(tmpl->clone(false));
411  node->setLayer(valueParser_.getLayer());
412  elements_.push(Element(node, name));
413 }
414 
416  bool hasName = false;
417  OUString name;
419  bool localized = false;
420  bool nillable = true;
421  for (;;) {
422  int attrNsId;
423  xmlreader::Span attrLn;
424  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
425  break;
426  }
427  if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
428  hasName = true;
429  name = reader.getAttributeValue(false).convertFromUtf8();
430  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
431  attrLn == "type")
432  {
434  reader, reader.getAttributeValue(true));
435  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
436  attrLn == "localized")
437  {
438  localized = xmldata::parseBoolean(reader.getAttributeValue(true));
439  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
440  attrLn == "nillable")
441  {
442  nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
443  }
444  }
445  if (!hasName) {
446  throw css::uno::RuntimeException(
447  "no prop name attribute in " + reader.getUrl());
448  }
449  if (valueParser_.type_ == TYPE_ERROR) {
450  throw css::uno::RuntimeException(
451  "no prop type attribute in " + reader.getUrl());
452  }
453  elements_.push(
454  Element(
455  (localized
458  valueParser_.getLayer(), valueParser_.type_, nillable))
460  new PropertyNode(
462  css::uno::Any(), false))),
463  name));
464 }
465 
468 {
469  xmlreader::Span attrSeparator;
470  for (;;) {
471  int attrNsId;
472  xmlreader::Span attrLn;
473  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
474  break;
475  }
476  if (attrNsId == ParseManager::NAMESPACE_OOR &&
477  attrLn == "separator")
478  {
479  attrSeparator = reader.getAttributeValue(false);
480  if (attrSeparator.length == 0) {
481  throw css::uno::RuntimeException(
482  "bad oor:separator attribute in " + reader.getUrl());
483  }
484  }
485  }
486  valueParser_.separator_ = OString(
487  attrSeparator.begin, attrSeparator.length);
488  valueParser_.start(property);
489 }
490 
491 void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
492  bool hasName = false;
493  OUString name;
494  bool extensible = false;
495  for (;;) {
496  int attrNsId;
497  xmlreader::Span attrLn;
498  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
499  break;
500  }
501  if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
502  hasName = true;
503  name = reader.getAttributeValue(false).convertFromUtf8();
504  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
505  attrLn == "extensible")
506  {
507  extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
508  }
509  }
510  if (!hasName) {
511  throw css::uno::RuntimeException(
512  "no group name attribute in " + reader.getUrl());
513  }
514  if (isTemplate) {
516  }
517  elements_.push(
518  Element(
519  new GroupNode(
520  valueParser_.getLayer(), extensible,
521  isTemplate ? name : OUString()),
522  name));
523 }
524 
525 void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
526  bool hasName = false;
527  OUString name;
528  OUString component(componentName_);
529  bool hasNodeType = false;
530  OUString nodeType;
531  for (;;) {
532  int attrNsId;
533  xmlreader::Span attrLn;
534  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
535  break;
536  }
537  if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
538  hasName = true;
539  name = reader.getAttributeValue(false).convertFromUtf8();
540  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
541  attrLn == "component")
542  {
543  component = reader.getAttributeValue(false).convertFromUtf8();
544  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
545  attrLn == "node-type")
546  {
547  hasNodeType = true;
548  nodeType = reader.getAttributeValue(false).convertFromUtf8();
549  }
550  }
551  if (!hasName) {
552  throw css::uno::RuntimeException(
553  "no set name attribute in " + reader.getUrl());
554  }
555  if (isTemplate) {
557  }
558  elements_.push(
559  Element(
560  new SetNode(
563  component, hasNodeType, nodeType, nullptr),
564  isTemplate ? name : OUString()),
565  name));
566 }
567 
569  OUString component(componentName_);
570  bool hasNodeType = false;
571  OUString nodeType;
572  for (;;) {
573  int attrNsId;
574  xmlreader::Span attrLn;
575  if (!reader.nextAttribute(&attrNsId, &attrLn)) {
576  break;
577  }
578  if (attrNsId == ParseManager::NAMESPACE_OOR &&
579  attrLn == "component")
580  {
581  component = reader.getAttributeValue(false).convertFromUtf8();
582  } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
583  attrLn == "node-type")
584  {
585  hasNodeType = true;
586  nodeType = reader.getAttributeValue(false).convertFromUtf8();
587  }
588  }
589  set->getAdditionalTemplateNames().push_back(
590  xmldata::parseTemplateReference(component, hasNodeType, nodeType, nullptr));
592 }
593 
594 }
595 
596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString componentName_
Definition: xcsparser.hxx:92
xmlreader::XmlReader::Text getTextMode() const
rtl::Reference< Node > node
Definition: xcsparser.hxx:79
bool startElement(xmlreader::XmlReader &reader, int nsId, xmlreader::Span const &name)
OUString parseTemplateReference(OUString const &component, bool hasNodeType, OUString const &nodeType, OUString const *defaultTemplateName)
Definition: xmldata.cxx:117
NodeMap templates
Definition: data.hxx:49
virtual xmlreader::XmlReader::Text getTextMode() override
Definition: xcsparser.cxx:117
void handleComponentSchema(xmlreader::XmlReader &reader)
Definition: xcsparser.cxx:322
std::pair< iterator, bool > insert(const value_type &vt)
Definition: nodemap.hxx:53
PyObject_HEAD PyUNO_callable_Internals * members
char const * begin
void handleNodeRef(xmlreader::XmlReader &reader)
Definition: xcsparser.cxx:369
bool parseBoolean(xmlreader::Span const &text)
Definition: xmldata.cxx:105
void handleSetItem(xmlreader::XmlReader &reader, SetNode *set)
Definition: xcsparser.cxx:568
virtual bool startElement(xmlreader::XmlReader &reader, int nsId, xmlreader::Span const &name, std::set< OUString > const *existingDependencies) override
Definition: xcsparser.cxx:121
ElementStack elements_
Definition: xcsparser.hxx:95
static OUString fullTemplateName(OUString const &component, OUString const &name)
Definition: data.cxx:150
ValueParser valueParser_
Definition: xcsparser.hxx:90
NodeMapImpl::value_type value_type
Definition: nodemap.hxx:39
void characters(xmlreader::Span const &text)
void handlePropValue(xmlreader::XmlReader &reader, rtl::Reference< Node > const &property)
Definition: xcsparser.cxx:466
XcsParser(int layer, Data &data)
Definition: xcsparser.cxx:111
const long LONG_MAX
void handleProp(xmlreader::XmlReader &reader)
Definition: xcsparser.cxx:415
virtual void characters(xmlreader::Span const &text) override
Definition: xcsparser.cxx:318
std::map< const SwTextNode *, const sal_uInt32 > NodeMap
Type parseType(xmlreader::XmlReader const &reader, xmlreader::Span const &text)
Definition: xmldata.cxx:38
NodeMap & getComponents() const
Definition: data.cxx:291
void start(rtl::Reference< Node > const &property, OUString const &localizedName=OUString())
NodeMapImpl::iterator iterator
Definition: nodemap.hxx:37
sal_Int32 length
Span getAttributeValue(bool fullyNormalize)
rtl::OUString convertFromUtf8() const
const OUString & getUrl() const
virtual ~XcsParser() override
Definition: xcsparser.cxx:115
void handleSet(xmlreader::XmlReader &reader, bool isTemplate)
Definition: xcsparser.cxx:525
void handleGroup(xmlreader::XmlReader &reader, bool isTemplate)
Definition: xcsparser.cxx:491
rtl::Reference< Node > getTemplate(int layer, OUString const &fullName) const
Definition: data.cxx:285
bool nextAttribute(int *nsId, Span *localName)
OUString name
Definition: components.cxx:83
virtual void endElement(xmlreader::XmlReader const &reader) override
Definition: xcsparser.cxx:253
std::vector< OUString > & getAdditionalTemplateNames()
Definition: setnode.hxx:52