1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/types.h>
22 #include <cppunit/extensions/HelperMacros.h>
23 #include <cppunit/plugin/TestPlugIn.h>
24 #include <test/bootstrapfixture.hxx>
25 #include <cppuhelper/implbase.hxx>
26 #include <com/sun/star/beans/Pair.hpp>
27 #include <com/sun/star/xml/sax/SAXException.hpp>
28 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
29 #include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
30 #include <comphelper/processfactory.hxx>
31 #include <com/sun/star/xml/sax/Parser.hpp>
32 #include <com/sun/star/xml/sax/XParser.hpp>
33 #include <com/sun/star/xml/sax/XLocator.hpp>
34 #include <com/sun/star/xml/sax/FastToken.hpp>
35 #include <com/sun/star/lang/XInitialization.hpp>
36 #include <osl/file.hxx>
37 #include <unotools/ucbstreamhelper.hxx>
38 #include <unotools/streamwrap.hxx>
39 #include <sax/fastattribs.hxx>
41 #include <string_view>
43 #include <rtl/ref.hxx>
51 using namespace xml::sax
;
52 using namespace ::osl
;
53 using namespace sax_fastparser
;
55 Reference
< XInputStream
> createStreamFromFile (
56 const OUString
& filePath
)
58 Reference
< XInputStream
> xInputStream
;
60 FileBase::getFileURLFromSystemPath(filePath
, aInStr
);
61 std::unique_ptr
<SvStream
> pStream
= utl::UcbStreamHelper::CreateStream(aInStr
, StreamMode::READ
);
62 if(pStream
== nullptr)
63 CPPUNIT_ASSERT(false);
64 Reference
< XStream
> xStream(new utl::OStreamWrapper(std::move(pStream
)));
65 xInputStream
.set(xStream
, UNO_QUERY
);
69 class TestDocumentHandler
: public cppu::WeakImplHelper
< XDocumentHandler
>
73 std::deque
< std::pair
<OUString
,OUString
> > m_aNamespaceStack
;
74 std::stack
<sal_uInt16
> m_aCountStack
;
76 OUString
canonicalform(const OUString
&sName
, const OUString
&sValue
, bool isElement
);
77 OUString
getNamespace(std::u16string_view sName
);
80 TestDocumentHandler() {}
81 const OUString
& getString() const { return m_aStr
; }
84 virtual void SAL_CALL
startDocument() override
;
85 virtual void SAL_CALL
endDocument() override
;
86 virtual void SAL_CALL
startElement( const OUString
& aName
, const Reference
< XAttributeList
>& xAttribs
) override
;
87 virtual void SAL_CALL
endElement( const OUString
& aName
) override
;
88 virtual void SAL_CALL
characters( const OUString
& aChars
) override
;
89 virtual void SAL_CALL
ignorableWhitespace( const OUString
& aWhitespaces
) override
;
90 virtual void SAL_CALL
processingInstruction( const OUString
& aTarget
, const OUString
& aData
) override
;
91 virtual void SAL_CALL
setDocumentLocator( const Reference
< XLocator
>& xLocator
) override
;
94 OUString
TestDocumentHandler::canonicalform(const OUString
&sName
, const OUString
&sValue
, bool isElement
)
96 sal_Int16 nIndex
= sName
.indexOf(":");
97 if ( !isElement
&& sName
.match( "xmlns" ) )
99 m_aCountStack
.top() += 1;
101 m_aNamespaceStack
.emplace_back( u
"default"_ustr
, sValue
);
103 m_aNamespaceStack
.emplace_back( sName
.copy( nIndex
+ 1 ), sValue
);
109 OUString sNamespace
= getNamespace( sName
.subView( 0, nIndex
) );
110 return sNamespace
+ sName
.subView(nIndex
);
114 OUString sDefaultns
= getNamespace( u
"default" );
115 if ( !isElement
|| sDefaultns
.isEmpty() )
118 return sDefaultns
+ ":" + sName
;
124 OUString
TestDocumentHandler::getNamespace(std::u16string_view sName
)
126 for (sal_Int16 i
= m_aNamespaceStack
.size() - 1; i
>=0; i
--)
128 std::pair
<OUString
, OUString
> aPair
= m_aNamespaceStack
.at(i
);
129 if (aPair
.first
== sName
)
135 void SAL_CALL
TestDocumentHandler::startDocument()
138 m_aNamespaceStack
.clear();
139 m_aNamespaceStack
.emplace_back( std::make_pair( u
"default"_ustr
, OUString() ) );
140 m_aCountStack
= std::stack
<sal_uInt16
>();
141 m_aCountStack
.emplace(0);
145 void SAL_CALL
TestDocumentHandler::endDocument()
149 void SAL_CALL
TestDocumentHandler::startElement( const OUString
& aName
, const Reference
< XAttributeList
>& xAttribs
)
151 OUString sAttributes
;
152 m_aCountStack
.push(0);
153 sal_uInt16 len
= xAttribs
->getLength();
154 for (sal_uInt16 i
=0; i
<len
; i
++)
156 OUString sAttrValue
= xAttribs
->getValueByIndex(i
);
157 OUString sAttrName
= canonicalform(xAttribs
->getNameByIndex(i
), sAttrValue
, false);
158 if (!sAttrName
.isEmpty())
159 sAttributes
+= sAttrName
+ sAttrValue
;
161 m_aStr
+= canonicalform(aName
, u
""_ustr
, true) + sAttributes
;
165 void SAL_CALL
TestDocumentHandler::endElement( const OUString
& aName
)
167 m_aStr
+= canonicalform(aName
, u
""_ustr
, true);
168 sal_uInt16 nPopQty
= m_aCountStack
.top();
169 for (sal_uInt16 i
=0; i
<nPopQty
; i
++)
170 m_aNamespaceStack
.pop_back();
175 void SAL_CALL
TestDocumentHandler::characters( const OUString
& aChars
)
181 void SAL_CALL
TestDocumentHandler::ignorableWhitespace( const OUString
& aWhitespaces
)
183 m_aStr
+= aWhitespaces
;
187 void SAL_CALL
TestDocumentHandler::processingInstruction( const OUString
& aTarget
, const OUString
& aData
)
189 m_aStr
+= aTarget
+ aData
;
193 void SAL_CALL
TestDocumentHandler::setDocumentLocator( const Reference
< XLocator
>& /*xLocator*/ )
197 class NSDocumentHandler
: public cppu::WeakImplHelper
< XDocumentHandler
>
200 NSDocumentHandler() {}
203 virtual void SAL_CALL
startDocument() override
{}
204 virtual void SAL_CALL
endDocument() override
{}
205 virtual void SAL_CALL
startElement( const OUString
& aName
, const Reference
< XAttributeList
>& xAttribs
) override
;
206 virtual void SAL_CALL
endElement( const OUString
& /* aName */ ) override
{}
207 virtual void SAL_CALL
characters( const OUString
& /* aChars */ ) override
{}
208 virtual void SAL_CALL
ignorableWhitespace( const OUString
& /* aWhitespaces */ ) override
{}
209 virtual void SAL_CALL
processingInstruction( const OUString
& /* aTarget */, const OUString
& /* aData */ ) override
{}
210 virtual void SAL_CALL
setDocumentLocator( const Reference
< XLocator
>& /* xLocator */ ) override
{}
213 OUString
getNamespaceValue( std::u16string_view rNamespacePrefix
)
215 OUString aNamespaceURI
;
216 if (rNamespacePrefix
== u
"office")
217 aNamespaceURI
= "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
218 else if (rNamespacePrefix
== u
"text")
219 aNamespaceURI
= "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
220 else if (rNamespacePrefix
== u
"note")
221 aNamespaceURI
= "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
222 return aNamespaceURI
;
225 OUString
resolveNamespace( const OUString
& aName
)
228 if (( index
= aName
.indexOf( ':' )) > 0 )
230 if ( aName
.getLength() > index
+ 1 )
232 OUString aAttributeName
= getNamespaceValue( aName
.subView( 0, index
) ) +
233 ":" + aName
.subView( index
+ 1 );
234 return aAttributeName
;
240 void SAL_CALL
NSDocumentHandler::startElement( const OUString
& aName
, const Reference
< XAttributeList
>&/* xAttribs */ )
242 if (! (aName
== "office:document" || aName
== "office:body" || aName
== "office:text" ||
243 aName
== "text:p" || aName
== "note:p") )
244 CPPUNIT_ASSERT(false);
246 OUString sResolvedName
= resolveNamespace(aName
);
247 if (! ( sResolvedName
== "urn:oasis:names:tc:opendocument:xmlns:office:1.0:document" ||
248 sResolvedName
== "urn:oasis:names:tc:opendocument:xmlns:office:1.0:body" ||
249 sResolvedName
== "urn:oasis:names:tc:opendocument:xmlns:office:1.0:text" ||
250 sResolvedName
== "urn:oasis:names:tc:opendocument:xmlns:text:1.0:p") )
251 CPPUNIT_ASSERT(false);
254 class DummyTokenHandler
: public sax_fastparser::FastTokenHandlerBase
257 const static std::string_view tokens
[];
258 const static std::u16string_view namespaceURIs
[];
259 const static std::string_view namespacePrefixes
[];
262 virtual Sequence
< sal_Int8
> SAL_CALL
getUTF8Identifier( sal_Int32 nToken
) override
;
263 virtual sal_Int32 SAL_CALL
getTokenFromUTF8( const css::uno::Sequence
< sal_Int8
>& Identifier
) override
;
264 //FastTokenHandlerBase
265 virtual sal_Int32
getTokenDirect(std::string_view sToken
) const override
;
268 const std::string_view
DummyTokenHandler::tokens
[] = {
269 "Signature", "CanonicalizationMethod",
271 "DigestMethod", "Reference",
272 "document", "spacing",
273 "Player", "Height" };
275 const std::u16string_view
DummyTokenHandler::namespaceURIs
[] = {
276 u
"http://www.w3.org/2000/09/xmldsig#",
277 u
"http://schemas.openxmlformats.org/wordprocessingml/2006/main/",
278 u
"xyzsports.com/players/football/" };
280 const std::string_view
DummyTokenHandler::namespacePrefixes
[] = {
285 Sequence
< sal_Int8
> DummyTokenHandler::getUTF8Identifier( sal_Int32 nToken
)
287 std::string_view aUtf8Token
;
288 if ( ( nToken
& 0xffff0000 ) != 0 ) //namespace
290 sal_uInt32 nNamespaceToken
= ( nToken
>> 16 ) - 1;
291 if ( nNamespaceToken
< std::size(namespacePrefixes
) )
292 aUtf8Token
= namespacePrefixes
[ nNamespaceToken
];
294 else //element or attribute
296 size_t nElementToken
= nToken
& 0xffff;
297 if ( nElementToken
< std::size(tokens
) )
298 aUtf8Token
= tokens
[ nElementToken
];
300 Sequence
< sal_Int8
> aSeq( reinterpret_cast< const sal_Int8
* >(
301 aUtf8Token
.data() ), aUtf8Token
.size() );
305 sal_Int32
DummyTokenHandler::getTokenFromUTF8( const uno::Sequence
< sal_Int8
>& rIdentifier
)
307 return getTokenDirect(std::string_view(
308 reinterpret_cast<const char*>(rIdentifier
.getConstArray()), rIdentifier
.getLength()));
311 sal_Int32
DummyTokenHandler::getTokenDirect(std::string_view sToken
) const
313 for( size_t i
= 0; i
< std::size(tokens
); i
++ )
315 if ( tokens
[i
] == sToken
)
316 return static_cast<sal_Int32
>(i
);
318 return FastToken::DONTKNOW
;
322 class XMLImportTest
: public test::BootstrapFixture
326 rtl::Reference
< TestDocumentHandler
> m_xDocumentHandler
;
327 Reference
< XParser
> m_xParser
;
328 Reference
< XParser
> m_xLegacyFastParser
;
331 virtual void setUp() override
;
333 XMLImportTest() : BootstrapFixture(true, false) {}
335 void testMissingNamespaceDeclaration();
336 void testIllegalNamespaceUse();
338 CPPUNIT_TEST_SUITE( XMLImportTest
);
339 CPPUNIT_TEST( parse
);
340 CPPUNIT_TEST( testMissingNamespaceDeclaration
);
341 CPPUNIT_TEST( testIllegalNamespaceUse
);
342 CPPUNIT_TEST_SUITE_END();
345 void XMLImportTest::setUp()
347 test::BootstrapFixture::setUp();
348 Reference
< XComponentContext
> xContext
= comphelper::getProcessComponentContext();
349 m_xDocumentHandler
.set( new TestDocumentHandler() );
350 m_xParser
= Parser::create( xContext
);
351 m_xParser
->setDocumentHandler( m_xDocumentHandler
);
352 m_xLegacyFastParser
.set( xContext
->getServiceManager()->createInstanceWithContext
353 ( u
"com.sun.star.xml.sax.LegacyFastParser"_ustr
, xContext
), UNO_QUERY
);
354 m_xLegacyFastParser
->setDocumentHandler( m_xDocumentHandler
);
356 Reference
< XFastTokenHandler
> xTokenHandler
;
357 xTokenHandler
.set( new DummyTokenHandler
);
358 uno::Reference
<lang::XInitialization
> const xInit(m_xLegacyFastParser
,
359 uno::UNO_QUERY_THROW
);
360 xInit
->initialize({ uno::Any(xTokenHandler
) });
362 sal_Int32 nNamespaceCount
= SAL_N_ELEMENTS(DummyTokenHandler::namespaceURIs
);
363 uno::Sequence
<uno::Any
> namespaceArgs( nNamespaceCount
+ 1 );
364 auto p_namespaceArgs
= namespaceArgs
.getArray();
365 p_namespaceArgs
[0] <<= u
"registerNamespaces"_ustr
;
366 for (sal_Int32 i
= 1; i
<= nNamespaceCount
; i
++ )
368 css::beans::Pair
<OUString
, sal_Int32
> rPair( OUString(DummyTokenHandler::namespaceURIs
[i
- 1]), i
<< 16 );
369 p_namespaceArgs
[i
] <<= rPair
;
371 xInit
->initialize( namespaceArgs
);
373 m_sDirPath
= m_directories
.getPathFromSrc( u
"/sax/qa/data/" );
376 void XMLImportTest::parse()
378 OUString fileNames
[] = {u
"simple.xml"_ustr
, u
"defaultns.xml"_ustr
, u
"inlinens.xml"_ustr
,
379 u
"multiplens.xml"_ustr
, u
"multiplepfx.xml"_ustr
,
380 u
"nstoattributes.xml"_ustr
, u
"nestedns.xml"_ustr
, u
"testthreading.xml"_ustr
};
382 for (size_t i
= 0; i
< std::size( fileNames
); i
++)
385 source
.sSystemId
= "internal";
387 source
.aInputStream
= createStreamFromFile( m_sDirPath
+ fileNames
[i
] );
388 m_xParser
->parseStream(source
);
389 const OUString rParserStr
= m_xDocumentHandler
->getString();
391 source
.aInputStream
= createStreamFromFile( m_sDirPath
+ fileNames
[i
] );
392 m_xLegacyFastParser
->parseStream(source
);
393 const OUString rLegacyFastParserStr
= m_xDocumentHandler
->getString();
395 CPPUNIT_ASSERT_EQUAL( rParserStr
, rLegacyFastParserStr
);
396 // OString o = OUStringToOString( Str, RTL_TEXTENCODING_ASCII_US );
397 // CPPUNIT_ASSERT_MESSAGE( string(o.pData->buffer), false );
401 void XMLImportTest::testMissingNamespaceDeclaration()
403 OUString fileNames
[] = { u
"manifestwithnsdecl.xml"_ustr
, u
"manifestwithoutnsdecl.xml"_ustr
};
405 uno::Reference
<lang::XInitialization
> const xInit(m_xLegacyFastParser
,
406 uno::UNO_QUERY_THROW
);
407 xInit
->initialize({ uno::Any(u
"IgnoreMissingNSDecl"_ustr
) });
409 for (sal_uInt16 i
= 0; i
< std::size( fileNames
); i
++)
414 source
.sSystemId
= "internal";
416 source
.aInputStream
= createStreamFromFile( m_sDirPath
+ fileNames
[i
] );
417 m_xParser
->parseStream(source
);
418 const OUString rParserStr
= m_xDocumentHandler
->getString();
420 source
.aInputStream
= createStreamFromFile( m_sDirPath
+ fileNames
[i
] );
421 m_xLegacyFastParser
->parseStream(source
);
422 const OUString rLegacyFastParserStr
= m_xDocumentHandler
->getString();
424 CPPUNIT_ASSERT_EQUAL( rParserStr
, rLegacyFastParserStr
);
426 catch( const SAXException
& )
432 void XMLImportTest::testIllegalNamespaceUse()
434 rtl::Reference
< NSDocumentHandler
> m_xNSDocumentHandler
;
435 m_xNSDocumentHandler
.set( new NSDocumentHandler() );
436 m_xParser
->setDocumentHandler( m_xNSDocumentHandler
);
438 source
.sSystemId
= "internal";
440 source
.aInputStream
= createStreamFromFile( m_sDirPath
+ "multiplepfx.xml" );
441 m_xParser
->parseStream(source
);
443 m_xLegacyFastParser
->setDocumentHandler( m_xNSDocumentHandler
);
444 source
.aInputStream
= createStreamFromFile( m_sDirPath
+ "multiplepfx.xml" );
445 m_xLegacyFastParser
->parseStream(source
);
448 CPPUNIT_TEST_SUITE_REGISTRATION( XMLImportTest
);
451 CPPUNIT_PLUGIN_IMPLEMENT();
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */