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