LibreOffice Module xmloff (master)  1
DomBuilderContext.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 
21 #include <DomBuilderContext.hxx>
22 
23 #include <xmloff/namespacemap.hxx>
24 #include <xmloff/xmlimp.hxx>
25 #include <xmloff/xmlerror.hxx>
26 
27 #include <com/sun/star/uno/Reference.hxx>
28 #include <com/sun/star/uno/Sequence.hxx>
29 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
30 #include <com/sun/star/xml/dom/XNode.hpp>
31 #include <com/sun/star/xml/dom/XElement.hpp>
32 #include <com/sun/star/xml/sax/XAttributeList.hpp>
33 #include <com/sun/star/xml/dom/NodeType.hpp>
34 
35 #include <rtl/ustring.hxx>
36 #include <sal/log.hxx>
37 
39 
40 
41 using com::sun::star::uno::XComponentContext;
44 using com::sun::star::uno::UNO_QUERY;
45 using com::sun::star::uno::UNO_QUERY_THROW;
46 using com::sun::star::xml::dom::DocumentBuilder;
47 using com::sun::star::xml::dom::XDocument;
48 using com::sun::star::xml::dom::XDocumentBuilder;
49 using com::sun::star::xml::dom::XNode;
50 using com::sun::star::xml::dom::XElement;
51 using com::sun::star::xml::sax::XAttributeList;
52 using com::sun::star::xml::dom::NodeType_ELEMENT_NODE;
53 
54 
55 // helper functions; implemented below
58  sal_uInt16 nPrefix,
59  const OUString& rLocalName,
60  const Reference<XNode>& xParent);
61 
62 
64  sal_uInt16 nPrefix,
65  const OUString& rLocalName ) :
66  SvXMLImportContext( rImport, nPrefix, rLocalName ),
67  mxNode( lcl_createElement( rImport, nPrefix, rLocalName,
69 {
70  SAL_WARN_IF( !mxNode.is(), "xmloff", "empty XNode not allowed" );
71  SAL_WARN_IF( !Reference<XElement>( mxNode, UNO_QUERY ).is(), "xmloff", "need element" );
72  SAL_WARN_IF( mxNode->getNodeType() != NodeType_ELEMENT_NODE, "xmloff", "need element" );
73 }
74 
76  sal_uInt16 nPrefix,
77  const OUString& rLocalName,
78  Reference<XNode> const & xParent ) :
79  SvXMLImportContext( rImport, nPrefix, rLocalName ),
80  mxNode( lcl_createElement( rImport, nPrefix, rLocalName, xParent ) )
81 {
82  SAL_WARN_IF( !mxNode.is(), "xmloff", "empty XNode not allowed" );
83  SAL_WARN_IF( !Reference<XElement>( mxNode, UNO_QUERY ).is(), "xmloff", "need element" );
84  SAL_WARN_IF( mxNode->getNodeType() != NodeType_ELEMENT_NODE, "xmloff", "need element" );
85 }
86 
88 {
89 }
90 
92 {
93  SAL_WARN_IF( !mxNode.is(), "xmloff", "empty XNode not allowed" );
94  return mxNode->getOwnerDocument();
95 }
96 
98  sal_uInt16 nPrefix,
99  const OUString& rLocalName,
101 {
102  // create DomBuilder for subtree
103  return new DomBuilderContext( GetImport(), nPrefix, rLocalName, mxNode );
104 }
105 
106 
108  const Reference<XAttributeList>& xAttrList )
109 {
110  SAL_WARN_IF( !mxNode.is(), "xmloff", "empty XNode not allowed" );
111  SAL_WARN_IF( !mxNode->getOwnerDocument().is(), "xmloff", "XNode must have XDocument" );
112 
113  // add attribute nodes to new node
114  sal_Int16 nAttributeCount = xAttrList->getLength();
115  for( sal_Int16 i = 0; i < nAttributeCount; i++ )
116  {
117  // get name & value for attribute
118  const OUString& rName = xAttrList->getNameByIndex( i );
119  const OUString& rValue = xAttrList->getValueByIndex( i );
120 
121  // namespace handling: determine namespace & namespace key
122  OUString sNamespace;
123  sal_uInt16 nNamespaceKey =
125  rName, nullptr, nullptr, &sNamespace);
126 
127  // create attribute node and set value
128  Reference<XElement> xElement( mxNode, UNO_QUERY_THROW );
129  switch( nNamespaceKey )
130  {
131  case XML_NAMESPACE_NONE:
132  // no namespace: create a non-namespaced attribute
133  xElement->setAttribute( rName, rValue );
134  break;
135  case XML_NAMESPACE_XMLNS:
136  // namespace declaration: ignore, since the DOM tree handles these
137  // declarations implicitly
138  break;
140  // unknown namespace: illegal input. Raise Warning.
141  {
143  aSeq[0] = rName;
144  aSeq[1] = rValue;
147  }
148  break;
149  default:
150  // a real and proper namespace: create namespaced attribute
151  xElement->setAttributeNS( sNamespace, rName, rValue );
152  break;
153  }
154  }
155 }
156 
158 {
159  // nothing to be done!
160 }
161 
162 void DomBuilderContext::Characters( const OUString& rCharacters )
163 {
164  SAL_WARN_IF( !mxNode.is(), "xmloff", "empty XNode not allowed" );
165 
166  // TODO: I assume adjacent text nodes should be joined, to preserve
167  // processing model? (I.e., if the SAX parser breaks a string into 2
168  // Characters(..) calls, the DOM model would still see only one child.)
169 
170  // create text node and append to parent
171  Reference<XNode> xNew(
172  mxNode->getOwnerDocument()->createTextNode( rCharacters ),
173  UNO_QUERY_THROW );
174  mxNode->appendChild( xNew );
175 }
176 
177 
178 // helper function implementations
179 
180 
182 {
184  SAL_WARN_IF( !xContext.is(), "xmloff", "can't get service factory" );
185 
186  Reference<XDocumentBuilder> xBuilder( DocumentBuilder::create(xContext) );
187 
188  return Reference<XNode>( xBuilder->newDocument(), UNO_QUERY_THROW );
189 }
190 
192  sal_uInt16 nPrefix,
193  const OUString& rLocalName,
194  const Reference<XNode>& xParent)
195 {
196  SAL_WARN_IF( !xParent.is(), "xmloff", "need parent node" );
197 
198  Reference<XDocument> xDocument = xParent->getOwnerDocument();
199  SAL_WARN_IF( !xDocument.is(), "xmloff", "no XDocument found!" );
200 
201  // TODO: come up with proper way of handling namespaces; re-creating the
202  // namespace from the key is NOT a good idea, and will not work for
203  // multiple prefixes for the same namespace. Fortunately, those are rare.
204 
205  Reference<XElement> xElement;
206  switch( nPrefix )
207  {
208  case XML_NAMESPACE_NONE:
209  // no namespace: use local name
210  xElement = xDocument->createElement( rLocalName );
211  break;
212  case XML_NAMESPACE_XMLNS:
214  // both cases are illegal; raise warning (and use only local name)
215  xElement = xDocument->createElement( rLocalName );
216  {
217  Sequence<OUString> aSeq { rLocalName };
218  rImport.SetError(
220  }
221  break;
222  default:
223  // We are only given the prefix and the local name; thus we have to ask
224  // the namespace map to create a qualified name for us. Technically,
225  // this is a bug, since this will fail for multiple prefixes used for
226  // the same namespace.
227  xElement = xDocument->createElementNS(
228  rImport.GetNamespaceMap().GetNameByKey( nPrefix ),
229  rImport.GetNamespaceMap().GetQNameByKey( nPrefix, rLocalName ) );
230  break;
231  }
232  SAL_WARN_IF( !xElement.is(), "xmloff", "can't create element" );
233 
234  // add new element to parent and return
235  xParent->appendChild( xElement );
236  return xElement;
237 }
238 
239 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SvXMLImport & GetImport()
Definition: xmlictxt.hxx:62
SvXMLNamespaceMap & GetNamespaceMap()
Definition: xmlimp.hxx:394
virtual void StartElement(const css::uno::Reference< css::xml::sax::XAttributeList > &xAttrList) override
StartElement is called after a context has been constructed and before an elements context is parsed...
sal_uInt16 GetKeyByAttrName(const OUString &rAttrName, OUString *pPrefix, OUString *pLocalName, OUString *pNamespace) const
const sal_uInt16 XML_NAMESPACE_UNKNOWN
virtual SvXMLImportContextRef CreateChildContext(sal_uInt16 nPrefix, const OUString &rLocalName, const css::uno::Reference< css::xml::sax::XAttributeList > &xAttrList) override
Create a children element context.
css::uno::Any const & rValue
Definition: ImageStyle.hxx:38
const sal_uInt16 XML_NAMESPACE_XMLNS
#define XMLERROR_FLAG_WARNING
Definition: xmlerror.hxx:35
int i
#define XMLERROR_NAMESPACE_TROUBLE
Definition: xmlerror.hxx:57
virtual void EndElement() override
EndElement is called before a context will be destructed, but after an elements context has been pars...
virtual void Characters(const OUString &rChars) override
This method is called for all characters that are contained in the current element.
const sal_uInt16 XML_NAMESPACE_NONE
OUString GetQNameByKey(sal_uInt16 nKey, const OUString &rLocalName, bool bCache=true) const
const OUString & GetNameByKey(sal_uInt16 nKey) const
This class deliberately does not support XWeak, to improve performance when loading large documents...
Definition: xmlictxt.hxx:44
virtual ~DomBuilderContext() override
#define SAL_WARN_IF(condition, area, stream)
Reference< XComponentContext > getProcessComponentContext()
void SetError(sal_Int32 nId, const css::uno::Sequence< OUString > &rMsgParams, const OUString &rExceptionMessage, const css::uno::Reference< css::xml::sax::XLocator > &rLocator)
Record an error condition that occurred during import.
Sequence< sal_Int8 > aSeq
DomBuilderContext(SvXMLImport &rImport, sal_uInt16 nPrefix, const OUString &rLocalName)
default constructor: create new DOM tree
css::uno::Reference< css::xml::dom::XDocument > getTree()
access the DOM tree
static Reference< XNode > lcl_createDomInstance()
css::uno::Reference< css::xml::dom::XNode > mxNode
static Reference< XNode > lcl_createElement(SvXMLImport &rImport, sal_uInt16 nPrefix, const OUString &rLocalName, const Reference< XNode > &xParent)