1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_xmloff.hxx"
26 
27 #include "DomBuilderContext.hxx"
28 
29 #include <xmloff/nmspmap.hxx>
30 #include <xmloff/xmlimp.hxx>
31 #include "xmloff/xmlerror.hxx"
32 
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/uno/Reference.hxx>
35 #include <com/sun/star/uno/Sequence.hxx>
36 #include <com/sun/star/xml/dom/XAttr.hpp>
37 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
38 #include <com/sun/star/xml/dom/XNode.hpp>
39 #include <com/sun/star/xml/dom/XElement.hpp>
40 #include <com/sun/star/xml/sax/XAttributeList.hpp>
41 #include <com/sun/star/xml/dom/NodeType.hpp>
42 
43 #include <rtl/ustring.hxx>
44 #include <tools/debug.hxx>
45 
46 #include <unotools/processfactory.hxx>
47 
48 
49 using com::sun::star::lang::XMultiServiceFactory;
50 using com::sun::star::uno::Reference;
51 using com::sun::star::uno::Sequence;
52 using com::sun::star::uno::UNO_QUERY;
53 using com::sun::star::uno::UNO_QUERY_THROW;
54 using com::sun::star::xml::dom::XAttr;
55 using com::sun::star::xml::dom::XDocument;
56 using com::sun::star::xml::dom::XDocumentBuilder;
57 using com::sun::star::xml::dom::XNode;
58 using com::sun::star::xml::dom::XElement;
59 using com::sun::star::xml::sax::XAttributeList;
60 using com::sun::star::xml::dom::NodeType_ELEMENT_NODE;
61 using rtl::OUString;
62 
63 
64 // helper functions; implemented below
65 Reference<XNode> lcl_createDomInstance();
66 Reference<XNode> lcl_createElement( SvXMLImport& rImport,
67                                     sal_uInt16 nPrefix,
68                                     const OUString rLocalName,
69                                     Reference<XNode> xParent);
70 
71 
DomBuilderContext(SvXMLImport & rImport,sal_uInt16 nPrefix,const OUString & rLocalName)72 DomBuilderContext::DomBuilderContext( SvXMLImport& rImport,
73                                       sal_uInt16 nPrefix,
74                                       const OUString& rLocalName ) :
75     SvXMLImportContext( rImport, nPrefix, rLocalName ),
76     mxNode( lcl_createElement( rImport, nPrefix, rLocalName,
77                                lcl_createDomInstance() ) )
78 {
79     DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
80     DBG_ASSERT( Reference<XElement>( mxNode, UNO_QUERY ).is(), "need element" );
81     DBG_ASSERT( mxNode->getNodeType() == NodeType_ELEMENT_NODE, "need element" );
82 }
83 
DomBuilderContext(SvXMLImport & rImport,sal_uInt16 nPrefix,const OUString & rLocalName,Reference<XNode> & xParent)84 DomBuilderContext::DomBuilderContext( SvXMLImport& rImport,
85                                       sal_uInt16 nPrefix,
86                                       const OUString& rLocalName,
87                                       Reference<XNode>& xParent ) :
88     SvXMLImportContext( rImport, nPrefix, rLocalName ),
89     mxNode( lcl_createElement( rImport, nPrefix, rLocalName, xParent ) )
90 {
91     DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
92     DBG_ASSERT( Reference<XElement>( mxNode, UNO_QUERY ).is(), "need element" );
93     DBG_ASSERT( mxNode->getNodeType() == NodeType_ELEMENT_NODE, "need element" );
94 }
95 
~DomBuilderContext()96 DomBuilderContext::~DomBuilderContext()
97 {
98 }
99 
getTree()100 Reference<XDocument> DomBuilderContext::getTree()
101 {
102     DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
103     return mxNode->getOwnerDocument();
104 }
105 
getNode()106 Reference<XNode> DomBuilderContext::getNode()
107 {
108     return mxNode;
109 }
110 
111 
CreateChildContext(sal_uInt16 nPrefix,const OUString & rLocalName,const Reference<XAttributeList> &)112 SvXMLImportContext* DomBuilderContext::CreateChildContext(
113     sal_uInt16 nPrefix,
114     const OUString& rLocalName,
115     const Reference<XAttributeList>& )
116 {
117     // create DomBuilder for subtree
118     return new DomBuilderContext( GetImport(), nPrefix, rLocalName, mxNode );
119 }
120 
121 
StartElement(const Reference<XAttributeList> & xAttrList)122 void DomBuilderContext::StartElement(
123     const Reference<XAttributeList>& xAttrList )
124 {
125     DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
126     DBG_ASSERT( mxNode->getOwnerDocument().is(), "XNode must have XDocument" );
127 
128     // add attribute nodes to new node
129     sal_Int16 nAttributeCount = xAttrList->getLength();
130     for( sal_Int16 i = 0; i < nAttributeCount; i++ )
131     {
132         // get name & value for attribute
133         const OUString& rName = xAttrList->getNameByIndex( i );
134         const OUString& rValue = xAttrList->getValueByIndex( i );
135 
136         // namespace handling: determine namespace & namespace keykey
137         OUString sNamespace;
138         sal_uInt16 nNamespaceKey =
139             GetImport().GetNamespaceMap()._GetKeyByAttrName(
140                 rName, NULL, NULL, &sNamespace );
141 
142         // create attribute node and set value
143         Reference<XElement> xElement( mxNode, UNO_QUERY_THROW );
144         switch( nNamespaceKey )
145         {
146         case XML_NAMESPACE_NONE:
147             // no namespace: create a non-namespaced attribute
148             xElement->setAttribute( rName, rValue );
149             break;
150         case XML_NAMESPACE_XMLNS:
151             // namespace declaration: ignore, since the DOM tree handles these
152             // declarations implicitly
153             break;
154         case XML_NAMESPACE_UNKNOWN:
155             // unknown namespace: illegal input. Raise Warning.
156             {
157                 Sequence<OUString> aSeq(2);
158                 aSeq[0] = rName;
159                 aSeq[1] = rValue;
160                 GetImport().SetError(
161                     XMLERROR_FLAG_WARNING | XMLERROR_NAMESPACE_TROUBLE, aSeq );
162             }
163             break;
164         default:
165             // a real and proper namespace: create namespaced attribute
166             xElement->setAttributeNS( sNamespace, rName, rValue );
167             break;
168         }
169     }
170 }
171 
EndElement()172 void DomBuilderContext::EndElement()
173 {
174     // nothing to be done!
175 }
176 
Characters(const OUString & rCharacters)177 void DomBuilderContext::Characters( const OUString& rCharacters )
178 {
179     DBG_ASSERT( mxNode.is(), "empty XNode not allowed" );
180 
181     // TODO: I assume adjacent text nodes should be joined, to preserve
182     // processinf model? (I.e., if the SAX parser breaks a string into 2
183     // Characters(..) calls, the DOM model would still see only one child.)
184 
185     // create text node and append to parent
186     Reference<XNode> xNew(
187         mxNode->getOwnerDocument()->createTextNode( rCharacters ),
188         UNO_QUERY_THROW );
189     mxNode->appendChild( xNew );
190 }
191 
192 
193 //
194 // helper function implementations
195 //
196 
197 const sal_Char sDocumentBuilder[] = "com.sun.star.xml.dom.DocumentBuilder";
198 
lcl_createDomInstance()199 Reference<XNode> lcl_createDomInstance()
200 {
201     Reference<XMultiServiceFactory> xFactory = utl::getProcessServiceFactory();
202     DBG_ASSERT( xFactory.is(), "can't get service factory" );
203 
204     Reference<XDocumentBuilder> xBuilder(
205         xFactory->createInstance(
206             OUString( RTL_CONSTASCII_USTRINGPARAM( sDocumentBuilder ) ) ),
207         UNO_QUERY_THROW );
208 
209     return Reference<XNode>( xBuilder->newDocument(), UNO_QUERY_THROW );
210 }
211 
lcl_createElement(SvXMLImport & rImport,sal_uInt16 nPrefix,const OUString rLocalName,Reference<XNode> xParent)212 Reference<XNode> lcl_createElement( SvXMLImport& rImport,
213                                     sal_uInt16 nPrefix,
214                                     const OUString rLocalName,
215                                     Reference<XNode> xParent)
216 {
217     DBG_ASSERT( xParent.is(), "need parent node" );
218 
219     Reference<XDocument> xDocument = xParent->getOwnerDocument();
220     DBG_ASSERT( xDocument.is(), "no XDocument found!" );
221 
222     // TODO: come up with proper way of handling namespaces; re-creating the
223     // namespace from the key is NOT a good idea, and will not work for
224     // multiple prefixes for the same namespace. Fortunately, those are rare.
225 
226     Reference<XElement> xElement;
227     switch( nPrefix )
228     {
229     case XML_NAMESPACE_NONE:
230         // no namespace: use local name
231         xElement = xDocument->createElement( rLocalName );
232         break;
233     case XML_NAMESPACE_XMLNS:
234     case XML_NAMESPACE_UNKNOWN:
235         // both cases are illegal; raise warning (and use only local name)
236         xElement = xDocument->createElement( rLocalName );
237         {
238             Sequence<OUString> aSeq(1);
239             aSeq[0] = rLocalName;
240             rImport.SetError(
241                 XMLERROR_FLAG_WARNING | XMLERROR_NAMESPACE_TROUBLE, aSeq );
242         }
243         break;
244     default:
245         // We are only given the prefix and the local name; thus we have to ask
246         // the namespace map to create a qualified name for us. Technically,
247         // this is a bug, since this will fail for multiple prefixes used for
248         // the same namespace.
249         xElement = xDocument->createElementNS(
250             rImport.GetNamespaceMap().GetNameByKey( nPrefix ),
251             rImport.GetNamespaceMap().GetQNameByKey( nPrefix, rLocalName ) );
252         break;
253     }
254     DBG_ASSERT( xElement.is(), "can't create element" );
255 
256     // add new element to parent and return
257     Reference<XNode> xNode( xElement, UNO_QUERY_THROW );
258     xParent->appendChild( xNode );
259     return xNode;
260 }
261