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 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 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 96 DomBuilderContext::~DomBuilderContext() 97 { 98 } 99 100 Reference<XDocument> DomBuilderContext::getTree() 101 { 102 DBG_ASSERT( mxNode.is(), "empty XNode not allowed" ); 103 return mxNode->getOwnerDocument(); 104 } 105 106 Reference<XNode> DomBuilderContext::getNode() 107 { 108 return mxNode; 109 } 110 111 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 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 172 void DomBuilderContext::EndElement() 173 { 174 // nothing to be done! 175 } 176 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 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 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