Line data Source code
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/nmspmap.hxx>
24 : #include <xmloff/xmlimp.hxx>
25 : #include "xmloff/xmlerror.hxx"
26 :
27 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
28 : #include <com/sun/star/uno/Reference.hxx>
29 : #include <com/sun/star/uno/Sequence.hxx>
30 : #include <com/sun/star/xml/dom/XAttr.hpp>
31 : #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
32 : #include <com/sun/star/xml/dom/XNode.hpp>
33 : #include <com/sun/star/xml/dom/XElement.hpp>
34 : #include <com/sun/star/xml/sax/XAttributeList.hpp>
35 : #include <com/sun/star/xml/dom/NodeType.hpp>
36 :
37 : #include <rtl/ustring.hxx>
38 : #include <tools/debug.hxx>
39 :
40 : #include <comphelper/processfactory.hxx>
41 :
42 :
43 : using com::sun::star::lang::XMultiServiceFactory;
44 : using com::sun::star::uno::XComponentContext;
45 : using com::sun::star::uno::Reference;
46 : using com::sun::star::uno::Sequence;
47 : using com::sun::star::uno::UNO_QUERY;
48 : using com::sun::star::uno::UNO_QUERY_THROW;
49 : using com::sun::star::xml::dom::DocumentBuilder;
50 : using com::sun::star::xml::dom::XAttr;
51 : using com::sun::star::xml::dom::XDocument;
52 : using com::sun::star::xml::dom::XDocumentBuilder;
53 : using com::sun::star::xml::dom::XNode;
54 : using com::sun::star::xml::dom::XElement;
55 : using com::sun::star::xml::sax::XAttributeList;
56 : using com::sun::star::xml::dom::NodeType_ELEMENT_NODE;
57 : using rtl::OUString;
58 :
59 :
60 : // helper functions; implemented below
61 : static Reference<XNode> lcl_createDomInstance();
62 : static Reference<XNode> lcl_createElement( SvXMLImport& rImport,
63 : sal_uInt16 nPrefix,
64 : const OUString rLocalName,
65 : Reference<XNode> xParent);
66 :
67 :
68 0 : DomBuilderContext::DomBuilderContext( SvXMLImport& rImport,
69 : sal_uInt16 nPrefix,
70 : const OUString& rLocalName ) :
71 : SvXMLImportContext( rImport, nPrefix, rLocalName ),
72 : mxNode( lcl_createElement( rImport, nPrefix, rLocalName,
73 0 : lcl_createDomInstance() ) )
74 : {
75 : DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
76 : DBG_ASSERT( Reference<XElement>( mxNode, UNO_QUERY ).is(), "need element" );
77 : DBG_ASSERT( mxNode->getNodeType() == NodeType_ELEMENT_NODE, "need element" );
78 0 : }
79 :
80 0 : DomBuilderContext::DomBuilderContext( SvXMLImport& rImport,
81 : sal_uInt16 nPrefix,
82 : const OUString& rLocalName,
83 : Reference<XNode>& xParent ) :
84 : SvXMLImportContext( rImport, nPrefix, rLocalName ),
85 0 : mxNode( lcl_createElement( rImport, nPrefix, rLocalName, xParent ) )
86 : {
87 : DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
88 : DBG_ASSERT( Reference<XElement>( mxNode, UNO_QUERY ).is(), "need element" );
89 : DBG_ASSERT( mxNode->getNodeType() == NodeType_ELEMENT_NODE, "need element" );
90 0 : }
91 :
92 0 : DomBuilderContext::~DomBuilderContext()
93 : {
94 0 : }
95 :
96 0 : Reference<XDocument> DomBuilderContext::getTree()
97 : {
98 : DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
99 0 : return mxNode->getOwnerDocument();
100 : }
101 :
102 0 : SvXMLImportContext* DomBuilderContext::CreateChildContext(
103 : sal_uInt16 nPrefix,
104 : const OUString& rLocalName,
105 : const Reference<XAttributeList>& )
106 : {
107 : // create DomBuilder for subtree
108 0 : return new DomBuilderContext( GetImport(), nPrefix, rLocalName, mxNode );
109 : }
110 :
111 :
112 0 : void DomBuilderContext::StartElement(
113 : const Reference<XAttributeList>& xAttrList )
114 : {
115 : DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
116 : DBG_ASSERT( mxNode->getOwnerDocument().is(), "XNode must have XDocument" );
117 :
118 : // add attribute nodes to new node
119 0 : sal_Int16 nAttributeCount = xAttrList->getLength();
120 0 : for( sal_Int16 i = 0; i < nAttributeCount; i++ )
121 : {
122 : // get name & value for attribute
123 0 : const OUString& rName = xAttrList->getNameByIndex( i );
124 0 : const OUString& rValue = xAttrList->getValueByIndex( i );
125 :
126 : // namespace handling: determine namespace & namespace keykey
127 0 : OUString sNamespace;
128 : sal_uInt16 nNamespaceKey =
129 0 : GetImport().GetNamespaceMap()._GetKeyByAttrName(
130 0 : rName, NULL, NULL, &sNamespace );
131 :
132 : // create attribute node and set value
133 0 : Reference<XElement> xElement( mxNode, UNO_QUERY_THROW );
134 0 : switch( nNamespaceKey )
135 : {
136 : case XML_NAMESPACE_NONE:
137 : // no namespace: create a non-namespaced attribute
138 0 : xElement->setAttribute( rName, rValue );
139 0 : break;
140 : case XML_NAMESPACE_XMLNS:
141 : // namespace declaration: ignore, since the DOM tree handles these
142 : // declarations implicitly
143 0 : break;
144 : case XML_NAMESPACE_UNKNOWN:
145 : // unknown namespace: illegal input. Raise Warning.
146 : {
147 0 : Sequence<OUString> aSeq(2);
148 0 : aSeq[0] = rName;
149 0 : aSeq[1] = rValue;
150 0 : GetImport().SetError(
151 0 : XMLERROR_FLAG_WARNING | XMLERROR_NAMESPACE_TROUBLE, aSeq );
152 : }
153 0 : break;
154 : default:
155 : // a real and proper namespace: create namespaced attribute
156 0 : xElement->setAttributeNS( sNamespace, rName, rValue );
157 0 : break;
158 : }
159 0 : }
160 0 : }
161 :
162 0 : void DomBuilderContext::EndElement()
163 : {
164 : // nothing to be done!
165 0 : }
166 :
167 0 : void DomBuilderContext::Characters( const OUString& rCharacters )
168 : {
169 : DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
170 :
171 : // TODO: I assume adjacent text nodes should be joined, to preserve
172 : // processinf model? (I.e., if the SAX parser breaks a string into 2
173 : // Characters(..) calls, the DOM model would still see only one child.)
174 :
175 : // create text node and append to parent
176 : Reference<XNode> xNew(
177 0 : mxNode->getOwnerDocument()->createTextNode( rCharacters ),
178 0 : UNO_QUERY_THROW );
179 0 : mxNode->appendChild( xNew );
180 0 : }
181 :
182 :
183 : //
184 : // helper function implementations
185 : //
186 :
187 0 : static Reference<XNode> lcl_createDomInstance()
188 : {
189 0 : Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
190 : DBG_ASSERT( xContext.is(), "can't get service factory" );
191 :
192 0 : Reference<XDocumentBuilder> xBuilder( DocumentBuilder::create(xContext) );
193 :
194 0 : return Reference<XNode>( xBuilder->newDocument(), UNO_QUERY_THROW );
195 : }
196 :
197 0 : static Reference<XNode> lcl_createElement( SvXMLImport& rImport,
198 : sal_uInt16 nPrefix,
199 : const OUString rLocalName,
200 : Reference<XNode> xParent)
201 : {
202 : DBG_ASSERT( xParent.is(), "need parent node" );
203 :
204 0 : Reference<XDocument> xDocument = xParent->getOwnerDocument();
205 : DBG_ASSERT( xDocument.is(), "no XDocument found!" );
206 :
207 : // TODO: come up with proper way of handling namespaces; re-creating the
208 : // namespace from the key is NOT a good idea, and will not work for
209 : // multiple prefixes for the same namespace. Fortunately, those are rare.
210 :
211 0 : Reference<XElement> xElement;
212 0 : switch( nPrefix )
213 : {
214 : case XML_NAMESPACE_NONE:
215 : // no namespace: use local name
216 0 : xElement = xDocument->createElement( rLocalName );
217 0 : break;
218 : case XML_NAMESPACE_XMLNS:
219 : case XML_NAMESPACE_UNKNOWN:
220 : // both cases are illegal; raise warning (and use only local name)
221 0 : xElement = xDocument->createElement( rLocalName );
222 : {
223 0 : Sequence<OUString> aSeq(1);
224 0 : aSeq[0] = rLocalName;
225 : rImport.SetError(
226 0 : XMLERROR_FLAG_WARNING | XMLERROR_NAMESPACE_TROUBLE, aSeq );
227 : }
228 0 : break;
229 : default:
230 : // We are only given the prefix and the local name; thus we have to ask
231 : // the namespace map to create a qualified name for us. Technically,
232 : // this is a bug, since this will fail for multiple prefixes used for
233 : // the same namespace.
234 0 : xElement = xDocument->createElementNS(
235 0 : rImport.GetNamespaceMap().GetNameByKey( nPrefix ),
236 0 : rImport.GetNamespaceMap().GetQNameByKey( nPrefix, rLocalName ) );
237 0 : break;
238 : }
239 : DBG_ASSERT( xElement.is(), "can't create element" );
240 :
241 : // add new element to parent and return
242 0 : Reference<XNode> xNode( xElement, UNO_QUERY_THROW );
243 0 : xParent->appendChild( xNode );
244 0 : return xNode;
245 : }
246 :
247 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|