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 .
20 #include "saxbuilder.hxx"
22 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
23 #include <com/sun/star/xml/sax/SAXException.hpp>
24 #include <cppuhelper/supportsservice.hxx>
25 #include <sax/fastattribs.hxx>
26 #include <xmloff/xmlimp.hxx>
28 using namespace css::lang
;
29 using namespace css::uno
;
30 using namespace css::xml::dom
;
31 using namespace css::xml::sax
;
35 CSAXDocumentBuilder::CSAXDocumentBuilder(const Reference
< XComponentContext
>& ctx
)
37 , m_aState( SAXDocumentBuilderState_READY
)
40 Sequence
< OUString
> SAL_CALL
CSAXDocumentBuilder::getSupportedServiceNames()
42 return { "com.sun.star.xml.dom.SAXDocumentBuilder" };
45 OUString SAL_CALL
CSAXDocumentBuilder::getImplementationName()
47 return "com.sun.star.comp.xml.dom.SAXDocumentBuilder";
50 sal_Bool SAL_CALL
CSAXDocumentBuilder::supportsService(const OUString
& aServiceName
)
52 return cppu::supportsService(this, aServiceName
);
55 SAXDocumentBuilderState SAL_CALL
CSAXDocumentBuilder::getState()
57 std::scoped_lock
g(m_Mutex
);
62 void SAL_CALL
CSAXDocumentBuilder::reset()
64 std::scoped_lock
g(m_Mutex
);
68 while (!m_aNodeStack
.empty()) m_aNodeStack
.pop();
69 m_aState
= SAXDocumentBuilderState_READY
;
72 Reference
< XDocument
> SAL_CALL
CSAXDocumentBuilder::getDocument()
74 std::scoped_lock
g(m_Mutex
);
76 if (m_aState
!= SAXDocumentBuilderState_DOCUMENT_FINISHED
)
77 throw RuntimeException();
82 Reference
< XDocumentFragment
> SAL_CALL
CSAXDocumentBuilder::getDocumentFragment()
84 std::scoped_lock
g(m_Mutex
);
86 if (m_aState
!= SAXDocumentBuilderState_FRAGMENT_FINISHED
)
87 throw RuntimeException();
91 void SAL_CALL
CSAXDocumentBuilder::startDocumentFragment(const Reference
< XDocument
>& ownerDoc
)
93 std::scoped_lock
g(m_Mutex
);
95 // start a new document fragment and push it onto the stack
96 // we have to be in a clean state to do this
97 if (m_aState
!= SAXDocumentBuilderState_READY
)
98 throw RuntimeException();
100 m_aDocument
= ownerDoc
;
101 Reference
< XDocumentFragment
> aFragment
= m_aDocument
->createDocumentFragment();
102 m_aNodeStack
.push(aFragment
);
103 m_aFragment
= aFragment
;
104 m_aState
= SAXDocumentBuilderState_BUILDING_FRAGMENT
;
107 void SAL_CALL
CSAXDocumentBuilder::endDocumentFragment()
109 std::scoped_lock
g(m_Mutex
);
111 // there should only be the document left on the node stack
112 if (m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
113 throw RuntimeException();
115 Reference
< XNode
> aNode
= m_aNodeStack
.top();
116 if ( aNode
->getNodeType() != NodeType_DOCUMENT_FRAGMENT_NODE
)
117 throw RuntimeException();
119 m_aState
= SAXDocumentBuilderState_FRAGMENT_FINISHED
;
122 //XFastDocumentHandler
123 void SAL_CALL
CSAXDocumentBuilder::startDocument()
125 std::scoped_lock
g(m_Mutex
);
127 // start a new document and push it onto the stack
128 // we have to be in a clean state to do this
129 if (m_aState
!= SAXDocumentBuilderState_READY
)
130 throw SAXException();
132 Reference
< XDocumentBuilder
> aBuilder(DocumentBuilder::create(m_xContext
));
133 Reference
< XDocument
> aDocument
= aBuilder
->newDocument();
134 m_aNodeStack
.push(aDocument
);
135 m_aDocument
= aDocument
;
136 m_aState
= SAXDocumentBuilderState_BUILDING_DOCUMENT
;
139 void SAL_CALL
CSAXDocumentBuilder::endDocument()
141 std::scoped_lock
g(m_Mutex
);
143 // there should only be the document left on the node stack
144 if (m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
)
145 throw SAXException();
147 Reference
< XNode
> aNode
= m_aNodeStack
.top();
148 if ( aNode
->getNodeType() != NodeType_DOCUMENT_NODE
)
149 throw SAXException();
151 m_aState
= SAXDocumentBuilderState_DOCUMENT_FINISHED
;
154 void SAL_CALL
CSAXDocumentBuilder::processingInstruction( const OUString
& rTarget
, const OUString
& rData
)
156 std::scoped_lock
g(m_Mutex
);
158 // append PI node to the current top
159 if ( m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
&&
160 m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
161 throw SAXException();
163 Reference
< XProcessingInstruction
> aInstruction
= m_aDocument
->createProcessingInstruction(
165 m_aNodeStack
.top()->appendChild(aInstruction
);
168 void SAL_CALL
CSAXDocumentBuilder::setDocumentLocator( const Reference
< XLocator
>& )
172 void SAL_CALL
CSAXDocumentBuilder::startFastElement( sal_Int32 nElement
, const Reference
< XFastAttributeList
>& xAttribs
)
174 std::scoped_lock
g(m_Mutex
);
176 if ( m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
&&
177 m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
179 throw SAXException();
182 Reference
< XElement
> aElement
;
183 const OUString
& aPrefix(SvXMLImport::getNamespacePrefixFromToken(nElement
, nullptr));
184 const OUString
& aURI( SvXMLImport::getNamespaceURIFromToken( nElement
) );
185 OUString
aQualifiedName( SvXMLImport::getNameFromToken( nElement
) );
186 if( !aPrefix
.isEmpty() )
187 aQualifiedName
= aPrefix
+ SvXMLImport::aNamespaceSeparator
+ aQualifiedName
;
189 if ( !aURI
.isEmpty() )
191 // found a URI for prefix
193 aElement
= m_aDocument
->createElementNS( aURI
, aQualifiedName
);
198 aElement
= m_aDocument
->createElement( aQualifiedName
);
200 aElement
.set( m_aNodeStack
.top()->appendChild(aElement
), UNO_QUERY
);
201 m_aNodeStack
.push(aElement
);
204 setElementFastAttributes(aElement
, xAttribs
);
207 // For arbitrary meta elements
208 void SAL_CALL
CSAXDocumentBuilder::startUnknownElement( const OUString
& rNamespace
, const OUString
& rName
, const Reference
< XFastAttributeList
>& xAttribs
)
210 std::scoped_lock
g(m_Mutex
);
212 if ( m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
&&
213 m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
215 throw SAXException();
218 Reference
< XElement
> aElement
;
219 if ( !rNamespace
.isEmpty() )
220 aElement
= m_aDocument
->createElementNS( rNamespace
, rName
);
222 aElement
= m_aDocument
->createElement( rName
);
224 aElement
.set( m_aNodeStack
.top()->appendChild(aElement
), UNO_QUERY
);
225 m_aNodeStack
.push(aElement
);
230 setElementFastAttributes(aElement
, xAttribs
);
231 const Sequence
< css::xml::Attribute
> unknownAttribs
= xAttribs
->getUnknownAttributes();
232 for ( const auto& rUnknownAttrib
: unknownAttribs
)
234 const OUString
& rAttrValue
= rUnknownAttrib
.Value
;
235 const OUString
& rAttrName
= rUnknownAttrib
.Name
;
236 const OUString
& rAttrNamespace
= rUnknownAttrib
.NamespaceURL
;
237 if ( !rAttrNamespace
.isEmpty() )
238 aElement
->setAttributeNS( rAttrNamespace
, rAttrName
, rAttrValue
);
240 aElement
->setAttribute( rAttrName
, rAttrValue
);
244 void CSAXDocumentBuilder::setElementFastAttributes(const Reference
< XElement
>& aElement
, const Reference
< XFastAttributeList
>& xAttribs
)
246 for (auto &it
: sax_fastparser::castToFastAttributeList( xAttribs
))
248 sal_Int32 nAttrToken
= it
.getToken();
249 const OUString
& aAttrPrefix(SvXMLImport::getNamespacePrefixFromToken(nAttrToken
, nullptr));
250 const OUString
& aAttrURI( SvXMLImport::getNamespaceURIFromToken( nAttrToken
) );
251 OUString
aAttrQualifiedName( SvXMLImport::getNameFromToken( nAttrToken
) );
252 if( !aAttrPrefix
.isEmpty() )
253 aAttrQualifiedName
= aAttrPrefix
+ SvXMLImport::aNamespaceSeparator
+ aAttrQualifiedName
;
255 if ( !aAttrURI
.isEmpty() )
256 aElement
->setAttributeNS( aAttrURI
, aAttrQualifiedName
, it
.toString() );
258 aElement
->setAttribute( aAttrQualifiedName
, it
.toString() );
262 void SAL_CALL
CSAXDocumentBuilder::endFastElement( sal_Int32 nElement
)
264 std::scoped_lock
g(m_Mutex
);
266 // pop the current element from the stack
267 if ( m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
&&
268 m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
269 throw SAXException();
271 Reference
< XNode
> aNode(m_aNodeStack
.top());
272 if (aNode
->getNodeType() != NodeType_ELEMENT_NODE
)
273 throw SAXException();
275 Reference
< XElement
> aElement(aNode
, UNO_QUERY
);
276 if( aElement
->getPrefix() != SvXMLImport::getNamespacePrefixFromToken(nElement
, nullptr) ||
277 aElement
->getTagName() != SvXMLImport::getNameFromToken( nElement
) ) // consistency check
278 throw SAXException();
285 void SAL_CALL
CSAXDocumentBuilder::endUnknownElement( const OUString
& /*rNamespace*/, const OUString
& rName
)
287 std::scoped_lock
g(m_Mutex
);
289 // pop the current element from the stack
290 if ( m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
&&
291 m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
292 throw SAXException();
294 Reference
< XNode
> aNode(m_aNodeStack
.top());
295 if (aNode
->getNodeType() != NodeType_ELEMENT_NODE
)
296 throw SAXException();
298 Reference
< XElement
> aElement(aNode
, UNO_QUERY
);
300 const OUString
& aPrefix
= aElement
->getPrefix();
301 if (!aPrefix
.isEmpty())
302 aRefName
= aPrefix
+ SvXMLImport::aNamespaceSeparator
+ aElement
->getTagName();
304 aRefName
= aElement
->getTagName();
305 if (aRefName
!= rName
) // consistency check
306 throw SAXException();
312 Reference
< XFastContextHandler
> SAL_CALL
CSAXDocumentBuilder::createFastChildContext( sal_Int32
/* nElement */, const Reference
< XFastAttributeList
>&/* xAttribs */ )
318 Reference
< XFastContextHandler
> SAL_CALL
CSAXDocumentBuilder::createUnknownChildContext( const OUString
&/* rNamespace */, const OUString
&/* rName */, const Reference
< XFastAttributeList
>&/* xAttribs */ )
323 void SAL_CALL
CSAXDocumentBuilder::characters( const OUString
& rChars
)
325 std::scoped_lock
g(m_Mutex
);
327 // append text node to the current top element
328 if (m_aState
!= SAXDocumentBuilderState_BUILDING_DOCUMENT
&&
329 m_aState
!= SAXDocumentBuilderState_BUILDING_FRAGMENT
)
330 throw SAXException();
332 Reference
< XText
> aText
= m_aDocument
->createTextNode(rChars
);
333 m_aNodeStack
.top()->appendChild(aText
);
337 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
338 unoxml_CSAXDocumentBuilder_get_implementation(
339 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
341 return cppu::acquire(new DOM::CSAXDocumentBuilder(context
));
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */