1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <DomBuilderContext.hxx>
23 #include <xmloff/namespacemap.hxx>
24 #include <xmloff/xmlimp.hxx>
25 #include <xmloff/xmlerror.hxx>
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/dom/NodeType.hpp>
34 #include <rtl/ustring.hxx>
35 #include <sal/log.hxx>
37 #include <comphelper/processfactory.hxx>
40 using com::sun::star::uno::XComponentContext
;
41 using com::sun::star::uno::Reference
;
42 using com::sun::star::uno::Sequence
;
43 using com::sun::star::uno::UNO_QUERY
;
44 using com::sun::star::uno::UNO_QUERY_THROW
;
45 using com::sun::star::xml::dom::DocumentBuilder
;
46 using com::sun::star::xml::dom::XDocument
;
47 using com::sun::star::xml::dom::XDocumentBuilder
;
48 using com::sun::star::xml::dom::XNode
;
49 using com::sun::star::xml::dom::XElement
;
50 using com::sun::star::xml::dom::NodeType_ELEMENT_NODE
;
53 // helper functions; implemented below
54 static Reference
<XNode
> lcl_createDomInstance();
55 static Reference
<XNode
> lcl_createElement( SvXMLImport
& rImport
,
57 const Reference
<XNode
>& xParent
);
58 static Reference
<XNode
> lcl_createElement(
59 const OUString
& rNamespace
, const OUString
& rName
,
60 const Reference
<XNode
>& xParent
);
62 DomBuilderContext::DomBuilderContext( SvXMLImport
& rImport
,
63 sal_Int32 nElement
) :
64 SvXMLImportContext( rImport
),
65 mxNode( lcl_createElement( rImport
, nElement
,
66 lcl_createDomInstance() ) )
68 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
69 SAL_WARN_IF( !Reference
<XElement
>( mxNode
, UNO_QUERY
).is(), "xmloff", "need element" );
70 SAL_WARN_IF( mxNode
->getNodeType() != NodeType_ELEMENT_NODE
, "xmloff", "need element" );
73 DomBuilderContext::DomBuilderContext( SvXMLImport
& rImport
,
74 const OUString
& rNamespace
, const OUString
& rName
) :
75 SvXMLImportContext( rImport
),
76 mxNode( lcl_createElement( rNamespace
, rName
,
77 lcl_createDomInstance() ) )
79 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
80 SAL_WARN_IF( !Reference
<XElement
>( mxNode
, UNO_QUERY
).is(), "xmloff", "need element" );
81 SAL_WARN_IF( mxNode
->getNodeType() != NodeType_ELEMENT_NODE
, "xmloff", "need element" );
84 DomBuilderContext::DomBuilderContext( SvXMLImport
& rImport
,
86 Reference
<XNode
> const & xParent
) :
87 SvXMLImportContext( rImport
),
88 mxNode( lcl_createElement( rImport
, nElement
, xParent
) )
90 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
91 SAL_WARN_IF( !Reference
<XElement
>( mxNode
, UNO_QUERY
).is(), "xmloff", "need element" );
92 SAL_WARN_IF( mxNode
->getNodeType() != NodeType_ELEMENT_NODE
, "xmloff", "need element" );
95 DomBuilderContext::DomBuilderContext( SvXMLImport
& rImport
,
96 const OUString
& rNamespace
, const OUString
& rName
,
97 Reference
<XNode
> const & xParent
) :
98 SvXMLImportContext( rImport
),
99 mxNode( lcl_createElement( rNamespace
, rName
, xParent
) )
101 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
102 SAL_WARN_IF( !Reference
<XElement
>( mxNode
, UNO_QUERY
).is(), "xmloff", "need element" );
103 SAL_WARN_IF( mxNode
->getNodeType() != NodeType_ELEMENT_NODE
, "xmloff", "need element" );
106 DomBuilderContext::~DomBuilderContext()
110 Reference
<XDocument
> DomBuilderContext::getTree()
112 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
113 return mxNode
->getOwnerDocument();
116 css::uno::Reference
< css::xml::sax::XFastContextHandler
> DomBuilderContext::createFastChildContext(
117 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
119 // create DomBuilder for subtree
120 return new DomBuilderContext( GetImport(), nElement
, mxNode
);
123 css::uno::Reference
< css::xml::sax::XFastContextHandler
> DomBuilderContext::createUnknownChildContext(
124 const OUString
& rNamespace
, const OUString
&rName
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
126 // create DomBuilder for subtree
127 return new DomBuilderContext( GetImport(), rNamespace
, rName
, mxNode
);
130 void SAL_CALL
DomBuilderContext::startFastElement(
131 sal_Int32
/*nElement*/,
132 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
134 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
135 SAL_WARN_IF( !mxNode
->getOwnerDocument().is(), "xmloff", "XNode must have XDocument" );
137 HandleAttributes(xAttrList
);
140 void SAL_CALL
DomBuilderContext::startUnknownElement(
141 const OUString
& /*rNamespace*/, const OUString
& /*rName*/,
142 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
144 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
145 SAL_WARN_IF( !mxNode
->getOwnerDocument().is(), "xmloff", "XNode must have XDocument" );
146 HandleAttributes(xAttrList
);
149 void DomBuilderContext::HandleAttributes(
150 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
152 // add attribute nodes to new node
153 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
155 sal_Int32 nAttrToken
= aIter
.getToken();
156 // get name & value for attribute
157 sal_uInt16 nNamespace
= (nAttrToken
>> NMSP_SHIFT
) - 1;
158 const OUString aPrefix
= SvXMLImport::getNamespacePrefixFromToken(nAttrToken
, &GetImport().GetNamespaceMap());
159 const OUString
& rLocalName
= SvXMLImport::getNameFromToken( nAttrToken
);
160 OUString aValue
= aIter
.toString();
162 // create attribute node and set value
163 Reference
<XElement
> xElement( mxNode
, UNO_QUERY_THROW
);
166 case XML_NAMESPACE_NONE
:
167 // no namespace: create a non-namespaced attribute
168 xElement
->setAttribute( rLocalName
, aValue
);
170 case XML_NAMESPACE_XMLNS
:
171 // namespace declaration: ignore, since the DOM tree handles these
172 // declarations implicitly
174 case XML_NAMESPACE_UNKNOWN
:
175 // unknown namespace: illegal input. Raise Warning.
177 GetImport().SetError(
178 XMLERROR_FLAG_WARNING
| XMLERROR_NAMESPACE_TROUBLE
, { rLocalName
, aValue
} );
183 // a real and proper namespace: create namespaced attribute
184 OUString namespaceURI
= SvXMLImport::getNamespaceURIFromToken(aIter
.getToken());
185 OUString qualifiedName
= aPrefix
.isEmpty() ? rLocalName
: aPrefix
+ SvXMLImport::aNamespaceSeparator
+ rLocalName
;
186 xElement
->setAttributeNS( namespaceURI
, qualifiedName
, aValue
);
191 const css::uno::Sequence
< css::xml::Attribute
> unknownAttribs
= xAttrList
->getUnknownAttributes();
192 for ( const auto& rUnknownAttrib
: unknownAttribs
)
194 // create attribute node and set value
195 Reference
<XElement
> xElement( mxNode
, UNO_QUERY_THROW
);
197 if (!rUnknownAttrib
.NamespaceURL
.isEmpty())
199 // unknown namespace: illegal input. Raise Warning.
200 GetImport().SetError(
201 XMLERROR_FLAG_WARNING
| XMLERROR_NAMESPACE_TROUBLE
, { rUnknownAttrib
.Name
, rUnknownAttrib
.Value
} );
205 // no namespace: create a non-namespaced attribute
206 xElement
->setAttribute( rUnknownAttrib
.Name
, rUnknownAttrib
.Value
);
211 void DomBuilderContext::characters( const OUString
& rCharacters
)
213 SAL_WARN_IF( !mxNode
.is(), "xmloff", "empty XNode not allowed" );
215 // TODO: I assume adjacent text nodes should be joined, to preserve
216 // processing model? (I.e., if the SAX parser breaks a string into 2
217 // Characters(..) calls, the DOM model would still see only one child.)
219 // create text node and append to parent
220 Reference
<XNode
> xNew(
221 mxNode
->getOwnerDocument()->createTextNode( rCharacters
),
223 mxNode
->appendChild( xNew
);
227 // helper function implementations
230 static Reference
<XNode
> lcl_createDomInstance()
232 const Reference
<XComponentContext
>& xContext
= comphelper::getProcessComponentContext();
233 SAL_WARN_IF( !xContext
.is(), "xmloff", "can't get service factory" );
235 Reference
<XDocumentBuilder
> xBuilder( DocumentBuilder::create(xContext
) );
237 return Reference
<XNode
>( xBuilder
->newDocument(), UNO_QUERY_THROW
);
240 static Reference
<XNode
> lcl_createElement( SvXMLImport
& rImport
,
242 const Reference
<XNode
>& xParent
)
244 SAL_WARN_IF( !xParent
.is(), "xmloff", "need parent node" );
246 Reference
<XDocument
> xDocument
= xParent
->getOwnerDocument();
247 SAL_WARN_IF( !xDocument
.is(), "xmloff", "no XDocument found!" );
249 // TODO: come up with proper way of handling namespaces; re-creating the
250 // namespace from the key is NOT a good idea, and will not work for
251 // multiple prefixes for the same namespace. Fortunately, those are rare.
253 Reference
<XElement
> xElement
;
254 sal_uInt16 nNamespace
= (nElement
>> NMSP_SHIFT
) - 1;
255 const OUString aPrefix
= SvXMLImport::getNamespacePrefixFromToken(nElement
, &rImport
.GetNamespaceMap());
256 const OUString
& rLocalName
= SvXMLImport::getNameFromToken( nElement
);
259 case XML_NAMESPACE_NONE
:
260 // no namespace: use local name
261 xElement
= xDocument
->createElement( rLocalName
);
263 case XML_NAMESPACE_XMLNS
:
264 case XML_NAMESPACE_UNKNOWN
:
265 // both cases are illegal; raise warning (and use only local name)
266 xElement
= xDocument
->createElement( rLocalName
);
268 Sequence
<OUString
> aSeq
{ rLocalName
};
270 XMLERROR_FLAG_WARNING
| XMLERROR_NAMESPACE_TROUBLE
, aSeq
);
274 // We are only given the prefix and the local name; thus we have to ask
275 // the namespace map to create a qualified name for us. Technically,
276 // this is a bug, since this will fail for multiple prefixes used for
277 // the same namespace.
278 OUString namespaceURI
= SvXMLImport::getNamespaceURIFromToken(nElement
);
279 OUString qualifiedName
= aPrefix
.isEmpty() ? rLocalName
: aPrefix
+ SvXMLImport::aNamespaceSeparator
+ rLocalName
;
280 xElement
= xDocument
->createElementNS(namespaceURI
, qualifiedName
);
283 SAL_WARN_IF( !xElement
.is(), "xmloff", "can't create element" );
285 // add new element to parent and return
286 xParent
->appendChild( xElement
);
290 static Reference
<XNode
> lcl_createElement(
291 const OUString
& rNamespace
, const OUString
& rName
,
292 const Reference
<XNode
>& xParent
)
294 SAL_WARN_IF( !xParent
.is(), "xmloff", "need parent node" );
296 Reference
<XDocument
> xDocument
= xParent
->getOwnerDocument();
297 SAL_WARN_IF( !xDocument
.is(), "xmloff", "no XDocument found!" );
299 // TODO: come up with proper way of handling namespaces; re-creating the
300 // namespace from the key is NOT a good idea, and will not work for
301 // multiple prefixes for the same namespace. Fortunately, those are rare.
303 Reference
<XElement
> xElement
;
304 if (rNamespace
.isEmpty())
306 // no namespace: use local name
307 xElement
= xDocument
->createElement( rName
);
311 xElement
= xDocument
->createElementNS(rNamespace
, rName
);
314 // add new element to parent and return
315 xParent
->appendChild( xElement
);
319 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */