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