diff options
Diffstat (limited to 'sax')
43 files changed, 12000 insertions, 0 deletions
diff --git a/sax/inc/sax/dllapi.h b/sax/inc/sax/dllapi.h new file mode 100644 index 000000000000..e9aca11fb0bd --- /dev/null +++ b/sax/inc/sax/dllapi.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef INCLUDED_SAX_DLLAPI_H +#define INCLUDED_SAX_DLLAPI_H + +#include "sal/types.h" + +#if defined SAX_DLLIMPLEMENTATION +#define SAX_DLLPUBLIC SAL_DLLPUBLIC_EXPORT +#else +#define SAX_DLLPUBLIC SAL_DLLPUBLIC_IMPORT +#endif + +#endif diff --git a/sax/inc/sax/fastattribs.hxx b/sax/inc/sax/fastattribs.hxx new file mode 100644 index 000000000000..9a968982d39e --- /dev/null +++ b/sax/inc/sax/fastattribs.hxx @@ -0,0 +1,91 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_FASTATTRIBS_HXX_ +#define _SAX_FASTATTRIBS_HXX_ + +#include <com/sun/star/xml/sax/XFastAttributeList.hpp> +#include <com/sun/star/xml/sax/XFastTokenHandler.hpp> +#include <com/sun/star/xml/Attribute.hpp> +#include <com/sun/star/xml/FastAttribute.hpp> + +#include <cppuhelper/implbase1.hxx> +#include "sax/dllapi.h" + +#include <map> +#include <vector> + +namespace sax_fastparser +{ + +struct UnknownAttribute +{ + ::rtl::OUString maNamespaceURL; + ::rtl::OString maName; + ::rtl::OString maValue; + + UnknownAttribute( const ::rtl::OUString& rNamespaceURL, const ::rtl::OString& rName, const ::rtl::OString& rValue ); + + UnknownAttribute( const ::rtl::OString& rName, const ::rtl::OString& rValue ); + + void FillAttribute( ::com::sun::star::xml::Attribute* pAttrib ) const; +}; + +typedef std::map< sal_Int32, ::rtl::OString > FastAttributeMap; +typedef std::vector< UnknownAttribute > UnknownAttributeList; + +class SAX_DLLPUBLIC FastAttributeList : public ::cppu::WeakImplHelper1< ::com::sun::star::xml::sax::XFastAttributeList > +{ +public: + FastAttributeList( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler >& xTokenHandler ); + virtual ~FastAttributeList(); + + void clear(); + void add( sal_Int32 nToken, const ::rtl::OString& rValue ); + void addUnknown( const ::rtl::OUString& rNamespaceURL, const ::rtl::OString& rName, const ::rtl::OString& rValue ); + void addUnknown( const ::rtl::OString& rName, const ::rtl::OString& rValue ); + + // XFastAttributeList + virtual ::sal_Bool SAL_CALL hasAttribute( ::sal_Int32 Token ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Int32 SAL_CALL getValueToken( ::sal_Int32 Token ) throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual ::sal_Int32 SAL_CALL getOptionalValueToken( ::sal_Int32 Token, ::sal_Int32 Default ) throw (::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getValue( ::sal_Int32 Token ) throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getOptionalValue( ::sal_Int32 Token ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::xml::Attribute > SAL_CALL getUnknownAttributes( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::xml::FastAttribute > SAL_CALL getFastAttributes() throw (::com::sun::star::uno::RuntimeException); + +private: + FastAttributeMap maAttributes; + UnknownAttributeList maUnknownAttributes; + FastAttributeMap::iterator maLastIter; + ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler > mxTokenHandler; + +}; + +} + +#endif diff --git a/sax/inc/sax/fshelper.hxx b/sax/inc/sax/fshelper.hxx new file mode 100644 index 000000000000..f816e3edc1c3 --- /dev/null +++ b/sax/inc/sax/fshelper.hxx @@ -0,0 +1,118 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_FS_HELPER_HXX_ +#define _SAX_FS_HELPER_HXX_ + +#include <com/sun/star/uno/XReference.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/xml/sax/XFastTokenHandler.hpp> +#include <stdarg.h> +#include <boost/shared_ptr.hpp> +#include <sax/fastattribs.hxx> +#include "sax/dllapi.h" + +#define FSNS(namespc, element) ((namespc << 16) | element) +const sal_Int32 FSEND = -1; // same as XML_TOKEN_INVALID + +namespace sax_fastparser { + +enum MergeMarksEnum { MERGE_MARKS_APPEND = 0, MERGE_MARKS_PREPEND = 1, MERGE_MARKS_POSTPONE = 2 }; + +typedef ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastAttributeList > XFastAttributeListRef; + +class FastSaxSerializer; + +class SAX_DLLPUBLIC FastSerializerHelper +{ +public: + + FastSerializerHelper( const ::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream >& xOutputStream ); + + ~FastSerializerHelper(); + + void startElement(const char* elementName, ...); + void singleElement(const char* elementName, ...); + void endElement(const char* elementName); + + void startElementV(sal_Int32 elementTokenId, va_list args); + void singleElementV(sal_Int32 elementTokenId, va_list args); + + inline void startElement(sal_Int32 elementTokenId, ...) + { va_list args; va_start( args, elementTokenId ); startElementV( elementTokenId, args ); va_end( args ); } + inline void singleElement(sal_Int32 elementTokenId, ...) + { va_list args; va_start( args, elementTokenId ); singleElementV( elementTokenId, args ); va_end( args ); } + inline void startElementNS(sal_Int32 namespaceTokenId, sal_Int32 elementTokenId, ...) + { va_list args; va_start( args, elementTokenId ); startElementV( FSNS( namespaceTokenId, elementTokenId), args ); va_end( args ); } + inline void singleElementNS(sal_Int32 namespaceTokenId, sal_Int32 elementTokenId, ...) + { va_list args; va_start( args, elementTokenId ); singleElementV( FSNS( namespaceTokenId, elementTokenId), args ); va_end( args ); } + void endElement(sal_Int32 elementTokenId); + inline void endElementNS(sal_Int32 namespaceTokenId, sal_Int32 elementTokenId) + { endElement( FSNS( namespaceTokenId, elementTokenId ) ); } + + void singleElement(const char* elementName, XFastAttributeListRef xAttrList); + inline void singleElement(sal_Int32 elementTokenId, XFastAttributeListRef xAttrList) + { singleElementV(elementTokenId, xAttrList); } + void singleElementV(sal_Int32 elementTokenId, XFastAttributeListRef xAttrList); + inline void singleElementNS(sal_Int32 namespaceTokenId, sal_Int32 elementTokenId, XFastAttributeListRef xAttrList) + { singleElementV(FSNS( namespaceTokenId, elementTokenId), xAttrList); } + + void startElementV(sal_Int32 elementTokenId, XFastAttributeListRef xAttrList); + inline void startElementNS(sal_Int32 namespaceTokenId, sal_Int32 elementTokenId, XFastAttributeListRef xAttrList) + { startElementV( FSNS( namespaceTokenId, elementTokenId ), xAttrList ); } + + FastSerializerHelper* write(const char* value); + FastSerializerHelper* write(const rtl::OUString& value); + FastSerializerHelper* write(sal_Int32 value); + FastSerializerHelper* write(sal_Int64 value); + FastSerializerHelper* write(float value); + FastSerializerHelper* write(double value); + + FastSerializerHelper* writeEscaped(const char* value); + FastSerializerHelper* writeEscaped(const rtl::OUString& value); + + FastSerializerHelper* writeId(sal_Int32 tokenId); + + ::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream > getOutputStream(); + + FastAttributeList *createAttrList(); + + void mark(); + void mergeTopMarks( MergeMarksEnum eMergeType = MERGE_MARKS_APPEND ); + +private: + + FastSaxSerializer* mpSerializer; + com::sun::star::uno::Reference<com::sun::star::xml::sax::XFastTokenHandler> mxTokenHandler; + +}; + +typedef boost::shared_ptr< FastSerializerHelper > FSHelperPtr; + +} + +#endif // _SAX_FS_HELPER_HXX_ diff --git a/sax/inc/sax/parser/saxparser.hxx b/sax/inc/sax/parser/saxparser.hxx new file mode 100644 index 000000000000..dde71accd680 --- /dev/null +++ b/sax/inc/sax/parser/saxparser.hxx @@ -0,0 +1,150 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_SAXPARSER_HXX_ +#define _SAX_SAXPARSER_HXX_ + +#include "sax/dllapi.h" +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/SAXException.hpp> +#include <rtl/ref.hxx> + +#ifndef BOOST_SHARED_PTR_HPP_INCLUDED +#include <boost/shared_ptr.hpp> +#endif + +#include <map> +#include <memory> +#include "sax/tools/saxobject.hxx" +#include "sax/tools/attributemap.hxx" + +#include <boost/scoped_ptr.hpp> + +namespace sax +{ + +// -------------------------------------------------------------------- + +class SaxParser; +class SaxContext; + +typedef rtl::Reference< SaxParser > SaxParserRef; +typedef rtl::Reference< SaxContext > SaxContextRef; + +/** base class for each implementation that handles all sax calls for an element */ +class SAX_DLLPUBLIC SaxContext : public SaxObject +{ +public: + SaxContext( const SaxParserRef& xParser ); + virtual ~SaxContext(); + + /** receives notification of the beginning of an element . + */ + virtual void StartElement( sal_uInt32 aElementToken, const AttributeMap& rAttributes ); + + /** receives notification of character data. + */ + virtual void Characters( const sal_Char *pCharacters, sal_uInt32 nLength ); + + /** receives notification of the end of an element. + */ + virtual void EndElement( sal_uInt32 aElementToken ); + + /** is called by the SaxParser for each child element inside this instances element */ + virtual SaxContextRef CreateContext( sal_uInt32 aElementToken, const AttributeMap& rAttributes ); + + const SaxParserRef& getParser() const { return mxParser; } +private: + SaxParserRef mxParser; +}; + +// -------------------------------------------------------------------- + +class SaxParserImpl; + +/** base class for loading a single xml stream. Derived classes must call + parseStream to start parsing and are notified through virtual methods + for sax events. */ +class SAX_DLLPUBLIC SaxParser : public SaxObject +{ +public: + SaxParser(); + virtual ~SaxParser(); + + /** returns the unicode representation of a token from the xml stream + that was unknown to the SaxTokenMap from the derived class. */ + rtl::OUString GetCustomToken( sal_uInt32 nToken ); + + /** returns the unicode representation of a namespace from the xml stream + that was unknown to the SaxTokenMap from the derived class. */ + rtl::OUString GetCustomNamespace( sal_uInt32 nToken ); + + /** returns the system id of the currently parsed stream */ + const rtl::OUString& GetSystemId() const; + + /** starts parsing of the source xml stream provided in the given sax InputSource */ + virtual void parseStream( const ::com::sun::star::xml::sax::InputSource& rInputSource ) throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException); + + /** is called once when parsing of the xml stream starts */ + virtual void StartDocument(); + + /** is called once for each element in the xml stream. + Default implementation calls StartElement() on the topmost contex. */ + virtual void StartElement( sal_uInt32 aElementToken, const AttributeMap& rAttributes ); + + /** is called for characters betwen elements in the xml stream. + Default implementation calls Characters() on the topmost contex. + @param pCharacters The characters in utf-8 encoding + @param nLength the size in bytes of the utf-8 string + */ + virtual void Characters( const sal_Char *pCharacters, sal_uInt32 nLength ); + + /** is called once at the end of each element in the xml stream. + Default implementation calls EndElement() on the topmost contex. */ + virtual void EndElement( sal_uInt32 aElementToken ); + + /** is called once after parsing the xml stream is finished */ + virtual void EndDocument(); + + /** is called once for each element in the xml stream and before StartElement() is called. + Default implementation calls CreateContext at the topmost context. + Returned contexts are pushed on a stack and removed after the corresponding EndElement call. */ + virtual SaxContextRef CreateContext( sal_uInt32 aElementToken, const AttributeMap& rAttributes ); + + /** must be implemented from derived classes. The returned SaxTokenMap is used to convert + element names and attribute names and values to tokens. */ + virtual const SaxTokenMapRef& getTokenMap() = 0; + +private: + void AddNamespace( sal_uInt32 nNamespaceId, sal_uInt32 nNamespaceURIToken ); + + boost::scoped_ptr< SaxParserImpl > mpImpl; +}; + +} + +#endif // _SAX_SAXPARSER_HXX_ diff --git a/sax/inc/sax/tools/attributemap.hxx b/sax/inc/sax/tools/attributemap.hxx new file mode 100644 index 000000000000..ea8f55a9b626 --- /dev/null +++ b/sax/inc/sax/tools/attributemap.hxx @@ -0,0 +1,70 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_ATTRIBUTEMAP_HXX_ +#define _SAX_ATTRIBUTEMAP_HXX_ + +#include "sax/dllapi.h" + +#include <map> +#include "sax/tools/tokenmap.hxx" + +namespace sax +{ + class SaxTokenMap; + + typedef std::map< sal_uInt32, rtl::OString > AttributeMapBase; + + /** a map for a set of xml attributes, identified with integer tokens. + Attribute values are stored in utf-8 encoding. */ + class SAX_DLLPUBLIC AttributeMap : public AttributeMapBase + { + public: + AttributeMap( const SaxTokenMap& rTokenMap ); + ~AttributeMap(); + + /** returns a unicode string, if the token does not exists the string is empty */ + ::rtl::OUString getString( SaxToken nToken ) const; + + /** returns true if the attribute with the token nToken is part of this map */ + bool has( SaxToken nToken ) const; + + /** converts the attribute with the token nToken to sal_Int32 or returns + nDefault if this attribute does not exists */ + sal_Int32 getInt32( SaxToken nToken, sal_Int32 nDefault = 0 ) const; + + /** converts the attribute with the token nToken to a token or returns + nDefault if this attribute does not exists */ + sal_uInt32 getToken( SaxToken nToken, SaxToken nDefault ) const; + + private: + const SaxTokenMap& mrTokenMap; + }; + +} + +#endif // _SAX_ATTRIBUTEMAP_HXX_ diff --git a/sax/inc/sax/tools/converter.hxx b/sax/inc/sax/tools/converter.hxx new file mode 100644 index 000000000000..4b65c1dc83b2 --- /dev/null +++ b/sax/inc/sax/tools/converter.hxx @@ -0,0 +1,208 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_CONVERTER_HXX +#define _SAX_CONVERTER_HXX + +#include "sax/dllapi.h" + +#include <sal/types.h> + +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/util/MeasureUnit.hpp> + + +namespace rtl +{ +class OUString; +class OUStringBuffer; +} + +namespace com { namespace sun { namespace star { + namespace util { + struct Date; + struct DateTime; + struct Duration; + } +} } } + +namespace sax { + +/** the Converter converts values of various types from + their internal represantation to the textual form used in xml + and back. + + All unit types are expressed as com::sun::star::util::MeasureUnit +*/ + + +class SAX_DLLPUBLIC Converter +{ +public: + /** convert string to measure using optional min and max values*/ + static bool convertMeasure( sal_Int32& rValue, + const ::rtl::OUString& rString, + sal_Int16 nTargetUnit = ::com::sun::star::util::MeasureUnit::MM_100TH, + sal_Int32 nMin = SAL_MIN_INT32, + sal_Int32 nMax = SAL_MAX_INT32 ); + + /** convert measure to string */ + static void convertMeasure( ::rtl::OUStringBuffer& rBuffer, + sal_Int32 nMeasure, + sal_Int16 SourceUnit = ::com::sun::star::util::MeasureUnit::MM_100TH, + sal_Int16 nTargetUnit = ::com::sun::star::util::MeasureUnit::INCH ); + + /** convert string to boolean */ + static bool convertBool( bool& rBool, + const ::rtl::OUString& rString ); + + /** convert boolean to string */ + static void convertBool( ::rtl::OUStringBuffer& rBuffer, + bool bValue ); + + /** convert string to percent */ + static bool convertPercent( sal_Int32& rValue, + const ::rtl::OUString& rString ); + + /** convert percent to string */ + static void convertPercent( ::rtl::OUStringBuffer& rBuffer, + sal_Int32 nValue ); + + /** convert string to pixel measure unite */ + static bool convertMeasurePx( sal_Int32& rValue, + const ::rtl::OUString& rString ); + + /** convert pixel measure unit to string */ + static void convertMeasurePx( ::rtl::OUStringBuffer& rBuffer, + sal_Int32 nValue ); + + /** convert string to color */ + static bool convertColor( sal_Int32& rColor, + const ::rtl::OUString&rValue ); + + /** convert color to string */ + static void convertColor( ::rtl::OUStringBuffer &rBuffer, + sal_Int32 nColor ); + + /** convert number to string */ + static void convertNumber( ::rtl::OUStringBuffer& rBuffer, + sal_Int32 nNumber ); + + /** convert string to number with optional min and max values */ + static bool convertNumber( sal_Int32& rValue, + const ::rtl::OUString& rString, + sal_Int32 nMin = SAL_MIN_INT32, + sal_Int32 nMax = SAL_MAX_INT32 ); + + /** convert double number to string (using ::rtl::math) and + DO convert from source unit to target unit */ + static void convertDouble( ::rtl::OUStringBuffer& rBuffer, + double fNumber, + bool bWriteUnits, + sal_Int16 nSourceUnit, + sal_Int16 nTargetUnit ); + + /** convert double number to string (using ::rtl::math) without unit conversion */ + static void convertDouble( ::rtl::OUStringBuffer& rBuffer, double fNumber); + + /** convert string to double number (using ::rtl::math) and DO convert from + source unit to target unit. */ + static bool convertDouble( double& rValue, + const ::rtl::OUString& rString, + sal_Int16 nSourceUnit, + sal_Int16 nTargetUnit ); + + /** convert string to double number (using ::rtl::math) without unit conversion */ + static bool convertDouble(double& rValue, const ::rtl::OUString& rString); + + /** convert string to double number (using ::rtl::math) with unit conversion */ + static bool convertDouble(double& rValue, const ::rtl::OUString& rString, sal_Int16 nTargetUnit ); + + /** convert double to ISO "duration" string; negative durations allowed */ + static void convertDuration(::rtl::OUStringBuffer& rBuffer, + const double fTime); + + /** convert util::Duration to ISO "duration" string */ + static void convertDuration(::rtl::OUStringBuffer& rBuffer, + const ::com::sun::star::util::Duration& rDuration); + + /** convert ISO "duration" string to double; negative durations allowed */ + static bool convertDuration(double & rfTime, + const ::rtl::OUString& rString); + + /** convert ISO "duration" string to util::Duration */ + static bool convertDuration(::com::sun::star::util::Duration& rDuration, + const ::rtl::OUString& rString); + + /** convert util::Date to ISO "date" string */ + static void convertDate( ::rtl::OUStringBuffer& rBuffer, + const com::sun::star::util::Date& rDate ); + + /** convert util::DateTime to ISO "date" or "dateTime" string */ + static void convertDateTime( ::rtl::OUStringBuffer& rBuffer, + const com::sun::star::util::DateTime& rDateTime, + bool bAddTimeIf0AM = false ); + + /** convert ISO "date" or "dateTime" string to util::DateTime */ + static bool convertDateTime( com::sun::star::util::DateTime& rDateTime, + const ::rtl::OUString& rString ); + + /** convert ISO "date" or "dateTime" string to util::DateTime or + util::Date */ + static bool convertDateOrDateTime( + com::sun::star::util::Date & rDate, + com::sun::star::util::DateTime & rDateTime, + bool & rbDateTime, + const ::rtl::OUString & rString ); + + /** gets the position of the first comma after npos in the string + rStr. Commas inside '"' pairs are not matched */ + static sal_Int32 indexOfComma( const ::rtl::OUString& rStr, + sal_Int32 nPos ); + + /** encodes the given byte sequence into Base64 */ + static void encodeBase64(rtl::OUStringBuffer& aStrBuffer, const com::sun::star::uno::Sequence<sal_Int8>& aPass); + + // Decode a base 64 encoded string into a sequence of bytes. The first + // version can be used for attribute values only, bacause it does not + // return any chars left from conversion. + // For text submitted throgh the SAX characters call, the later method + // must be used! + static void decodeBase64(com::sun::star::uno::Sequence<sal_Int8>& aPass, const rtl::OUString& sBuffer); + + static sal_Int32 decodeBase64SomeChars(com::sun::star::uno::Sequence<sal_Int8>& aPass, const rtl::OUString& sBuffer); + + static void clearUndefinedChars(rtl::OUString& rTarget, const rtl::OUString& rSource); + + static double GetConversionFactor(::rtl::OUStringBuffer& rUnit, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit); + static sal_Int16 GetUnitFromString(const ::rtl::OUString& rString, sal_Int16 nDefaultUnit); + +}; + +} + +#endif // _SAX_CONVERTER_HXX diff --git a/sax/inc/sax/tools/saxobject.hxx b/sax/inc/sax/tools/saxobject.hxx new file mode 100644 index 000000000000..56b901d9579f --- /dev/null +++ b/sax/inc/sax/tools/saxobject.hxx @@ -0,0 +1,50 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_OBJECT_HXX_ +#define _SAX_OBJECT_HXX_ + +#include <sal/types.h> +#include "sax/dllapi.h" + +namespace sax +{ + + /** simple base class to allow refcounting with rtl::Reference or css::uno::Reference */ + class SAX_DLLPUBLIC SaxObject + { + public: + SaxObject(); + virtual ~SaxObject(); + virtual void SAL_CALL acquire() throw (); + virtual void SAL_CALL release() throw (); + private: + sal_uInt32 mnRefCount; + }; +} + +#endif // _SAX_OBJECT_HXX_ diff --git a/sax/inc/sax/tools/tokenmap.hxx b/sax/inc/sax/tools/tokenmap.hxx new file mode 100644 index 000000000000..42add0690e9a --- /dev/null +++ b/sax/inc/sax/tools/tokenmap.hxx @@ -0,0 +1,65 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_TOKENMAP_HXX_ +#define _SAX_TOKENMAP_HXX_ + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include "sax/dllapi.h" +#include "sax/tools/saxobject.hxx" + +namespace sax +{ + /** type for a token identifier */ + typedef sal_uInt32 SaxToken; + + /** this class maps a set of ascii/utf-8 strings to token identifier */ + class SAX_DLLPUBLIC SaxTokenMap : public SaxObject + { + public: + /** constant do indicate an unknown token */ + const static SaxToken InvalidToken = (SaxToken)-1; + + /** returns the token identifier for the given ascii string or SaxTokenMap::InvalidToken */ + virtual SaxToken GetToken( const sal_Char* pChar, sal_uInt32 nLength = 0 ) const = 0; + + /** returns the token identifier for the given unicode string or SaxTokenMap::InvalidToken */ + virtual SaxToken GetToken( const ::rtl::OUString& rToken ) const = 0; + + /** returns the unicode string for the given token identifier */ + virtual const ::rtl::OUString& GetToken( SaxToken nToken ) const = 0; + + /** returns if the given unicode string equals the given token identifier */ + bool IsToken( const ::rtl::OUString& rToken, SaxToken nToken ) const { return GetToken( rToken ) == nToken; } + }; + + /** reference type to a SaxTokenMap */ + typedef rtl::Reference< SaxTokenMap > SaxTokenMapRef; +} + +#endif // _SAX_TOKENMAP_HXX_ diff --git a/sax/inc/xml2utf.hxx b/sax/inc/xml2utf.hxx new file mode 100644 index 000000000000..1a0640f5a687 --- /dev/null +++ b/sax/inc/xml2utf.hxx @@ -0,0 +1,147 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// TODO: Woher? +#define Max( a, b ) (((a)>(b)) ? (a) : (b) ) +#define Min( a, b ) (((a)<(b)) ? (a) : (b) ) + +/* +* +* Text2UnicodeConverter +* +**/ +namespace sax_expatwrap { + +class Text2UnicodeConverter +{ + +public: + Text2UnicodeConverter( const ::rtl::OString & sEncoding ); + ~Text2UnicodeConverter(); + + ::com::sun::star::uno::Sequence < sal_Unicode > convert( const ::com::sun::star::uno::Sequence<sal_Int8> & ); + sal_Bool canContinue() { return m_bCanContinue; } + +private: + void init( rtl_TextEncoding encoding ); + + rtl_TextToUnicodeConverter m_convText2Unicode; + rtl_TextToUnicodeContext m_contextText2Unicode; + sal_Bool m_bCanContinue; + sal_Bool m_bInitialized; + rtl_TextEncoding m_rtlEncoding; + ::com::sun::star::uno::Sequence<sal_Int8> m_seqSource; +}; + +/*---------------------------------------- +* +* Unicode2TextConverter +* +**-----------------------------------------*/ +class Unicode2TextConverter +{ +public: + Unicode2TextConverter( rtl_TextEncoding encoding ); + ~Unicode2TextConverter(); + + inline ::com::sun::star::uno::Sequence<sal_Int8> convert( const ::rtl::OUString &s ) + { + return convert( s.getStr() , s.getLength() ); + } + ::com::sun::star::uno::Sequence<sal_Int8> convert( const sal_Unicode * , sal_Int32 nLength ); + sal_Bool canContinue() { return m_bCanContinue; } + +private: + void init( rtl_TextEncoding encoding ); + + rtl_UnicodeToTextConverter m_convUnicode2Text; + rtl_UnicodeToTextContext m_contextUnicode2Text; + sal_Bool m_bCanContinue; + sal_Bool m_bInitialized; + rtl_TextEncoding m_rtlEncoding; + ::com::sun::star::uno::Sequence<sal_Unicode> m_seqSource; +}; + + + +/*---------------------------------------- +* +* XMLFile2UTFConverter +* +**-----------------------------------------*/ +class XMLFile2UTFConverter +{ +public: + XMLFile2UTFConverter( ): + m_bStarted( sal_False ), + m_pText2Unicode( 0 ), + m_pUnicode2Text( 0 ) + {} + + ~XMLFile2UTFConverter(); + + void setInputStream( ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > &r ) { m_in = r; } + void setEncoding( const ::rtl::OString &s ) { m_sEncoding = s; } + + + + // @param nMaxToRead The number of chars, that should be read. Note that this is no exact number. There + // may be returned less or more bytes than ordered. + sal_Int32 readAndConvert( ::com::sun::star::uno::Sequence<sal_Int8> &seq , sal_Int32 nMaxToRead ) + throw ( ::com::sun::star::io::IOException, + ::com::sun::star::io::NotConnectedException , + ::com::sun::star::io::BufferSizeExceededException , + ::com::sun::star::uno::RuntimeException ); + +private: + + // Called only on first Sequence of bytes. Tries to figure out file format and encoding information. + // @return TRUE, when encoding information could be retrieved + // @return FALSE, when no encoding information was found in file + sal_Bool scanForEncoding( ::com::sun::star::uno::Sequence<sal_Int8> &seq ); + + // Called only on first Sequence of bytes. Tries to figure out + // if enough data is available to scan encoding + // @return TRUE, when encoding is retrievable + // @return FALSE, when more data is needed + sal_Bool isEncodingRecognizable( const ::com::sun::star::uno::Sequence< sal_Int8 > & seq ); + + // When encoding attribute is within the text (in the first line), it is removed. + void removeEncoding( ::com::sun::star::uno::Sequence<sal_Int8> &seq ); + + // Initializes decoding depending on m_sEncoding setting + void initializeDecoding(); +private: + ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > m_in; + + sal_Bool m_bStarted; + ::rtl::OString m_sEncoding; + + Text2UnicodeConverter *m_pText2Unicode; + Unicode2TextConverter *m_pUnicode2Text; +}; +} diff --git a/sax/prj/build.lst b/sax/prj/build.lst new file mode 100644 index 000000000000..653d77ce9e25 --- /dev/null +++ b/sax/prj/build.lst @@ -0,0 +1,6 @@ +ax sax : offapi cppuhelper EXPAT:expat comphelper NULL +ax sax usr1 - all ax_mkout NULL +ax sax\source\expatwrap nmake - all ax_expatwrap NULL +ax sax\source\tools nmake - all ax_tools NULL +ax sax\source\fastparser nmake - all ax_fastparser ax_expatwrap ax_tools NULL +ax sax\qa\cppunit nmake - all ax_qa_cppunit ax_tools NULL diff --git a/sax/prj/d.lst b/sax/prj/d.lst new file mode 100644 index 000000000000..76177f554909 --- /dev/null +++ b/sax/prj/d.lst @@ -0,0 +1,16 @@ +..\%__SRC%\bin\*.dll %_DEST%\bin%_EXT%\*.dll +..\%__SRC%\lib\*.so %_DEST%\lib%_EXT%\*.so +..\%__SRC%\lib\*.dylib %_DEST%\lib%_EXT%\*.dylib +..\%__SRC%\lib\*.lib %_DEST%\lib%_EXT%\*.lib +..\%__SRC%\misc\fastsax.component %_DEST%\xml%_EXT%\fastsax.component +..\%__SRC%\misc\sax.component %_DEST%\xml%_EXT%\sax.component +..\%__SRC%\misc\sax.inbuild.component %_DEST%\xml%_EXT%\sax.inbuild.component + +mkdir: %_DEST%\inc%_EXT%\sax +mkdir: %_DEST%\inc%_EXT%\sax\tools +..\inc\sax\dllapi.h %_DEST%\inc%_EXT%\sax\dllapi.h +..\inc\sax\fshelper.hxx %_DEST%\inc%_EXT%\sax\fshelper.hxx +..\inc\sax\fastattribs.hxx %_DEST%\inc%_EXT%\sax\fastattribs.hxx +..\inc\sax\tools\converter.hxx %_DEST%\inc%_EXT%\sax\tools\converter.hxx + +dos: sh -c "if test %OS% = MACOSX; then macosx-create-bundle %_DEST%\lib%_EXT%\*.dylib; fi" diff --git a/sax/qa/cppunit/makefile.mk b/sax/qa/cppunit/makefile.mk new file mode 100644 index 000000000000..b28f0cbac2ab --- /dev/null +++ b/sax/qa/cppunit/makefile.mk @@ -0,0 +1,78 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=../.. +PRJNAME=sax +TARGET=qa_cppunit + +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +#building with stlport, but cppunit was not built with stlport +.IF "$(USE_SYSTEM_STL)"!="YES" +.IF "$(SYSTEM_CPPUNIT)"=="YES" +CFLAGSCXX+=-DADAPT_EXT_STL +.ENDIF +.ENDIF + +CFLAGSCXX += $(CPPUNIT_CFLAGS) +DLLPRE = # no leading "lib" on .so files + +# --- Libs --------------------------------------------------------- + +SHL1OBJS= \ + $(SLO)/test_converter.obj \ + + +SHL1STDLIBS= \ + $(SAXLIB) \ + $(SALLIB) \ + $(CPPUNITLIB) \ + + +SHL1TARGET= test_converter +SHL1RPATH = NONE +SHL1IMPLIB= i$(SHL1TARGET) +# SHL1DEF= $(MISC)/$(SHL1TARGET).def +DEF1NAME=$(SHL1TARGET) +# DEF1EXPORTFILE= export.exp +SHL1VERSIONMAP= version.map + +# --- All object files --------------------------------------------- + +SLOFILES= \ + $(SHL1OBJS) \ + + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk +.INCLUDE : _cppunit.mk + diff --git a/sax/qa/cppunit/test_converter.cxx b/sax/qa/cppunit/test_converter.cxx new file mode 100644 index 000000000000..b1881f248c0c --- /dev/null +++ b/sax/qa/cppunit/test_converter.cxx @@ -0,0 +1,246 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "preextstl.h" +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include "postextstl.h" + +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Duration.hpp> + +#include "sax/tools/converter.hxx" + + +using namespace ::com::sun::star; +using sax::Converter; + + +namespace { + +class ConverterTest + : public ::CppUnit::TestFixture +{ +public: + virtual void setUp(); + virtual void tearDown(); + + void testDuration(); + void testDateTime(); + + CPPUNIT_TEST_SUITE(ConverterTest); + CPPUNIT_TEST(testDuration); + CPPUNIT_TEST(testDateTime); + CPPUNIT_TEST_SUITE_END(); + +private: +}; + +void ConverterTest::setUp() +{ +} + +void ConverterTest::tearDown() +{ +} + +static bool eqDuration(util::Duration a, util::Duration b) { + return a.Years == b.Years && a.Months == b.Months && a.Days == b.Days + && a.Hours == b.Hours && a.Minutes == b.Minutes + && a.Seconds == b.Seconds + && a.MilliSeconds == b.MilliSeconds + && a.Negative == b.Negative; +} + +static void doTest(util::Duration const & rid, char const*const pis, + char const*const i_pos = 0) +{ + char const*const pos((i_pos) ? i_pos : pis); + util::Duration od; + ::rtl::OUString is(::rtl::OUString::createFromAscii(pis)); + bool bSuccess = Converter::convertDuration(od, is); + OSL_TRACE("%d %dY %dM %dD %dH %dM %dS %dm", + od.Negative, od.Years, od.Months, od.Days, + od.Hours, od.Minutes, od.Seconds, od.MilliSeconds); + CPPUNIT_ASSERT(bSuccess); + CPPUNIT_ASSERT(eqDuration(rid, od)); + ::rtl::OUStringBuffer buf; + Converter::convertDuration(buf, od); + OSL_TRACE( + ::rtl::OUStringToOString(buf.getStr(), RTL_TEXTENCODING_UTF8)); + CPPUNIT_ASSERT(buf.makeStringAndClear().equalsAscii(pos)); +} + +static void doTestDurationF(char const*const pis) +{ + util::Duration od; + bool bSuccess = Converter::convertDuration(od, + ::rtl::OUString::createFromAscii(pis)); + OSL_TRACE("%d %dY %dM %dD %dH %dM %dS %dH", + od.Negative, od.Years, od.Months, od.Days, + od.Hours, od.Minutes, od.Seconds, od.MilliSeconds); + CPPUNIT_ASSERT(!bSuccess); +} + +void ConverterTest::testDuration() +{ + OSL_TRACE("\nSAX CONVERTER TEST BEGIN\n"); + doTest( util::Duration(false, 1, 0, 0, 0, 0, 0, 0), "P1Y" ); + doTest( util::Duration(false, 0, 42, 0, 0, 0, 0, 0), "P42M" ); + doTest( util::Duration(false, 0, 0, 111, 0, 0, 0, 0), "P111D" ); + doTest( util::Duration(false, 0, 0, 0, 52, 0, 0, 0), "PT52H" ); + doTest( util::Duration(false, 0, 0, 0, 0, 717, 0, 0), "PT717M" ); + doTest( util::Duration(false, 0, 0, 0, 0, 0, 121, 0), "PT121S" ); + doTest( util::Duration(false, 0, 0, 0, 0, 0, 0, 190), "PT0.19S" ); + doTest( util::Duration(false, 0, 0, 0, 0, 0, 0, 90), "PT0.09S" ); + doTest( util::Duration(false, 0, 0, 0, 0, 0, 0, 9), "PT0.009S" ); + doTest( util::Duration(false, 0, 0, 0, 0, 0, 9, 999), + "PT9.999999999999999999999999999999S", "PT9.999S" ); + doTest( util::Duration(true , 0, 0, 9999, 0, 0, 0, 0), "-P9999D" ); + doTest( util::Duration(true , 7, 6, 5, 4, 3, 2, 10), + "-P7Y6M5DT4H3M2.01S" ); + doTest( util::Duration(false, 0, 6, 0, 0, 3, 0, 0), "P6MT3M" ); + doTest( util::Duration(false, 0, 0, 0, 0, 0, 0, 0), "P0D" ); + doTestDurationF("1Y1M"); // invalid: no ^P + doTestDurationF("P-1Y1M"); // invalid: - after P + doTestDurationF("P1M1Y"); // invalid: Y after M + doTestDurationF("PT1Y"); // invalid: Y after T + doTestDurationF("P1Y1M1M"); // invalid: M twice, no T + doTestDurationF("P1YT1MT1M"); // invalid: T twice + doTestDurationF("P1YT"); // invalid: T but no H,M,S + doTestDurationF("P99999999999Y"); // cannot parse so many Ys + doTestDurationF("PT.1S"); // invalid: no 0 preceding . + doTestDurationF("PT5M.134S"); // invalid: no 0 preceding . + doTestDurationF("PT1.S"); // invalid: no digit following . + OSL_TRACE("\nSAX CONVERTER TEST END\n"); +} + + +static bool eqDateTime(util::DateTime a, util::DateTime b) { + return a.Year == b.Year && a.Month == b.Month && a.Day == b.Day + && a.Hours == b.Hours && a.Minutes == b.Minutes + && a.Seconds == b.Seconds + && a.HundredthSeconds == b.HundredthSeconds; +} + +static void doTest(util::DateTime const & rdt, char const*const pis, + char const*const i_pos = 0) +{ + char const*const pos((i_pos) ? i_pos : pis); + ::rtl::OUString is(::rtl::OUString::createFromAscii(pis)); + util::DateTime odt; + bool bSuccess( Converter::convertDateTime(odt, is) ); + OSL_TRACE("Y:%d M:%d D:%d H:%d M:%d S:%d H:%d", + odt.Year, odt.Month, odt.Day, + odt.Hours, odt.Minutes, odt.Seconds, odt.HundredthSeconds); + CPPUNIT_ASSERT(bSuccess); + CPPUNIT_ASSERT(eqDateTime(rdt, odt)); + ::rtl::OUStringBuffer buf; + Converter::convertDateTime(buf, odt, true); + OSL_TRACE( + ::rtl::OUStringToOString(buf.getStr(), RTL_TEXTENCODING_UTF8)); + CPPUNIT_ASSERT(buf.makeStringAndClear().equalsAscii(pos)); +} + +static void doTestDateTimeF(char const*const pis) +{ + util::DateTime odt; + bool bSuccess = Converter::convertDateTime(odt, + ::rtl::OUString::createFromAscii(pis)); + OSL_TRACE("Y:%d M:%d D:%d H:%dH M:%d S:%d H:%d", + odt.Year, odt.Month, odt.Day, + odt.Hours, odt.Minutes, odt.Seconds, odt.HundredthSeconds); + CPPUNIT_ASSERT(!bSuccess); +} + +void ConverterTest::testDateTime() +{ + OSL_TRACE("\nSAX CONVERTER TEST BEGIN\n"); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), "0001-01-01T00:00:00" ); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), + "0001-01-01T00:00:00Z", "0001-01-01T00:00:00" ); +// doTest( util::DateTime(0, 0, 0, 0, 1, 1, -1), "-0001-01-01T00:00:00" ); +// doTest( util::DateTime(0, 0, 0, 0, 1, 1, -1), "-0001-01-01T00:00:00Z" ); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), + "0001-01-01T00:00:00-00:00", "0001-01-01T00:00:00" ); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), + "0001-01-01T00:00:00+00:00", "0001-01-01T00:00:00" ); + doTest( util::DateTime(0, 0, 0, 0, 2, 1, 1)/*(0, 0, 12, 0, 2, 1, 1)*/, + "0001-01-02T00:00:00-12:00", "0001-01-02T00:00:00" ); +// "0001-02-01T12:00:00" ); + doTest( util::DateTime(0, 0, 0, 0, 2, 1, 1)/*(0, 0, 12, 0, 1, 1, 1)*/, + "0001-01-02T00:00:00+12:00", "0001-01-02T00:00:00" ); +// "0001-01-01T12:00:00" ); + doTest( util::DateTime(99, 59, 59, 23, 31, 12, 9999), + "9999-12-31T23:59:59.99" ); + doTest( util::DateTime(99, 59, 59, 23, 31, 12, 9999), + "9999-12-31T23:59:59.99Z", "9999-12-31T23:59:59.99" ); + doTest( util::DateTime(99, 59, 59, 23, 31, 12, 9999), + "9999-12-31T23:59:59.9999999999999999999999999999999999999", + "9999-12-31T23:59:59.99" ); + doTest( util::DateTime(99, 59, 59, 23, 31, 12, 9999), + "9999-12-31T23:59:59.9999999999999999999999999999999999999Z", + "9999-12-31T23:59:59.99" ); + doTest( util::DateTime(0, 0, 0, 24, 1, 1, 333) + /*(0, 0, 0, 0, 2, 1, 333)*/, + "0333-01-01T24:00:00"/*, "0333-01-02T00:00:00"*/ ); + doTestDateTimeF( "+0001-01-01T00:00:00" ); // invalid: ^+ + doTestDateTimeF( "1-01-01T00:00:00" ); // invalid: < 4 Y + doTestDateTimeF( "0001-1-01T00:00:00" ); // invalid: < 2 M + doTestDateTimeF( "0001-01-1T00:00:00" ); // invalid: < 2 D + doTestDateTimeF( "0001-01-01T0:00:00" ); // invalid: < 2 H + doTestDateTimeF( "0001-01-01T00:0:00" ); // invalid: < 2 M + doTestDateTimeF( "0001-01-01T00:00:0" ); // invalid: < 2 S + doTestDateTimeF( "0001-01-01T00:00:00." ); // invalid: .$ + doTestDateTimeF( "0001-01-01T00:00:00+1:00" ); // invalid: < 2 TZ H + doTestDateTimeF( "0001-01-01T00:00:00+00:1" ); // invalid: < 2 TZ M + doTestDateTimeF( "0001-13-01T00:00:00" ); // invalid: M > 12 + doTestDateTimeF( "0001-01-32T00:00:00" ); // invalid: D > 31 + doTestDateTimeF( "0001-01-01T25:00:00" ); // invalid: H > 24 + doTestDateTimeF( "0001-01-01T00:60:00" ); // invalid: H > 59 + doTestDateTimeF( "0001-01-01T00:00:60" ); // invalid: S > 59 + doTestDateTimeF( "0001-01-01T24:01:00" ); // invalid: H=24, but M != 0 + doTestDateTimeF( "0001-01-01T24:00:01" ); // invalid: H=24, but S != 0 + doTestDateTimeF( "0001-01-01T24:00:00.1" ); // invalid: H=24, but H != 0 + doTestDateTimeF( "0001-01-02T00:00:00+15:00" ); // invalid: TZ > +14:00 + doTestDateTimeF( "0001-01-02T00:00:00+14:01" ); // invalid: TZ > +14:00 + doTestDateTimeF( "0001-01-02T00:00:00-15:00" ); // invalid: TZ < -14:00 + doTestDateTimeF( "0001-01-02T00:00:00-14:01" ); // invalid: TZ < -14:00 + OSL_TRACE("\nSAX CONVERTER TEST END\n"); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(ConverterTest); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + diff --git a/sax/qa/cppunit/version.map b/sax/qa/cppunit/version.map new file mode 100644 index 000000000000..3308588ef6f8 --- /dev/null +++ b/sax/qa/cppunit/version.map @@ -0,0 +1,34 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +UDK_3_0_0 { + global: + cppunitTestPlugIn; + + local: + *; +}; diff --git a/sax/source/expatwrap/attrlistimpl.cxx b/sax/source/expatwrap/attrlistimpl.cxx new file mode 100644 index 000000000000..114eb653f648 --- /dev/null +++ b/sax/source/expatwrap/attrlistimpl.cxx @@ -0,0 +1,168 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "attrlistimpl.hxx" + +#include <vector> + +#include <cppuhelper/weak.hxx> + +using namespace ::std; +using namespace ::rtl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::xml::sax; + + +namespace sax_expatwrap { +struct TagAttribute +{ + TagAttribute() + {} + TagAttribute( const OUString &aName, const OUString &aType , const OUString &aValue ) + { + this->sName = aName; + this->sType = aType; + this->sValue = aValue; + } + + OUString sName; + OUString sType; + OUString sValue; +}; + +struct AttributeList_impl +{ + AttributeList_impl() + { + // performance improvement during adding + vecAttribute.reserve(20); + } + vector<struct TagAttribute> vecAttribute; +}; + + + +sal_Int16 AttributeList::getLength(void) throw (RuntimeException) +{ + return static_cast<sal_Int16>(m_pImpl->vecAttribute.size()); +} + + +AttributeList::AttributeList( const AttributeList &r ) : + cppu::WeakImplHelper2<XAttributeList, XCloneable>() +{ + m_pImpl = new AttributeList_impl; + *m_pImpl = *(r.m_pImpl); +} + +OUString AttributeList::getNameByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( std::vector< TagAttribute >::size_type(i) < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sName; + } + return OUString(); +} + + +OUString AttributeList::getTypeByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( std::vector< TagAttribute >::size_type(i) < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sType; + } + return OUString(); +} + +OUString AttributeList::getValueByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( std::vector< TagAttribute >::size_type(i) < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sValue; + } + return OUString(); + +} + +OUString AttributeList::getTypeByName( const OUString& sName ) throw (RuntimeException) +{ + vector<struct TagAttribute>::iterator ii = m_pImpl->vecAttribute.begin(); + + for( ; ii != m_pImpl->vecAttribute.end() ; ii ++ ) { + if( (*ii).sName == sName ) { + return (*ii).sType; + } + } + return OUString(); +} + +OUString AttributeList::getValueByName(const OUString& sName) throw (RuntimeException) +{ + vector<struct TagAttribute>::iterator ii = m_pImpl->vecAttribute.begin(); + + for( ; ii != m_pImpl->vecAttribute.end() ; ii ++ ) { + if( (*ii).sName == sName ) { + return (*ii).sValue; + } + } + return OUString(); +} + + +Reference< XCloneable > AttributeList::createClone() throw (RuntimeException) +{ + AttributeList *p = new AttributeList( *this ); + return Reference< XCloneable > ( (XCloneable * ) p ); +} + + + +AttributeList::AttributeList() +{ + m_pImpl = new AttributeList_impl; +} + + + +AttributeList::~AttributeList() +{ + delete m_pImpl; +} + + +void AttributeList::addAttribute( const OUString &sName , + const OUString &sType , + const OUString &sValue ) +{ + m_pImpl->vecAttribute.push_back( TagAttribute( sName , sType , sValue ) ); +} + +void AttributeList::clear() +{ + m_pImpl->vecAttribute.clear(); +} + +} diff --git a/sax/source/expatwrap/attrlistimpl.hxx b/sax/source/expatwrap/attrlistimpl.hxx new file mode 100644 index 000000000000..aaf6cf84b359 --- /dev/null +++ b/sax/source/expatwrap/attrlistimpl.hxx @@ -0,0 +1,85 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_ATTRLISTIMPL_HXX +#define _SAX_ATTRLISTIMPL_HXX + +#include "sal/config.h" +//#include "sax/saxdllapi.h" + +#include <cppuhelper/implbase2.hxx> + +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/xml/sax/XAttributeList.hpp> + +namespace sax_expatwrap +{ + +struct AttributeList_impl; + +//FIXME +class /*SAX_DLLPUBLIC*/ AttributeList : + public ::cppu::WeakImplHelper2< + ::com::sun::star::xml::sax::XAttributeList, + ::com::sun::star::util::XCloneable > +{ +public: + AttributeList(); + AttributeList( const AttributeList & ); + virtual ~AttributeList(); + + void addAttribute( const ::rtl::OUString &sName , + const ::rtl::OUString &sType , const ::rtl::OUString &sValue ); + void clear(); +public: + // XAttributeList + virtual sal_Int16 SAL_CALL getLength(void) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getNameByIndex(sal_Int16 i) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getTypeByIndex(sal_Int16 i) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getTypeByName(const ::rtl::OUString& aName) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getValueByIndex(sal_Int16 i) + throw(::com::sun::star::uno::RuntimeException); + virtual ::rtl::OUString SAL_CALL getValueByName(const ::rtl::OUString& aName) + throw( ::com::sun::star::uno::RuntimeException); + + // XCloneable + virtual ::com::sun::star::uno::Reference< XCloneable > SAL_CALL + createClone() throw(::com::sun::star::uno::RuntimeException); + +private: + struct AttributeList_impl *m_pImpl; +}; + +} + +#endif + diff --git a/sax/source/expatwrap/factory.hxx b/sax/source/expatwrap/factory.hxx new file mode 100644 index 000000000000..c6a566781044 --- /dev/null +++ b/sax/source/expatwrap/factory.hxx @@ -0,0 +1,34 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +namespace sax_expatwrap { +Reference< XInterface > SAL_CALL SaxWriter_CreateInstance( + const Reference< XMultiServiceFactory > & rSMgr ) throw (Exception); +OUString SaxWriter_getServiceName() throw(); +OUString SaxWriter_getImplementationName() throw(); +Sequence< OUString > SaxWriter_getSupportedServiceNames(void) throw(); +} + diff --git a/sax/source/expatwrap/makefile.mk b/sax/source/expatwrap/makefile.mk new file mode 100644 index 000000000000..6e1348c0d403 --- /dev/null +++ b/sax/source/expatwrap/makefile.mk @@ -0,0 +1,87 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ=..$/.. + +PRJNAME=sax +TARGET=sax.uno +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +DLLPRE = + +.IF "$(SYSTEM_ZLIB)" == "YES" +CFLAGS+=-DSYSTEM_ZLIB +.ENDIF + +.IF "$(SYSTEM_EXPAT)" == "YES" +CFLAGS+=-DSYSTEM_EXPAT +.ELSE +CFLAGS += -DXML_UNICODE +.ENDIF + +#----------------------------------------------------------- + +SLOFILES =\ + $(SLO)$/xml2utf.obj\ + $(SLO)$/attrlistimpl.obj\ + $(SLO)$/sax_expat.obj \ + $(SLO)$/saxwriter.obj + +SHL1TARGET= $(TARGET) +SHL1IMPLIB= i$(TARGET) + +SHL1STDLIBS= \ + $(SALLIB) \ + $(CPPULIB) \ + $(CPPUHELPERLIB)\ + $(EXPAT3RDLIB) + +SHL1DEPN= +SHL1VERSIONMAP= $(SOLARENV)$/src$/component.map +SHL1LIBS= $(SLB)$/$(TARGET).lib +SHL1DEF= $(MISC)$/$(SHL1TARGET).def +DEF1NAME= $(SHL1TARGET) + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +ALLTAR : $(MISC)/sax.component $(MISC)/sax.inbuild.component + +$(MISC)/sax.component .ERRREMOVE : $(SOLARENV)/bin/createcomponent.xslt \ + sax.component + $(XSLTPROC) --nonet --stringparam uri \ + '$(COMPONENTPREFIX_BASIS_NATIVE)$(SHL1TARGETN:f)' -o $@ \ + $(SOLARENV)/bin/createcomponent.xslt sax.component + +$(MISC)/sax.inbuild.component .ERRREMOVE : \ + $(SOLARENV)/bin/createcomponent.xslt sax.component + $(XSLTPROC) --nonet --stringparam uri \ + '$(COMPONENTPREFIX_INBUILD_NATIVE)$(SHL1TARGETN:f)' -o $@ \ + $(SOLARENV)/bin/createcomponent.xslt sax.component diff --git a/sax/source/expatwrap/sax.component b/sax/source/expatwrap/sax.component new file mode 100644 index 000000000000..5e6699d9dd33 --- /dev/null +++ b/sax/source/expatwrap/sax.component @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--********************************************************************** +* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* Copyright 2000, 2010 Oracle and/or its affiliates. +* +* OpenOffice.org - a multi-platform office productivity suite +* +* This file is part of OpenOffice.org. +* +* OpenOffice.org is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 3 +* only, as published by the Free Software Foundation. +* +* OpenOffice.org is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License version 3 for more details +* (a copy is included in the LICENSE file that accompanied this code). +* +* You should have received a copy of the GNU Lesser General Public License +* version 3 along with OpenOffice.org. If not, see +* <http://www.openoffice.org/license.html> +* for a copy of the LGPLv3 License. +* +**********************************************************************--> + +<component loader="com.sun.star.loader.SharedLibrary" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.extensions.xml.sax.ParserExpat"> + <service name="com.sun.star.xml.sax.Parser"/> + </implementation> + <implementation name="com.sun.star.extensions.xml.sax.Writer"> + <service name="com.sun.star.xml.sax.Writer"/> + </implementation> +</component> diff --git a/sax/source/expatwrap/sax_expat.cxx b/sax/source/expatwrap/sax_expat.cxx new file mode 100644 index 000000000000..aaaac6bd564e --- /dev/null +++ b/sax/source/expatwrap/sax_expat.cxx @@ -0,0 +1,1076 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include <stdlib.h> +#include <string.h> +#include <sal/alloca.h> +#include <vector> + +#include <osl/diagnose.h> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/io/XSeekable.hpp> + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase1.hxx> +#include <cppuhelper/implbase2.hxx> + +#include <expat.h> + +using namespace ::rtl; +using namespace ::std; +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; + +#include "factory.hxx" +#include "attrlistimpl.hxx" +#include "xml2utf.hxx" + +namespace sax_expatwrap { + +// Useful macros for correct String conversion depending on the choosen expat-mode +#ifdef XML_UNICODE +OUString XmlNChar2OUString( const XML_Char *p , int nLen ) +{ + if( p ) { + if( sizeof( sal_Unicode ) == sizeof( XML_Char ) ) + { + return OUString( (sal_Unicode*)p,nLen); + } + else + { + sal_Unicode *pWchar = (sal_Unicode *)alloca( sizeof( sal_Unicode ) * nLen ); + for( int n = 0 ; n < nLen ; n++ ) { + pWchar[n] = (sal_Unicode) p[n]; + } + return OUString( pWchar , nLen ); + } + } + else { + return OUString(); + } +} + +OUString XmlChar2OUString( const XML_Char *p ) +{ + if( p ) { + int nLen; + for( nLen = 0 ; p[nLen] ; nLen ++ ) + ; + return XmlNChar2OUString( p , nLen ); + } + else return OUString(); +} + + +#define XML_CHAR_TO_OUSTRING(x) XmlChar2OUString(x) +#define XML_CHAR_N_TO_USTRING(x,n) XmlNChar2OUString(x,n) +#else +#define XML_CHAR_TO_OUSTRING(x) OUString(x , strlen( x ), RTL_TEXTENCODING_UTF8) +#define XML_CHAR_N_TO_USTRING(x,n) OUString(x,n, RTL_TEXTENCODING_UTF8 ) +#endif + + +/* +* The following macro encapsulates any call to an event handler. +* It ensures, that exceptions thrown by the event handler are +* treated properly. +*/ +#define CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS(pThis,call) \ + if( ! pThis->bExceptionWasThrown ) { \ + try {\ + pThis->call;\ + }\ + catch( SAXParseException &e ) {\ + pThis->callErrorHandler( pThis , e );\ + }\ + catch( SAXException &e ) {\ + pThis->callErrorHandler( pThis , SAXParseException(\ + e.Message, \ + e.Context, \ + e.WrappedException,\ + pThis->rDocumentLocator->getPublicId(),\ + pThis->rDocumentLocator->getSystemId(),\ + pThis->rDocumentLocator->getLineNumber(),\ + pThis->rDocumentLocator->getColumnNumber()\ + ) );\ + }\ + catch( com::sun::star::uno::RuntimeException &e ) {\ + pThis->bExceptionWasThrown = sal_True; \ + pThis->bRTExceptionWasThrown = sal_True; \ + pImpl->rtexception = e; \ + }\ + }\ + ((void)0) + +#define IMPLEMENTATION_NAME "com.sun.star.comp.extensions.xml.sax.ParserExpat" +#define SERVICE_NAME "com.sun.star.xml.sax.Parser" + +class SaxExpatParser_Impl; + + +// This class implements the external Parser interface +class SaxExpatParser : + public WeakImplHelper2< + XParser, + XServiceInfo + > +{ + +public: + SaxExpatParser(); + ~SaxExpatParser(); + +public: + + // The implementation details + static Sequence< OUString > getSupportedServiceNames_Static(void) throw (); + +public: + // The SAX-Parser-Interface + virtual void SAL_CALL parseStream( const InputSource& structSource) + throw ( SAXException, + IOException, + RuntimeException); + virtual void SAL_CALL setDocumentHandler(const Reference< XDocumentHandler > & xHandler) + throw (RuntimeException); + + virtual void SAL_CALL setErrorHandler(const Reference< XErrorHandler > & xHandler) + throw (RuntimeException); + virtual void SAL_CALL setDTDHandler(const Reference < XDTDHandler > & xHandler) + throw (RuntimeException); + virtual void SAL_CALL setEntityResolver(const Reference< XEntityResolver >& xResolver) + throw (RuntimeException); + + virtual void SAL_CALL setLocale( const Locale &locale ) throw (RuntimeException); + +public: // XServiceInfo + OUString SAL_CALL getImplementationName() throw (); + Sequence< OUString > SAL_CALL getSupportedServiceNames(void) throw (); + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) throw (); + +private: + + SaxExpatParser_Impl *m_pImpl; + +}; + +//-------------------------------------- +// the extern interface +//--------------------------------------- +Reference< XInterface > SAL_CALL SaxExpatParser_CreateInstance( + const Reference< XMultiServiceFactory > & ) throw(Exception) +{ + SaxExpatParser *p = new SaxExpatParser; + + return Reference< XInterface > ( (OWeakObject * ) p ); +} + + + +Sequence< OUString > SaxExpatParser::getSupportedServiceNames_Static(void) throw () +{ + Sequence<OUString> aRet(1); + aRet.getArray()[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(SERVICE_NAME) ); + return aRet; +} + + +//--------------------------------------------- +// the implementation part +//--------------------------------------------- + + +// Entity binds all information neede for a single file +struct Entity +{ + InputSource structSource; + XML_Parser pParser; + XMLFile2UTFConverter converter; +}; + + +class SaxExpatParser_Impl +{ +public: // module scope + Mutex aMutex; + + Reference< XDocumentHandler > rDocumentHandler; + Reference< XExtendedDocumentHandler > rExtendedDocumentHandler; + + Reference< XErrorHandler > rErrorHandler; + Reference< XDTDHandler > rDTDHandler; + Reference< XEntityResolver > rEntityResolver; + Reference < XLocator > rDocumentLocator; + + + Reference < XAttributeList > rAttrList; + AttributeList *pAttrList; + + // External entity stack + vector<struct Entity> vecEntity; + void pushEntity( const struct Entity &entity ) + { vecEntity.push_back( entity ); } + void popEntity() + { vecEntity.pop_back( ); } + struct Entity &getEntity() + { return vecEntity.back(); } + + + // Exception cannot be thrown through the C-XmlParser (possible resource leaks), + // therefor the exception must be saved somewhere. + SAXParseException exception; + RuntimeException rtexception; + sal_Bool bExceptionWasThrown; + sal_Bool bRTExceptionWasThrown; + + Locale locale; + +public: + // the C-Callbacks for the expat parser + void static callbackStartElement(void *userData, const XML_Char *name , const XML_Char **atts); + void static callbackEndElement(void *userData, const XML_Char *name); + void static callbackCharacters( void *userData , const XML_Char *s , int nLen ); + void static callbackProcessingInstruction( void *userData , + const XML_Char *sTarget , + const XML_Char *sData ); + + void static callbackUnparsedEntityDecl( void *userData , + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + + void static callbackNotationDecl( void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + + int static callbackExternalEntityRef( XML_Parser parser, + const XML_Char *openEntityNames, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + + int static callbackUnknownEncoding(void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + + void static callbackDefault( void *userData, const XML_Char *s, int len); + + void static callbackStartCDATA( void *userData ); + void static callbackEndCDATA( void *userData ); + void static callbackComment( void *userData , const XML_Char *s ); + void static callErrorHandler( SaxExpatParser_Impl *pImpl , const SAXParseException &e ); + +public: + void parse(); +}; + +extern "C" +{ + static void call_callbackStartElement(void *userData, const XML_Char *name , const XML_Char **atts) + { + SaxExpatParser_Impl::callbackStartElement(userData,name,atts); + } + static void call_callbackEndElement(void *userData, const XML_Char *name) + { + SaxExpatParser_Impl::callbackEndElement(userData,name); + } + static void call_callbackCharacters( void *userData , const XML_Char *s , int nLen ) + { + SaxExpatParser_Impl::callbackCharacters(userData,s,nLen); + } + static void call_callbackProcessingInstruction(void *userData,const XML_Char *sTarget,const XML_Char *sData ) + { + SaxExpatParser_Impl::callbackProcessingInstruction(userData,sTarget,sData ); + } + static void call_callbackUnparsedEntityDecl(void *userData , + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName) + { + SaxExpatParser_Impl::callbackUnparsedEntityDecl(userData,entityName,base,systemId,publicId,notationName); + } + static void call_callbackNotationDecl(void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId) + { + SaxExpatParser_Impl::callbackNotationDecl(userData,notationName,base,systemId,publicId); + } + static int call_callbackExternalEntityRef(XML_Parser parser, + const XML_Char *openEntityNames, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId) + { + return SaxExpatParser_Impl::callbackExternalEntityRef(parser,openEntityNames,base,systemId,publicId); + } + static int call_callbackUnknownEncoding(void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info) + { + return SaxExpatParser_Impl::callbackUnknownEncoding(encodingHandlerData,name,info); + } + static void call_callbackDefault( void *userData, const XML_Char *s, int len) + { + SaxExpatParser_Impl::callbackDefault(userData,s,len); + } + static void call_callbackStartCDATA( void *userData ) + { + SaxExpatParser_Impl::callbackStartCDATA(userData); + } + static void call_callbackEndCDATA( void *userData ) + { + SaxExpatParser_Impl::callbackEndCDATA(userData); + } + static void call_callbackComment( void *userData , const XML_Char *s ) + { + SaxExpatParser_Impl::callbackComment(userData,s); + } +} + + +//--------------------------------------------- +// LocatorImpl +//--------------------------------------------- +class LocatorImpl : + public WeakImplHelper2< XLocator, com::sun::star::io::XSeekable > + // should use a different interface for stream positions! +{ +public: + LocatorImpl( SaxExpatParser_Impl *p ) + { + m_pParser = p; + } + +public: //XLocator + virtual sal_Int32 SAL_CALL getColumnNumber(void) throw () + { + return XML_GetCurrentColumnNumber( m_pParser->getEntity().pParser ); + } + virtual sal_Int32 SAL_CALL getLineNumber(void) throw () + { + return XML_GetCurrentLineNumber( m_pParser->getEntity().pParser ); + } + virtual OUString SAL_CALL getPublicId(void) throw () + { + return m_pParser->getEntity().structSource.sPublicId; + } + virtual OUString SAL_CALL getSystemId(void) throw () + { + return m_pParser->getEntity().structSource.sSystemId; + } + + // XSeekable (only for getPosition) + + virtual void SAL_CALL seek( sal_Int64 ) throw() + { + } + virtual sal_Int64 SAL_CALL getPosition() throw() + { + return XML_GetCurrentByteIndex( m_pParser->getEntity().pParser ); + } + virtual ::sal_Int64 SAL_CALL getLength() throw() + { + return 0; + } + +private: + + SaxExpatParser_Impl *m_pParser; +}; + + + + +SaxExpatParser::SaxExpatParser( ) +{ + m_pImpl = new SaxExpatParser_Impl; + + LocatorImpl *pLoc = new LocatorImpl( m_pImpl ); + m_pImpl->rDocumentLocator = Reference< XLocator > ( pLoc ); + + // performance-Improvment. Reference is needed when calling the startTag callback. + // Handing out the same object with every call is allowed (see sax-specification) + m_pImpl->pAttrList = new AttributeList; + m_pImpl->rAttrList = Reference< XAttributeList > ( m_pImpl->pAttrList ); + + m_pImpl->bExceptionWasThrown = sal_False; + m_pImpl->bRTExceptionWasThrown = sal_False; +} + +SaxExpatParser::~SaxExpatParser() +{ + delete m_pImpl; +} + + +/*************** +* +* parseStream does Parser-startup initializations. The SaxExpatParser_Impl::parse() method does +* the file-specific initialization work. (During a parser run, external files may be opened) +* +****************/ +void SaxExpatParser::parseStream( const InputSource& structSource) + throw (SAXException, + IOException, + RuntimeException) +{ + // Only one text at one time + MutexGuard guard( m_pImpl->aMutex ); + + + struct Entity entity; + entity.structSource = structSource; + + if( ! entity.structSource.aInputStream.is() ) + { + throw SAXException( OUString::createFromAscii( "No input source" ) , + Reference< XInterface > () , Any() ); + } + + entity.converter.setInputStream( entity.structSource.aInputStream ); + if( entity.structSource.sEncoding.getLength() ) + { + entity.converter.setEncoding( + OUStringToOString( entity.structSource.sEncoding , RTL_TEXTENCODING_ASCII_US ) ); + } + + // create parser with proper encoding + entity.pParser = XML_ParserCreate( 0 ); + if( ! entity.pParser ) + { + throw SAXException( OUString::createFromAscii( "Couldn't create parser" ) , + Reference< XInterface > (), Any() ); + } + + // set all necessary C-Callbacks + XML_SetUserData( entity.pParser , m_pImpl ); + XML_SetElementHandler( entity.pParser , + call_callbackStartElement , + call_callbackEndElement ); + XML_SetCharacterDataHandler( entity.pParser , call_callbackCharacters ); + XML_SetProcessingInstructionHandler(entity.pParser , + call_callbackProcessingInstruction ); + XML_SetUnparsedEntityDeclHandler( entity.pParser, + call_callbackUnparsedEntityDecl ); + XML_SetNotationDeclHandler( entity.pParser, call_callbackNotationDecl ); + XML_SetExternalEntityRefHandler( entity.pParser, + call_callbackExternalEntityRef); + XML_SetUnknownEncodingHandler( entity.pParser, call_callbackUnknownEncoding ,0); + + if( m_pImpl->rExtendedDocumentHandler.is() ) { + + // These handlers just delegate calls to the ExtendedHandler. If no extended handler is + // given, these callbacks can be ignored + XML_SetDefaultHandlerExpand( entity.pParser, call_callbackDefault ); + XML_SetCommentHandler( entity.pParser, call_callbackComment ); + XML_SetCdataSectionHandler( entity.pParser , + call_callbackStartCDATA , + call_callbackEndCDATA ); + } + + + m_pImpl->exception = SAXParseException(); + m_pImpl->pushEntity( entity ); + try + { + // start the document + if( m_pImpl->rDocumentHandler.is() ) { + m_pImpl->rDocumentHandler->setDocumentLocator( m_pImpl->rDocumentLocator ); + m_pImpl->rDocumentHandler->startDocument(); + } + + m_pImpl->parse(); + + // finish document + if( m_pImpl->rDocumentHandler.is() ) { + m_pImpl->rDocumentHandler->endDocument(); + } + } +// catch( SAXParseException &e ) +// { +// m_pImpl->popEntity(); +// XML_ParserFree( entity.pParser ); +// Any aAny; +// aAny <<= e; +// throw SAXException( e.Message, e.Context, aAny ); +// } + catch( SAXException & ) + { + m_pImpl->popEntity(); + XML_ParserFree( entity.pParser ); + throw; + } + catch( IOException & ) + { + m_pImpl->popEntity(); + XML_ParserFree( entity.pParser ); + throw; + } + catch( RuntimeException & ) + { + m_pImpl->popEntity(); + XML_ParserFree( entity.pParser ); + throw; + } + + m_pImpl->popEntity(); + XML_ParserFree( entity.pParser ); +} + +void SaxExpatParser::setDocumentHandler(const Reference< XDocumentHandler > & xHandler) + throw (RuntimeException) +{ + m_pImpl->rDocumentHandler = xHandler; + m_pImpl->rExtendedDocumentHandler = + Reference< XExtendedDocumentHandler >( xHandler , UNO_QUERY ); +} + +void SaxExpatParser::setErrorHandler(const Reference< XErrorHandler > & xHandler) + throw (RuntimeException) +{ + m_pImpl->rErrorHandler = xHandler; +} + +void SaxExpatParser::setDTDHandler(const Reference< XDTDHandler > & xHandler) + throw (RuntimeException) +{ + m_pImpl->rDTDHandler = xHandler; +} + +void SaxExpatParser::setEntityResolver(const Reference < XEntityResolver > & xResolver) + throw (RuntimeException) +{ + m_pImpl->rEntityResolver = xResolver; +} + + +void SaxExpatParser::setLocale( const Locale & locale ) throw (RuntimeException) +{ + m_pImpl->locale = locale; +} + +// XServiceInfo +OUString SaxExpatParser::getImplementationName() throw () +{ + return OUString::createFromAscii( IMPLEMENTATION_NAME ); +} + +// XServiceInfo +sal_Bool SaxExpatParser::supportsService(const OUString& ServiceName) throw () +{ + Sequence< OUString > aSNL = getSupportedServiceNames(); + const OUString * pArray = aSNL.getConstArray(); + + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + + return sal_False; +} + +// XServiceInfo +Sequence< OUString > SaxExpatParser::getSupportedServiceNames(void) throw () +{ + + Sequence<OUString> seq(1); + seq.getArray()[0] = OUString::createFromAscii( SERVICE_NAME ); + return seq; +} + + +/*--------------------------------------- +* +* Helper functions and classes +* +* +*-------------------------------------------*/ +OUString getErrorMessage( XML_Error xmlE, OUString sSystemId , sal_Int32 nLine ) +{ + OUString Message; + if( XML_ERROR_NONE == xmlE ) { + Message = OUString::createFromAscii( "No" ); + } + else if( XML_ERROR_NO_MEMORY == xmlE ) { + Message = OUString::createFromAscii( "no memory" ); + } + else if( XML_ERROR_SYNTAX == xmlE ) { + Message = OUString::createFromAscii( "syntax" ); + } + else if( XML_ERROR_NO_ELEMENTS == xmlE ) { + Message = OUString::createFromAscii( "no elements" ); + } + else if( XML_ERROR_INVALID_TOKEN == xmlE ) { + Message = OUString::createFromAscii( "invalid token" ); + } + else if( XML_ERROR_UNCLOSED_TOKEN == xmlE ) { + Message = OUString::createFromAscii( "unclosed token" ); + } + else if( XML_ERROR_PARTIAL_CHAR == xmlE ) { + Message = OUString::createFromAscii( "partial char" ); + } + else if( XML_ERROR_TAG_MISMATCH == xmlE ) { + Message = OUString::createFromAscii( "tag mismatch" ); + } + else if( XML_ERROR_DUPLICATE_ATTRIBUTE == xmlE ) { + Message = OUString::createFromAscii( "duplicate attribute" ); + } + else if( XML_ERROR_JUNK_AFTER_DOC_ELEMENT == xmlE ) { + Message = OUString::createFromAscii( "junk after doc element" ); + } + else if( XML_ERROR_PARAM_ENTITY_REF == xmlE ) { + Message = OUString::createFromAscii( "parameter entity reference" ); + } + else if( XML_ERROR_UNDEFINED_ENTITY == xmlE ) { + Message = OUString::createFromAscii( "undefined entity" ); + } + else if( XML_ERROR_RECURSIVE_ENTITY_REF == xmlE ) { + Message = OUString::createFromAscii( "recursive entity reference" ); + } + else if( XML_ERROR_ASYNC_ENTITY == xmlE ) { + Message = OUString::createFromAscii( "async entity" ); + } + else if( XML_ERROR_BAD_CHAR_REF == xmlE ) { + Message = OUString::createFromAscii( "bad char reference" ); + } + else if( XML_ERROR_BINARY_ENTITY_REF == xmlE ) { + Message = OUString::createFromAscii( "binary entity reference" ); + } + else if( XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF == xmlE ) { + Message = OUString::createFromAscii( "attribute external entity reference" ); + } + else if( XML_ERROR_MISPLACED_XML_PI == xmlE ) { + Message = OUString::createFromAscii( "misplaced xml processing instruction" ); + } + else if( XML_ERROR_UNKNOWN_ENCODING == xmlE ) { + Message = OUString::createFromAscii( "unknown encoding" ); + } + else if( XML_ERROR_INCORRECT_ENCODING == xmlE ) { + Message = OUString::createFromAscii( "incorrect encoding" ); + } + else if( XML_ERROR_UNCLOSED_CDATA_SECTION == xmlE ) { + Message = OUString::createFromAscii( "unclosed cdata section" ); + } + else if( XML_ERROR_EXTERNAL_ENTITY_HANDLING == xmlE ) { + Message = OUString::createFromAscii( "external entity reference" ); + } + else if( XML_ERROR_NOT_STANDALONE == xmlE ) { + Message = OUString::createFromAscii( "not standalone" ); + } + + OUString str = OUString::createFromAscii( "[" ); + str += sSystemId; + str += OUString::createFromAscii( " line " ); + str += OUString::valueOf( nLine ); + str += OUString::createFromAscii( "]: " ); + str += Message; + str += OUString::createFromAscii( "error" ); + + return str; +} + + +// starts parsing with actual parser ! +void SaxExpatParser_Impl::parse( ) +{ + const int nBufSize = 16*1024; + + int nRead = nBufSize; + Sequence< sal_Int8 > seqOut(nBufSize); + + while( nRead ) { + nRead = getEntity().converter.readAndConvert( seqOut , nBufSize ); + + if( ! nRead ) { + XML_Parse( getEntity().pParser , + ( const char * ) seqOut.getArray() , + 0 , + 1 ); + break; + } + + sal_Bool bContinue = ( XML_Parse( getEntity().pParser , + (const char *) seqOut.getArray(), + nRead, + 0 ) != 0 ); + + if( ! bContinue || this->bExceptionWasThrown ) { + + if ( this->bRTExceptionWasThrown ) + throw rtexception; + + // Error during parsing ! + XML_Error xmlE = XML_GetErrorCode( getEntity().pParser ); + OUString sSystemId = rDocumentLocator->getSystemId(); + sal_Int32 nLine = rDocumentLocator->getLineNumber(); + + SAXParseException aExcept( + getErrorMessage(xmlE , sSystemId, nLine) , + Reference< XInterface >(), + Any( &exception , getCppuType( &exception) ), + rDocumentLocator->getPublicId(), + rDocumentLocator->getSystemId(), + rDocumentLocator->getLineNumber(), + rDocumentLocator->getColumnNumber() + ); + + if( rErrorHandler.is() ) { + + // error handler is set, so the handler may throw the exception + Any a; + a <<= aExcept; + rErrorHandler->fatalError( a ); + } + + // Error handler has not thrown an exception, but parsing cannot go on, + // so an exception MUST be thrown. + throw aExcept; + } // if( ! bContinue ) + } // while +} + +//------------------------------------------ +// +// The C-Callbacks +// +//----------------------------------------- +void SaxExpatParser_Impl::callbackStartElement( void *pvThis , + const XML_Char *pwName , + const XML_Char **awAttributes ) +{ + // in case of two concurrent threads, there is only the danger of an leak, + // which is neglectable for one string + static OUString g_CDATA( RTL_CONSTASCII_USTRINGPARAM( "CDATA" ) ); + + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + + if( pImpl->rDocumentHandler.is() ) { + + int i = 0; + pImpl->pAttrList->clear(); + + while( awAttributes[i] ) { + OSL_ASSERT( awAttributes[i+1] ); + pImpl->pAttrList->addAttribute( + XML_CHAR_TO_OUSTRING( awAttributes[i] ) , + g_CDATA , // expat doesn't know types + XML_CHAR_TO_OUSTRING( awAttributes[i+1] ) ); + i +=2; + } + + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( + pImpl , + rDocumentHandler->startElement( XML_CHAR_TO_OUSTRING( pwName ) , + pImpl->rAttrList ) ); + } +} + +void SaxExpatParser_Impl::callbackEndElement( void *pvThis , const XML_Char *pwName ) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + + if( pImpl->rDocumentHandler.is() ) { + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( pImpl, + rDocumentHandler->endElement( XML_CHAR_TO_OUSTRING( pwName ) ) ); + } +} + + +void SaxExpatParser_Impl::callbackCharacters( void *pvThis , const XML_Char *s , int nLen ) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + + if( pImpl->rDocumentHandler.is() ) { + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( pImpl , + rDocumentHandler->characters( XML_CHAR_N_TO_USTRING(s,nLen) ) ); + } +} + +void SaxExpatParser_Impl::callbackProcessingInstruction( void *pvThis, + const XML_Char *sTarget , + const XML_Char *sData ) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + if( pImpl->rDocumentHandler.is() ) { + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( + pImpl , + rDocumentHandler->processingInstruction( XML_CHAR_TO_OUSTRING( sTarget ), + XML_CHAR_TO_OUSTRING( sData ) ) ); + } +} + + +void SaxExpatParser_Impl::callbackUnparsedEntityDecl(void *pvThis , + const XML_Char *entityName, + const XML_Char * /*base*/, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + if( pImpl->rDTDHandler.is() ) { + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( + pImpl , + rDTDHandler->unparsedEntityDecl( + XML_CHAR_TO_OUSTRING( entityName ), + XML_CHAR_TO_OUSTRING( publicId ) , + XML_CHAR_TO_OUSTRING( systemId ) , + XML_CHAR_TO_OUSTRING( notationName ) ) ); + } +} + +void SaxExpatParser_Impl::callbackNotationDecl( void *pvThis, + const XML_Char *notationName, + const XML_Char * /*base*/, + const XML_Char *systemId, + const XML_Char *publicId) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + if( pImpl->rDTDHandler.is() ) { + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( pImpl, + rDTDHandler->notationDecl( XML_CHAR_TO_OUSTRING( notationName ) , + XML_CHAR_TO_OUSTRING( publicId ) , + XML_CHAR_TO_OUSTRING( systemId ) ) ); + } + +} + + + +int SaxExpatParser_Impl::callbackExternalEntityRef( XML_Parser parser, + const XML_Char *context, + const XML_Char * /*base*/, + const XML_Char *systemId, + const XML_Char *publicId) +{ + sal_Bool bOK = sal_True; + InputSource source; + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)XML_GetUserData( parser )); + + struct Entity entity; + + if( pImpl->rEntityResolver.is() ) { + try + { + entity.structSource = pImpl->rEntityResolver->resolveEntity( + XML_CHAR_TO_OUSTRING( publicId ) , + XML_CHAR_TO_OUSTRING( systemId ) ); + } + catch( SAXParseException & e ) + { + pImpl->exception = e; + bOK = sal_False; + } + catch( SAXException & e ) + { + pImpl->exception = SAXParseException( + e.Message , e.Context , e.WrappedException , + pImpl->rDocumentLocator->getPublicId(), + pImpl->rDocumentLocator->getSystemId(), + pImpl->rDocumentLocator->getLineNumber(), + pImpl->rDocumentLocator->getColumnNumber() ); + bOK = sal_False; + } + } + + if( entity.structSource.aInputStream.is() ) { + entity.pParser = XML_ExternalEntityParserCreate( parser , context, 0 ); + if( ! entity.pParser ) + { + return sal_False; + } + + entity.converter.setInputStream( entity.structSource.aInputStream ); + pImpl->pushEntity( entity ); + try + { + pImpl->parse(); + } + catch( SAXParseException & e ) + { + pImpl->exception = e; + bOK = sal_False; + } + catch( IOException &e ) + { + pImpl->exception.WrappedException <<= e; + bOK = sal_False; + } + catch( RuntimeException &e ) + { + pImpl->exception.WrappedException <<=e; + bOK = sal_False; + } + + pImpl->popEntity(); + + XML_ParserFree( entity.pParser ); + } + + return bOK; +} + +int SaxExpatParser_Impl::callbackUnknownEncoding(void * /*encodingHandlerData*/, + const XML_Char * /*name*/, + XML_Encoding * /*info*/) +{ + return 0; +} + +void SaxExpatParser_Impl::callbackDefault( void *pvThis, const XML_Char *s, int len) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( pImpl, + rExtendedDocumentHandler->unknown( XML_CHAR_N_TO_USTRING( s ,len) ) ); +} + +void SaxExpatParser_Impl::callbackComment( void *pvThis , const XML_Char *s ) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( pImpl, + rExtendedDocumentHandler->comment( XML_CHAR_TO_OUSTRING( s ) ) ); +} + +void SaxExpatParser_Impl::callbackStartCDATA( void *pvThis ) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS( pImpl, rExtendedDocumentHandler->startCDATA() ); +} + + +void SaxExpatParser_Impl::callErrorHandler( SaxExpatParser_Impl *pImpl , + const SAXParseException & e ) +{ + try + { + if( pImpl->rErrorHandler.is() ) { + Any a; + a <<= e; + pImpl->rErrorHandler->error( a ); + } + else { + pImpl->exception = e; + pImpl->bExceptionWasThrown = sal_True; + } + } + catch( SAXParseException & ex ) { + pImpl->exception = ex; + pImpl->bExceptionWasThrown = sal_True; + } + catch( SAXException & ex ) { + pImpl->exception = SAXParseException( + ex.Message, + ex.Context, + ex.WrappedException, + pImpl->rDocumentLocator->getPublicId(), + pImpl->rDocumentLocator->getSystemId(), + pImpl->rDocumentLocator->getLineNumber(), + pImpl->rDocumentLocator->getColumnNumber() + ); + pImpl->bExceptionWasThrown = sal_True; + } +} + +void SaxExpatParser_Impl::callbackEndCDATA( void *pvThis ) +{ + SaxExpatParser_Impl *pImpl = ((SaxExpatParser_Impl*)pvThis); + + CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS(pImpl,rExtendedDocumentHandler->endCDATA() ); +} + +} +using namespace sax_expatwrap; + +extern "C" +{ + +void SAL_CALL component_getImplementationEnvironment( + const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ ) +{ + *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +} + +void * SAL_CALL component_getFactory( + const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) +{ + void * pRet = 0; + + if (pServiceManager ) + { + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > xSMgr = + reinterpret_cast< XMultiServiceFactory * > ( pServiceManager ); + + OUString aImplementationName = OUString::createFromAscii( pImplName ); + + if (aImplementationName == + OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ) ) + { + xRet = createSingleFactory( xSMgr, aImplementationName, + SaxExpatParser_CreateInstance, + SaxExpatParser::getSupportedServiceNames_Static() ); + } + else if ( aImplementationName == SaxWriter_getImplementationName() ) + { + xRet = createSingleFactory( xSMgr, aImplementationName, + SaxWriter_CreateInstance, + SaxWriter_getSupportedServiceNames() ); + } + + if (xRet.is()) + { + xRet->acquire(); + pRet = xRet.get(); + } + } + + return pRet; +} + + +} + diff --git a/sax/source/expatwrap/saxwriter.cxx b/sax/source/expatwrap/saxwriter.cxx new file mode 100644 index 000000000000..92d53700aa86 --- /dev/null +++ b/sax/source/expatwrap/saxwriter.cxx @@ -0,0 +1,1454 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include <string.h> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/xml/sax/SAXInvalidCharacterException.hpp> + +#include <com/sun/star/io/XActiveDataSource.hpp> + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase3.hxx> + +#include <rtl/strbuf.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace ::rtl; +using namespace ::std; +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; + +#include "factory.hxx" +#include "xml2utf.hxx" + +#define LINEFEED 10 +#define SEQUENCESIZE 1024 +#define MAXCOLUMNCOUNT 72 + +/****** +* +* +* Character conversion functions +* +* +*****/ + +namespace sax_expatwrap { +/***** +* +* Calculates the length of the sequence after conversion, but the conversion is not done. +* .g. &<>"' plus some more are +* special characters in XML that need to be transformed +* +* @param bConvertAll For Attributes it is necessary to convert every symbol (including line feed and tab) +* Set this to true, if you want to perform this special conversion +* @return The returned value is equal to the length of the incoming sequence, when no ++ conversion is necessary, otherwise it is larger than the length of the sequence. +****/ +// inline sal_Int32 CalcXMLLen( const Sequence<sal_Int8> & seq , sal_Bool bConvertAll ) throw() +// { +// sal_Int32 nLen = 0; +// const sal_Int8 *pArray = seq.getConstArray(); + +// for( int i = 0 ; i < seq.getLength() ; i ++ ) { + +// sal_Int8 c = pArray[i]; +// switch( c ) +// { +// case '&': // resemble to & +// nLen +=5; +// break; +// case '<': // < +// case '>': // > +// nLen +=4; +// break; +// case 39: // 39 == ''', ' +// case '"': // " +// case 13: // 
 +// nLen += 6; +// break; + +// case 10: // 
 +// case 9: // 	 +// if( bConvertAll ) +// { +// nLen += 6; // +// } +// break; +// default: +// nLen ++; +// } +// } + +// return nLen; +// } + +enum SaxInvalidCharacterError +{ + SAX_NONE, + SAX_WARNING, + SAX_ERROR +}; + +class SaxWriterHelper +{ + Reference< XOutputStream > m_out; + Sequence < sal_Int8 > m_Sequence; + sal_Int8* mp_Sequence; + + sal_Int32 nLastLineFeedPos; // is negative after writing a sequence + sal_uInt32 nCurrentPos; + sal_Bool m_bStartElementFinished; + + + inline sal_uInt32 writeSequence() throw( SAXException ); + + // use only if to insert the bytes more space in the sequence is needed and + // so the sequence has to write out and reset rPos to 0 + // writes sequence only on overflow, sequence could be full on the end (rPos == SEQUENCESIZE) + inline void AddBytes(sal_Int8* pTarget, sal_uInt32& rPos, + const sal_Int8* pBytes, sal_uInt32 nBytesCount) throw( SAXException ); + inline sal_Bool convertToXML(const sal_Unicode * pStr, + sal_Int32 nStrLen, + sal_Bool bDoNormalization, + sal_Bool bNormalizeWhitespace, + sal_Int8 *pTarget, + sal_uInt32& rPos) throw( SAXException ); + inline void FinishStartElement() throw( SAXException ); +public: + SaxWriterHelper(Reference< XOutputStream > m_TempOut) : + m_out(m_TempOut), + m_Sequence(SEQUENCESIZE), + mp_Sequence(NULL), + nLastLineFeedPos(0), + nCurrentPos(0), + m_bStartElementFinished(sal_True) + { + OSL_ENSURE(SEQUENCESIZE > 50, "Sequence cache size to small"); + mp_Sequence = m_Sequence.getArray(); + } + ~SaxWriterHelper() + { + OSL_ENSURE(!nCurrentPos, "cached Sequence not written"); + OSL_ENSURE(m_bStartElementFinished, "StartElement not complettly written"); + } + + inline void insertIndentation(sal_uInt32 m_nLevel) throw( SAXException ); + +// returns whether it works correct or invalid characters were in the string +// If there are invalid characters in the string it returns sal_False. +// Than the calling method has to throw the needed Exception. + inline sal_Bool writeString(const rtl::OUString& rWriteOutString, + sal_Bool bDoNormalization, + sal_Bool bNormalizeWhitespace) throw( SAXException ); + + sal_uInt32 GetLastColumnCount() { return (sal_uInt32)(nCurrentPos - nLastLineFeedPos); } + + inline void startDocument() throw( SAXException ); + +// returns whether it works correct or invalid characters were in the strings +// If there are invalid characters in one of the strings it returns sal_False. +// Than the calling method has to throw the needed Exception. + inline SaxInvalidCharacterError startElement(const rtl::OUString& rName, const Reference< XAttributeList >& xAttribs) throw( SAXException ); + inline sal_Bool FinishEmptyElement() throw( SAXException ); + +// returns whether it works correct or invalid characters were in the string +// If there are invalid characters in the string it returns sal_False. +// Than the calling method has to throw the needed Exception. + inline sal_Bool endElement(const rtl::OUString& rName) throw( SAXException ); + inline void endDocument() throw( SAXException ); + +// returns whether it works correct or invalid characters were in the strings +// If there are invalid characters in the string it returns sal_False. +// Than the calling method has to throw the needed Exception. + inline sal_Bool processingInstruction(const rtl::OUString& rTarget, const rtl::OUString& rData) throw( SAXException ); + inline void startCDATA() throw( SAXException ); + inline void endCDATA() throw( SAXException ); + +// returns whether it works correct or invalid characters were in the strings +// If there are invalid characters in the string it returns sal_False. +// Than the calling method has to throw the needed Exception. + inline sal_Bool comment(const rtl::OUString& rComment) throw( SAXException ); + + inline void clearBuffer() throw( SAXException ); +}; + +const sal_Bool g_bValidCharsBelow32[32] = +{ +// 0 1 2 3 4 5 6 7 + 0,0,0,0,0,0,0,0, //0 + 0,1,1,0,0,1,0,0, //8 + 0,0,0,0,0,0,0,0, //16 + 0,0,0,0,0,0,0,0 +}; + +inline sal_Bool IsInvalidChar(const sal_Unicode aChar) +{ + sal_Bool bRet(sal_False); + // check first for the most common characters + if( aChar < 32 || aChar >= 0xd800 ) + bRet = ( (aChar < 32 && ! g_bValidCharsBelow32[aChar]) || + aChar == 0xffff || + aChar == 0xfffe ); + return bRet; +} + +/******** +* write through to the output stream +* +*****/ +inline sal_uInt32 SaxWriterHelper::writeSequence() throw( SAXException ) +{ + try + { + m_out->writeBytes( m_Sequence ); + } + catch( IOException & e ) + { + Any a; + a <<= e; + throw SAXException( + OUString::createFromAscii( "io exception during writing" ), + Reference< XInterface > (), + a ); + } + nLastLineFeedPos -= SEQUENCESIZE; + return 0; +} + +inline void SaxWriterHelper::AddBytes(sal_Int8* pTarget, sal_uInt32& rPos, + const sal_Int8* pBytes, sal_uInt32 nBytesCount) throw( SAXException ) +{ + OSL_ENSURE((rPos + nBytesCount) > SEQUENCESIZE, "wrong use of AddBytesMethod"); + sal_uInt32 nCount(SEQUENCESIZE - rPos); + memcpy( &(pTarget[rPos]) , pBytes, nCount); + + OSL_ENSURE(rPos + nCount == SEQUENCESIZE, "the position should be the at the end"); + + rPos = writeSequence(); + sal_uInt32 nRestCount(nBytesCount - nCount); + if ((rPos + nRestCount) <= SEQUENCESIZE) + { + memcpy( &(pTarget[rPos]), &pBytes[nCount], nRestCount); + rPos += nRestCount; + } + else + AddBytes(pTarget, rPos, &pBytes[nCount], nRestCount); +} + +/** Converts an UTF16 string to UTF8 and does XML normalization + + @param pTarget + Pointer to a piece of memory, to where the output should be written. The caller + must call calcXMLByteLength on the same string, to ensure, + that there is enough memory for converting. + */ +inline sal_Bool SaxWriterHelper::convertToXML( const sal_Unicode * pStr, + sal_Int32 nStrLen, + sal_Bool bDoNormalization, + sal_Bool bNormalizeWhitespace, + sal_Int8 *pTarget, + sal_uInt32& rPos ) throw( SAXException ) +{ + sal_Bool bRet(sal_True); + sal_uInt32 nSurrogate = 0; + + for( sal_Int32 i = 0 ; i < nStrLen ; i ++ ) + { + sal_uInt16 c = pStr[i]; + if (IsInvalidChar(c)) + bRet = sal_False; + else if( (c >= 0x0001) && (c <= 0x007F) ) + { + if( bDoNormalization ) + { + switch( c ) + { + case '&': // resemble to & + { + if ((rPos + 5) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)"&", 5); + else + { + memcpy( &(pTarget[rPos]) , "&", 5 ); + rPos += 5; + } + } + break; + case '<': + { + if ((rPos + 4) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)"<", 4); + else + { + memcpy( &(pTarget[rPos]) , "<" , 4 ); + rPos += 4; // < + } + } + break; + case '>': + { + if ((rPos + 4) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)">", 4); + else + { + memcpy( &(pTarget[rPos]) , ">" , 4 ); + rPos += 4; // > + } + } + break; + case 39: // 39 == ''' + { + if ((rPos + 6) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)"'", 6); + else + { + memcpy( &(pTarget[rPos]) , "'" , 6 ); + rPos += 6; // ' + } + } + break; + case '"': + { + if ((rPos + 6) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)""", 6); + else + { + memcpy( &(pTarget[rPos]) , """ , 6 ); + rPos += 6; // " + } + } + break; + case 13: + { + if ((rPos + 6) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)"
", 6); + else + { + memcpy( &(pTarget[rPos]) , "
" , 6 ); + rPos += 6; + } + } + break; + case LINEFEED: + { + if( bNormalizeWhitespace ) + { + if ((rPos + 6) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)"
" , 6); + else + { + memcpy( &(pTarget[rPos]) , "
" , 6 ); + rPos += 6; + } + } + else + { + pTarget[rPos] = LINEFEED; + nLastLineFeedPos = rPos; + rPos ++; + } + } + break; + case 9: + { + if( bNormalizeWhitespace ) + { + if ((rPos + 6) > SEQUENCESIZE) + AddBytes(pTarget, rPos, (sal_Int8*)"	" , 6); + else + { + memcpy( &(pTarget[rPos]) , "	" , 6 ); + rPos += 6; + } + } + else + { + pTarget[rPos] = 9; + rPos ++; + } + } + break; + default: + { + pTarget[rPos] = (sal_Int8)c; + rPos ++; + } + break; + } + } + else + { + pTarget[rPos] = (sal_Int8)c; + if ((sal_Int8)c == LINEFEED) + nLastLineFeedPos = rPos; + rPos ++; + } + } + else if( c >= 0xd800 && c < 0xdc00 ) + { + // 1. surrogate: save (until 2. surrogate) + OSL_ENSURE( nSurrogate == 0, "left-over Unicode surrogate" ); + nSurrogate = ( ( c & 0x03ff ) + 0x0040 ); + } + else if( c >= 0xdc00 && c < 0xe000 ) + { + // 2. surrogate: write as UTF-8 + OSL_ENSURE( nSurrogate != 0, "lone 2nd Unicode surrogate" ); + + nSurrogate = ( nSurrogate << 10 ) | ( c & 0x03ff ); + if( nSurrogate >= 0x00010000 && nSurrogate <= 0x0010FFFF ) + { + sal_Int8 aBytes[] = { sal_Int8(0xF0 | ((nSurrogate >> 18) & 0x0F)), + sal_Int8(0x80 | ((nSurrogate >> 12) & 0x3F)), + sal_Int8(0x80 | ((nSurrogate >> 6) & 0x3F)), + sal_Int8(0x80 | ((nSurrogate >> 0) & 0x3F)) }; + if ((rPos + 4) > SEQUENCESIZE) + AddBytes(pTarget, rPos, aBytes, 4); + else + { + pTarget[rPos] = aBytes[0]; + rPos ++; + pTarget[rPos] = aBytes[1]; + rPos ++; + pTarget[rPos] = aBytes[2]; + rPos ++; + pTarget[rPos] = aBytes[3]; + rPos ++; + } + } + else + { + OSL_ENSURE( false, "illegal Unicode character" ); + bRet = sal_False; + } + + // reset surrogate + nSurrogate = 0; + } + else if( c > 0x07FF ) + { + sal_Int8 aBytes[] = { sal_Int8(0xE0 | ((c >> 12) & 0x0F)), + sal_Int8(0x80 | ((c >> 6) & 0x3F)), + sal_Int8(0x80 | ((c >> 0) & 0x3F)) }; + if ((rPos + 3) > SEQUENCESIZE) + AddBytes(pTarget, rPos, aBytes, 3); + else + { + pTarget[rPos] = aBytes[0]; + rPos ++; + pTarget[rPos] = aBytes[1]; + rPos ++; + pTarget[rPos] = aBytes[2]; + rPos ++; + } + } + else + { + sal_Int8 aBytes[] = { sal_Int8(0xC0 | ((c >> 6) & 0x1F)), + sal_Int8(0x80 | ((c >> 0) & 0x3F)) }; + if ((rPos + 2) > SEQUENCESIZE) + AddBytes(pTarget, rPos, aBytes, 2); + else + { + pTarget[rPos] = aBytes[0]; + rPos ++; + pTarget[rPos] = aBytes[1]; + rPos ++; + } + } + OSL_ENSURE(rPos <= SEQUENCESIZE, "not reset current position"); + if (rPos == SEQUENCESIZE) + rPos = writeSequence(); + + // reset left-over surrogate + if( ( nSurrogate != 0 ) && !( c >= 0xd800 && c < 0xdc00 ) ) + { + OSL_ENSURE( nSurrogate != 0, "left-over Unicode surrogate" ); + nSurrogate = 0; + bRet = sal_False; + } + } + return bRet; +} + +inline void SaxWriterHelper::FinishStartElement() throw( SAXException ) +{ + if (!m_bStartElementFinished) + { + mp_Sequence[nCurrentPos] = '>'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + m_bStartElementFinished = sal_True; + } +} + +inline void SaxWriterHelper::insertIndentation(sal_uInt32 m_nLevel) throw( SAXException ) +{ + FinishStartElement(); + if (m_nLevel > 0) + { + if ((nCurrentPos + m_nLevel + 1) <= SEQUENCESIZE) + { + mp_Sequence[nCurrentPos] = LINEFEED; + nLastLineFeedPos = nCurrentPos; + nCurrentPos++; + memset( &(mp_Sequence[nCurrentPos]) , 32 , m_nLevel ); + nCurrentPos += m_nLevel; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + } + else + { + sal_uInt32 nCount(m_nLevel + 1); + sal_Int8* pBytes = new sal_Int8[nCount]; + pBytes[0] = LINEFEED; + memset( &(pBytes[1]), 32, m_nLevel ); + AddBytes(mp_Sequence, nCurrentPos, pBytes, nCount); + delete[] pBytes; + nLastLineFeedPos = nCurrentPos - nCount; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + } + } + else + { + mp_Sequence[nCurrentPos] = LINEFEED; + nLastLineFeedPos = nCurrentPos; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + } +} + +inline sal_Bool SaxWriterHelper::writeString( const rtl::OUString& rWriteOutString, + sal_Bool bDoNormalization, + sal_Bool bNormalizeWhitespace ) throw( SAXException ) +{ + FinishStartElement(); + return convertToXML(rWriteOutString.getStr(), + rWriteOutString.getLength(), + bDoNormalization, + bNormalizeWhitespace, + mp_Sequence, + nCurrentPos); +} + +inline void SaxWriterHelper::startDocument() throw( SAXException ) +{ + const char pc[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + const int nLen = strlen( pc ); + if ((nCurrentPos + nLen) <= SEQUENCESIZE) + { + memcpy( mp_Sequence, pc , nLen ); + nCurrentPos += nLen; + } + else + { + AddBytes(mp_Sequence, nCurrentPos, (sal_Int8*)pc, nLen); + } + OSL_ENSURE(nCurrentPos <= SEQUENCESIZE, "not reset current position"); + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = LINEFEED; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); +} + +inline SaxInvalidCharacterError SaxWriterHelper::startElement(const rtl::OUString& rName, const Reference< XAttributeList >& xAttribs) throw( SAXException ) +{ + FinishStartElement(); + mp_Sequence[nCurrentPos] = '<'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + SaxInvalidCharacterError eRet(SAX_NONE); + if (!writeString(rName, sal_False, sal_False)) + eRet = SAX_ERROR; + + sal_Int16 nAttribCount = xAttribs.is() ? static_cast<sal_Int16>(xAttribs->getLength()) : 0; + for(sal_Int16 i = 0 ; i < nAttribCount ; i++ ) + { + mp_Sequence[nCurrentPos] = ' '; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + if (!writeString(xAttribs->getNameByIndex( i ), sal_False, sal_False)) + eRet = SAX_ERROR; + + mp_Sequence[nCurrentPos] = '='; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '"'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + if (!writeString(xAttribs->getValueByIndex( i ), sal_True, sal_True) && + !(eRet == SAX_ERROR)) + eRet = SAX_WARNING; + + mp_Sequence[nCurrentPos] = '"'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + } + + m_bStartElementFinished = sal_False; // because the '>' character is not added, + // because it is possible, that the "/>" + // characters have to add + return eRet; +} + +inline sal_Bool SaxWriterHelper::FinishEmptyElement() throw( SAXException ) +{ + if (m_bStartElementFinished) + return sal_False; + + mp_Sequence[nCurrentPos] = '/'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '>'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + m_bStartElementFinished = sal_True; + + return sal_True; +} + +inline sal_Bool SaxWriterHelper::endElement(const rtl::OUString& rName) throw( SAXException ) +{ + FinishStartElement(); + mp_Sequence[nCurrentPos] = '<'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '/'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + sal_Bool bRet(writeString( rName, sal_False, sal_False)); + + mp_Sequence[nCurrentPos] = '>'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + return bRet; +} + +inline void SaxWriterHelper::endDocument() throw( SAXException ) +{ + if (nCurrentPos > 0) + { + m_Sequence.realloc(nCurrentPos); + nCurrentPos = writeSequence(); + //m_Sequence.realloc(SEQUENCESIZE); + } +} + +inline void SaxWriterHelper::clearBuffer() throw( SAXException ) +{ + FinishStartElement(); + if (nCurrentPos > 0) + { + m_Sequence.realloc(nCurrentPos); + nCurrentPos = writeSequence(); + m_Sequence.realloc(SEQUENCESIZE); + // Be sure to update the array pointer after the reallocation. + mp_Sequence = m_Sequence.getArray(); + } +} + +inline sal_Bool SaxWriterHelper::processingInstruction(const rtl::OUString& rTarget, const rtl::OUString& rData) throw( SAXException ) +{ + FinishStartElement(); + mp_Sequence[nCurrentPos] = '<'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '?'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + sal_Bool bRet(writeString( rTarget, sal_False, sal_False )); + + mp_Sequence[nCurrentPos] = ' '; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + if (!writeString( rData, sal_False, sal_False )) + bRet = sal_False; + + mp_Sequence[nCurrentPos] = '?'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '>'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + return bRet; +} + +inline void SaxWriterHelper::startCDATA() throw( SAXException ) +{ + FinishStartElement(); + if ((nCurrentPos + 9) <= SEQUENCESIZE) + { + memcpy( &(mp_Sequence[nCurrentPos]), "<![CDATA[" , 9 ); + nCurrentPos += 9; + } + else + AddBytes(mp_Sequence, nCurrentPos, (sal_Int8*)"<![CDATA[" , 9); + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); +} + +inline void SaxWriterHelper::endCDATA() throw( SAXException ) +{ + FinishStartElement(); + if ((nCurrentPos + 3) <= SEQUENCESIZE) + { + memcpy( &(mp_Sequence[nCurrentPos]), "]]>" , 3 ); + nCurrentPos += 3; + } + else + AddBytes(mp_Sequence, nCurrentPos, (sal_Int8*)"]]>" , 3); + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); +} + +inline sal_Bool SaxWriterHelper::comment(const rtl::OUString& rComment) throw( SAXException ) +{ + FinishStartElement(); + mp_Sequence[nCurrentPos] = '<'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '!'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '-'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '-'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + sal_Bool bRet(writeString( rComment, sal_False, sal_False)); + + mp_Sequence[nCurrentPos] = '-'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '-'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + mp_Sequence[nCurrentPos] = '>'; + nCurrentPos++; + if (nCurrentPos == SEQUENCESIZE) + nCurrentPos = writeSequence(); + + return bRet; +} + +inline sal_Int32 calcXMLByteLength( const sal_Unicode *pStr, sal_Int32 nStrLen, + sal_Bool bDoNormalization, + sal_Bool bNormalizeWhitespace ) +{ + sal_Int32 nOutputLength = 0; + sal_uInt32 nSurrogate = 0; + + for( sal_Int32 i = 0 ; i < nStrLen ; i++ ) + { + sal_uInt16 c = pStr[i]; + if( !IsInvalidChar(c) && (c >= 0x0001) && (c <= 0x007F) ) + { + if( bDoNormalization ) + { + switch( c ) + { + case '&': // resemble to & + nOutputLength +=5; + break; + case '<': // < + case '>': // > + nOutputLength +=4; + break; + case 39: // 39 == ''', ' + case '"': // " + case 13: // 
 + nOutputLength += 6; + break; + + case 10: // 
 + case 9: // 	 + if( bNormalizeWhitespace ) + { + nOutputLength += 6; // + } + else + { + nOutputLength ++; + } + break; + default: + nOutputLength ++; + } + } + else + { + nOutputLength ++; + } + } + else if( c >= 0xd800 && c < 0xdc00 ) + { + // save surrogate + nSurrogate = ( ( c & 0x03ff ) + 0x0040 ); + } + else if( c >= 0xdc00 && c < 0xe000 ) + { + // 2. surrogate: write as UTF-8 (if range is OK + nSurrogate = ( nSurrogate << 10 ) | ( c & 0x03ff ); + if( nSurrogate >= 0x00010000 && nSurrogate <= 0x0010FFFF ) + nOutputLength += 4; + nSurrogate = 0; + } + else if( c > 0x07FF ) + { + nOutputLength += 3; + } + else + { + nOutputLength += 2; + } + + // surrogate processing + if( ( nSurrogate != 0 ) && !( c >= 0xd800 && c < 0xdc00 ) ) + nSurrogate = 0; + } + + return nOutputLength; +} + +/** returns position of first ascii 10 within the string, -1 when no 10 in string. + */ +static inline sal_Int32 getFirstLineBreak( const OUString & str ) throw () +{ + const sal_Unicode *pSource = str.getStr(); + sal_Int32 nLen = str.getLength(); + + for( int n = 0; n < nLen ; n ++ ) + { + if( LINEFEED == pSource[n] ) { + return n; + } + } + return -1; +} + +/** returns position of last ascii 10 within sequence, -1 when no 10 in string. + */ +static inline sal_Int32 getLastLineBreak( const Sequence<sal_Int8> & seq) throw () +{ + const sal_Int8 *pSource = seq.getConstArray(); + sal_Int32 nLen = seq.getLength(); + + for( int n = nLen-1; n >= 0 ; n -- ) + { + if( LINEFEED == pSource[n] ) { + return n; + } + } + return -1; +} + + +class SAXWriter : + public WeakImplHelper3< + XActiveDataSource, + XExtendedDocumentHandler, + XServiceInfo > +{ +public: + SAXWriter( ) : + m_seqStartElement(), + mp_SaxWriterHelper( NULL ), + m_bForceLineBreak(sal_False), + m_bAllowLineBreak(sal_False) + {} + ~SAXWriter() + { + delete mp_SaxWriterHelper; + } + +public: // XActiveDataSource + virtual void SAL_CALL setOutputStream(const Reference< XOutputStream > & aStream) + throw (RuntimeException) + { + // temporary: set same stream again to clear buffer + if ( m_out == aStream && mp_SaxWriterHelper && m_bDocStarted ) + mp_SaxWriterHelper->clearBuffer(); + else + { + + m_out = aStream; + delete mp_SaxWriterHelper; + mp_SaxWriterHelper = new SaxWriterHelper(m_out); + m_bDocStarted = sal_False; + m_nLevel = 0; + m_bIsCDATA = sal_False; + + } + } + virtual Reference< XOutputStream > SAL_CALL getOutputStream(void) + throw(RuntimeException) + { return m_out; } + +public: // XDocumentHandler + virtual void SAL_CALL startDocument(void) + throw(SAXException, RuntimeException); + + virtual void SAL_CALL endDocument(void) + throw(SAXException, RuntimeException); + + virtual void SAL_CALL startElement(const OUString& aName, + const Reference< XAttributeList > & xAttribs) + throw (SAXException, RuntimeException); + + virtual void SAL_CALL endElement(const OUString& aName) + throw(SAXException, RuntimeException); + + virtual void SAL_CALL characters(const OUString& aChars) + throw(SAXException, RuntimeException); + + virtual void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) + throw(SAXException, RuntimeException); + virtual void SAL_CALL processingInstruction(const OUString& aTarget, + const OUString& aData) + throw(SAXException, RuntimeException); + virtual void SAL_CALL setDocumentLocator(const Reference< XLocator > & xLocator) + throw(SAXException, RuntimeException); + +public: // XExtendedDocumentHandler + virtual void SAL_CALL startCDATA(void) throw(SAXException, RuntimeException); + virtual void SAL_CALL endCDATA(void) throw(RuntimeException); + virtual void SAL_CALL comment(const OUString& sComment) + throw(SAXException, RuntimeException); + virtual void SAL_CALL unknown(const OUString& sString) + throw(SAXException, RuntimeException); + virtual void SAL_CALL allowLineBreak(void) + throw(SAXException,RuntimeException); + +public: // XServiceInfo + OUString SAL_CALL getImplementationName() throw(); + Sequence< OUString > SAL_CALL getSupportedServiceNames(void) throw(); + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) throw(); + +private: + + void writeSequence( const Sequence<sal_Int8> & seq ); + sal_Int32 getIndentPrefixLength( sal_Int32 nFirstLineBreakOccurence ) throw(); + + Reference< XOutputStream > m_out; + Sequence < sal_Int8 > m_seqStartElement; + SaxWriterHelper* mp_SaxWriterHelper; + + // Status information + sal_Bool m_bDocStarted : 1; + sal_Bool m_bIsCDATA : 1; + sal_Bool m_bForceLineBreak : 1; + sal_Bool m_bAllowLineBreak : 1; + sal_Int32 m_nLevel; +}; + + +//-------------------------------------- +// the extern interface +//--------------------------------------- +Reference < XInterface > SAL_CALL SaxWriter_CreateInstance( + const Reference < XMultiServiceFactory > & ) + throw (Exception) +{ + SAXWriter *p = new SAXWriter; + return Reference< XInterface > ( SAL_STATIC_CAST(OWeakObject *, p ) ); +} + +OUString SaxWriter_getServiceName() throw() +{ + return OUString::createFromAscii( "com.sun.star.xml.sax.Writer" ); +} + +OUString SaxWriter_getImplementationName() throw() +{ + return OUString::createFromAscii( "com.sun.star.extensions.xml.sax.Writer" ); +} + +Sequence< OUString > SaxWriter_getSupportedServiceNames(void) throw() +{ + Sequence<OUString> aRet(1); + aRet.getArray()[0] = SaxWriter_getServiceName(); + return aRet; +} + + +sal_Int32 SAXWriter::getIndentPrefixLength( sal_Int32 nFirstLineBreakOccurence ) throw() +{ + sal_Int32 nLength =-1; + if (mp_SaxWriterHelper) + { + if ( m_bForceLineBreak || + (m_bAllowLineBreak && + ((nFirstLineBreakOccurence + mp_SaxWriterHelper->GetLastColumnCount()) > MAXCOLUMNCOUNT)) ) + nLength = m_nLevel; + } + m_bForceLineBreak = sal_False; + m_bAllowLineBreak = sal_False; + return nLength; +} + +static inline sal_Bool isFirstCharWhitespace( const sal_Unicode *p ) throw() +{ + return *p == ' '; +} + + +// XServiceInfo +OUString SAXWriter::getImplementationName() throw() +{ + return SaxWriter_getImplementationName(); +} + +// XServiceInfo +sal_Bool SAXWriter::supportsService(const OUString& ServiceName) throw() +{ + Sequence< OUString > aSNL = getSupportedServiceNames(); + const OUString * pArray = aSNL.getConstArray(); + + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + + return sal_False; +} + +// XServiceInfo +Sequence< OUString > SAXWriter::getSupportedServiceNames(void) throw () +{ + Sequence<OUString> seq(1); + seq.getArray()[0] = SaxWriter_getServiceName(); + return seq; +} + + + +void SAXWriter::startDocument() throw(SAXException, RuntimeException ) +{ + if( m_bDocStarted || ! m_out.is() || !mp_SaxWriterHelper ) { + throw SAXException(); + } + m_bDocStarted = sal_True; + mp_SaxWriterHelper->startDocument(); +} + + +void SAXWriter::endDocument(void) throw(SAXException, RuntimeException) +{ + if( ! m_bDocStarted ) + { + throw SAXException( + OUString::createFromAscii( "endDocument called before startDocument" ), + Reference< XInterface >() , Any() ); + } + if( m_nLevel ) { + throw SAXException( + OUString::createFromAscii( "unexpected end of document" ), + Reference< XInterface >() , Any() ); + } + mp_SaxWriterHelper->endDocument(); + try + { + m_out->closeOutput(); + } + catch( IOException & e ) + { + Any a; + a <<= e; + throw SAXException( + OUString::createFromAscii( "IO exception during closing the IO Stream" ), + Reference< XInterface > (), + a ); + } +} + + +void SAXWriter::startElement(const OUString& aName, const Reference< XAttributeList >& xAttribs) + throw(SAXException, RuntimeException) +{ + if( ! m_bDocStarted ) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "startElement called before startDocument" )); + throw except; + } + if( m_bIsCDATA ) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "startElement call not allowed with CDATA sections" )); + throw except; + } + + sal_Int32 nLength(0); + if (m_bAllowLineBreak) + { + sal_Int32 nAttribCount = xAttribs.is() ? xAttribs->getLength() : 0; + + nLength ++; // "<" + nLength += calcXMLByteLength( aName.getStr() , aName.getLength(), + sal_False, sal_False ); // the tag name + + sal_Int16 n; + for( n = 0 ; n < static_cast<sal_Int16>(nAttribCount) ; n ++ ) { + nLength ++; // " " + OUString tmp = xAttribs->getNameByIndex( n ); + + nLength += calcXMLByteLength( tmp.getStr() , tmp.getLength() , sal_False, sal_False ); + + nLength += 2; // =" + + tmp = xAttribs->getValueByIndex( n ); + + nLength += calcXMLByteLength( tmp.getStr(), tmp.getLength(), sal_True, sal_True ); + + nLength += 1; // " + } + + nLength ++; // '>' + } + + // Is there a new indentation necesarry ? + sal_Int32 nPrefix(getIndentPrefixLength( nLength )); + + // write into sequence + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + SaxInvalidCharacterError eRet(mp_SaxWriterHelper->startElement(aName, xAttribs)); + + m_nLevel++; + + if (eRet == SAX_WARNING) + { + SAXInvalidCharacterException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export in a attribute value" ) ); + throw except; + } + else if (eRet == SAX_ERROR) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export" ) ); + throw except; + } +} + +void SAXWriter::endElement(const OUString& aName) throw (SAXException, RuntimeException) +{ + if( ! m_bDocStarted ) { + throw SAXException (); + } + m_nLevel --; + + if( m_nLevel < 0 ) { + throw SAXException(); + } + sal_Bool bRet(sal_True); + + if( mp_SaxWriterHelper->FinishEmptyElement() ) + m_bForceLineBreak = sal_False; + else + { + // only ascii chars allowed + sal_Int32 nLength(0); + if (m_bAllowLineBreak) + nLength = 3 + calcXMLByteLength( aName.getStr(), aName.getLength(), sal_False, sal_False ); + sal_Int32 nPrefix = getIndentPrefixLength( nLength ); + + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + bRet = mp_SaxWriterHelper->endElement(aName); + } + + if (!bRet) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export" ) ); + throw except; + } +} + +void SAXWriter::characters(const OUString& aChars) throw(SAXException, RuntimeException) +{ + if( ! m_bDocStarted ) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "characters method called before startDocument" ) ); + throw except; + } + + sal_Bool bThrowException(sal_False); + if( aChars.getLength() ) + { + if( m_bIsCDATA ) + bThrowException = !mp_SaxWriterHelper->writeString( aChars, sal_False, sal_False ); + else + { + // Note : nFirstLineBreakOccurence is not exact, because we don't know, how + // many 2 and 3 byte chars are inbetween. However this whole stuff + // is eitherway for pretty printing only, so it does not need to be exact. + sal_Int32 nLength(0); + sal_Int32 nIndentPrefix(-1); + if (m_bAllowLineBreak) + { + sal_Int32 nFirstLineBreakOccurence = getFirstLineBreak( aChars ); + + nLength = calcXMLByteLength( aChars.getStr(), aChars.getLength(), + ! m_bIsCDATA , sal_False ); + nIndentPrefix = getIndentPrefixLength( + nFirstLineBreakOccurence >= 0 ? nFirstLineBreakOccurence : nLength ); + } + else + nIndentPrefix = getIndentPrefixLength(nLength); + + // insert indentation + if( nIndentPrefix >= 0 ) + { + if( isFirstCharWhitespace( aChars.getStr() ) ) + mp_SaxWriterHelper->insertIndentation( nIndentPrefix - 1 ); + else + mp_SaxWriterHelper->insertIndentation( nIndentPrefix ); + } + bThrowException = !mp_SaxWriterHelper->writeString(aChars, sal_True , sal_False); + } + } + if (bThrowException) + { + SAXInvalidCharacterException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export" ) ); + throw except; + } +} + + +void SAXWriter::ignorableWhitespace(const OUString&) throw(SAXException, RuntimeException) +{ + if( ! m_bDocStarted ) + { + throw SAXException (); + } + + m_bForceLineBreak = sal_True; +} + +void SAXWriter::processingInstruction(const OUString& aTarget, const OUString& aData) + throw (SAXException, RuntimeException) +{ + if( ! m_bDocStarted || m_bIsCDATA ) + { + throw SAXException(); + } + + sal_Int32 nLength(0); + if (m_bAllowLineBreak) + { + nLength = 2; // "<?" + nLength += calcXMLByteLength( aTarget.getStr(), aTarget.getLength(), sal_False, sal_False ); + + nLength += 1; // " " + + nLength += calcXMLByteLength( aData.getStr(), aData.getLength(), sal_False, sal_False ); + + nLength += 2; // "?>" + } + + sal_Int32 nPrefix = getIndentPrefixLength( nLength ); + + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + if (!mp_SaxWriterHelper->processingInstruction(aTarget, aData)) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export" ) ); + throw except; + } +} + + +void SAXWriter::setDocumentLocator(const Reference< XLocator >&) + throw (SAXException, RuntimeException) +{ + +} + +void SAXWriter::startCDATA(void) throw(SAXException, RuntimeException) +{ + if( ! m_bDocStarted || m_bIsCDATA) + { + throw SAXException (); + } + + sal_Int32 nLength = 9; + sal_Int32 nPrefix = getIndentPrefixLength( nLength ); + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + mp_SaxWriterHelper->startCDATA(); + + m_bIsCDATA = sal_True; +} + +void SAXWriter::endCDATA(void) throw (RuntimeException) +{ + if( ! m_bDocStarted | ! m_bIsCDATA) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "endCDATA was called without startCDATA" ) ); + throw except; + } + + sal_Int32 nLength = 3; + sal_Int32 nPrefix = getIndentPrefixLength( nLength ); + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + mp_SaxWriterHelper->endCDATA(); + + m_bIsCDATA = sal_False; +} + + +void SAXWriter::comment(const OUString& sComment) throw(SAXException, RuntimeException) +{ + if( ! m_bDocStarted || m_bIsCDATA ) + { + throw SAXException(); + } + + sal_Int32 nLength(0); + if (m_bAllowLineBreak) + { + nLength = 4; // "<!--" + nLength += calcXMLByteLength( sComment.getStr(), sComment.getLength(), sal_False, sal_False); + + nLength += 3; + } + + sal_Int32 nPrefix = getIndentPrefixLength( nLength ); + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + if (!mp_SaxWriterHelper->comment(sComment)) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export" ) ); + throw except; + } +} + + +void SAXWriter::allowLineBreak( ) throw ( SAXException , RuntimeException) +{ + if( ! m_bDocStarted || m_bAllowLineBreak ) { + throw SAXException(); + } + + m_bAllowLineBreak = sal_True; +} + +void SAXWriter::unknown(const OUString& sString) throw (SAXException, RuntimeException) +{ + + if( ! m_bDocStarted ) + { + throw SAXException (); + } + if( m_bIsCDATA ) + { + throw SAXException(); + } + + if( sString.matchAsciiL( "<?xml", 5 ) ) + return; + + sal_Int32 nLength(0); + if (m_bAllowLineBreak) + nLength = calcXMLByteLength( sString.getStr(), sString.getLength(), sal_False, sal_False ); + + sal_Int32 nPrefix = getIndentPrefixLength( nLength ); + if( nPrefix >= 0 ) + mp_SaxWriterHelper->insertIndentation( nPrefix ); + + if (!mp_SaxWriterHelper->writeString( sString, sal_False, sal_False)) + { + SAXException except; + except.Message = OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid charcter during XML-Export" ) ); + throw except; + } +} + +} + diff --git a/sax/source/expatwrap/xml2utf.cxx b/sax/source/expatwrap/xml2utf.cxx new file mode 100644 index 000000000000..bbd72b2a0d8b --- /dev/null +++ b/sax/source/expatwrap/xml2utf.cxx @@ -0,0 +1,570 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include <string.h> + +#include <sal/types.h> + +#include <rtl/textenc.h> +#include <rtl/tencinfo.h> + + +#include <com/sun/star/io/XInputStream.hpp> + +using namespace rtl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; + +#include "xml2utf.hxx" + +namespace sax_expatwrap { + +sal_Int32 XMLFile2UTFConverter::readAndConvert( Sequence<sal_Int8> &seq , sal_Int32 nMaxToRead ) + throw ( IOException, NotConnectedException , BufferSizeExceededException , RuntimeException ) +{ + + Sequence<sal_Int8> seqIn; + + if( ! m_in.is() ) { + throw NotConnectedException(); + } + if( ! m_bStarted ) { + nMaxToRead = Max( 512 , nMaxToRead ); // it should be possible to find the encoding attribute + // within the first 512 bytes == 128 chars in UCS-4 + } + + sal_Int32 nRead; + Sequence< sal_Int8 > seqStart; + while( sal_True ) + { + nRead = m_in->readSomeBytes( seq , nMaxToRead ); + + if( nRead + seqStart.getLength()) + { + // if nRead is 0, the file is already eof. + if( ! m_bStarted && nRead ) + { + // ensure that enough data is available to parse encoding + if( seqStart.getLength() ) + { + // prefix with what we had so far. + sal_Int32 nLength = seq.getLength(); + seq.realloc( seqStart.getLength() + nLength ); + + memmove (seq.getArray() + seqStart.getLength(), + seq.getConstArray(), + nLength); + memcpy (seq.getArray(), + seqStart.getConstArray(), + seqStart.getLength()); + } + + // autodetection with the first bytes + if( ! isEncodingRecognizable( seq ) ) + { + // remember what we have so far. + seqStart = seq; + + // read more ! + continue; + } + if( scanForEncoding( seq ) || m_sEncoding.getLength() ) { + // initialize decoding + initializeDecoding(); + } + nRead = seq.getLength(); + seqStart = Sequence < sal_Int8 > (); + } + + // do the encoding + if( m_pText2Unicode && m_pUnicode2Text && + m_pText2Unicode->canContinue() && m_pUnicode2Text->canContinue() ) { + + Sequence<sal_Unicode> seqUnicode = m_pText2Unicode->convert( seq ); + seq = m_pUnicode2Text->convert( seqUnicode.getConstArray(), seqUnicode.getLength() ); + } + + if( ! m_bStarted ) + { + // it must now be ensured, that no encoding attribute exist anymore + // ( otherwise the expat-Parser will crash ) + // This must be done after decoding ! + // ( e.g. Files decoded in ucs-4 cannot be read properly ) + m_bStarted = sal_True; + removeEncoding( seq ); + } + nRead = seq.getLength(); + } + + break; + } + return nRead; +} + + +XMLFile2UTFConverter::~XMLFile2UTFConverter() +{ + if( m_pText2Unicode ) + delete m_pText2Unicode; + if( m_pUnicode2Text ) + delete m_pUnicode2Text; +} + + +void XMLFile2UTFConverter::removeEncoding( Sequence<sal_Int8> &seq ) +{ + const sal_Int8 *pSource = seq.getArray(); + if( ! strncmp( (const char * ) pSource , "<?xml" , 4) ) + { + + // scan for encoding + OString str( (sal_Char * ) pSource , seq.getLength() ); + + // cut sequence to first line break + // find first line break; + int nMax = str.indexOf( 10 ); + if( nMax >= 0 ) + { + str = str.copy( 0 , nMax ); + } + + int nFound = str.indexOf( " encoding" ); + if( nFound >= 0 ) { + int nStop; + int nStart = str.indexOf( "\"" , nFound ); + if( nStart < 0 || str.indexOf( "'" , nFound ) < nStart ) + { + nStart = str.indexOf( "'" , nFound ); + nStop = str.indexOf( "'" , nStart +1 ); + } + else + { + nStop = str.indexOf( "\"" , nStart +1); + } + + if( nStart >= 0 && nStop >= 0 && nStart+1 < nStop ) + { + // remove encoding tag from file + memmove( &( seq.getArray()[nFound] ) , + &( seq.getArray()[nStop+1]) , + seq.getLength() - nStop -1); + seq.realloc( seq.getLength() - ( nStop+1 - nFound ) ); +// str = String( (char * ) seq.getArray() , seq.getLen() ); + } + } + } +} + +// Checks, if enough data has been accumulated to recognize the encoding +sal_Bool XMLFile2UTFConverter::isEncodingRecognizable( const Sequence< sal_Int8 > &seq) +{ + const sal_Int8 *pSource = seq.getConstArray(); + sal_Bool bCheckIfFirstClosingBracketExsists = sal_False; + + if( seq.getLength() < 8 ) { + // no recognition possible, when less than 8 bytes are available + return sal_False; + } + + if( ! strncmp( (const char * ) pSource , "<?xml" , 4 ) ) { + // scan if the <?xml tag finishes within this buffer + bCheckIfFirstClosingBracketExsists = sal_True; + } + else if( ('<' == pSource[0] || '<' == pSource[2] ) && + ( ('?' == pSource[4] || '?' == pSource[6] ) ) ) + { + // check for utf-16 + bCheckIfFirstClosingBracketExsists = sal_True; + } + else if( ( '<' == pSource[1] || '<' == pSource[3] ) && + ( '?' == pSource[5] || '?' == pSource[7] ) ) + { + // check for + bCheckIfFirstClosingBracketExsists = sal_True; + } + + if( bCheckIfFirstClosingBracketExsists ) + { + for( sal_Int32 i = 0; i < seq.getLength() ; i ++ ) + { + // whole <?xml tag is valid + if( '>' == pSource[ i ] ) + { + return sal_True; + } + } + return sal_False; + } + + // No <? tag in front, no need for a bigger buffer + return sal_True; +} + +sal_Bool XMLFile2UTFConverter::scanForEncoding( Sequence< sal_Int8 > &seq ) +{ + const sal_uInt8 *pSource = reinterpret_cast<const sal_uInt8*>( seq.getConstArray() ); + sal_Bool bReturn = sal_True; + + if( seq.getLength() < 4 ) { + // no recognition possible, when less than 4 bytes are available + return sal_False; + } + + // first level : detect possible file formats + if( ! strncmp( (const char * ) pSource , "<?xml" , 4 ) ) { + + // scan for encoding + OString str( (const sal_Char *) pSource , seq.getLength() ); + + // cut sequence to first line break + //find first line break; + int nMax = str.indexOf( 10 ); + if( nMax >= 0 ) + { + str = str.copy( 0 , nMax ); + } + + int nFound = str.indexOf( " encoding" ); + if( nFound < str.getLength() ) { + int nStop; + int nStart = str.indexOf( "\"" , nFound ); + if( nStart < 0 || str.indexOf( "'" , nFound ) < nStart ) + { + nStart = str.indexOf( "'" , nFound ); + nStop = str.indexOf( "'" , nStart +1 ); + } + else + { + nStop = str.indexOf( "\"" , nStart +1); + } + if( nStart >= 0 && nStop >= 0 && nStart+1 < nStop ) + { + // encoding found finally + m_sEncoding = str.copy( nStart+1 , nStop - nStart - 1 ); + } + } + } + else if( 0xFE == pSource[0] && + 0xFF == pSource[1] ) { + // UTF-16 big endian + // conversion is done so that encoding information can be easily extracted + m_sEncoding = "utf-16"; + } + else if( 0xFF == pSource[0] && + 0xFE == pSource[1] ) { + // UTF-16 little endian + // conversion is done so that encoding information can be easily extracted + m_sEncoding = "utf-16"; + } + else if( 0x00 == pSource[0] && 0x3c == pSource[1] && 0x00 == pSource[2] && 0x3f == pSource[3] ) { + // UTF-16 big endian without byte order mark (this is (strictly speaking) an error.) + // The byte order mark is simply added + + // simply add the byte order mark ! + seq.realloc( seq.getLength() + 2 ); + memmove( &( seq.getArray()[2] ) , seq.getArray() , seq.getLength() - 2 ); + ((sal_uInt8*)seq.getArray())[0] = 0xFE; + ((sal_uInt8*)seq.getArray())[1] = 0xFF; + + m_sEncoding = "utf-16"; + } + else if( 0x3c == pSource[0] && 0x00 == pSource[1] && 0x3f == pSource[2] && 0x00 == pSource[3] ) { + // UTF-16 little endian without byte order mark (this is (strictly speaking) an error.) + // The byte order mark is simply added + + seq.realloc( seq.getLength() + 2 ); + memmove( &( seq.getArray()[2] ) , seq.getArray() , seq.getLength() - 2 ); + ((sal_uInt8*)seq.getArray())[0] = 0xFF; + ((sal_uInt8*)seq.getArray())[1] = 0xFE; + + m_sEncoding = "utf-16"; + } + else if( 0xEF == pSource[0] && + 0xBB == pSource[1] && + 0xBF == pSource[2] ) + { + // UTF-8 BOM (byte order mark); signifies utf-8, and not byte order + // The BOM is removed. + memmove( seq.getArray(), &( seq.getArray()[3] ), seq.getLength()-3 ); + seq.realloc( seq.getLength() - 3 ); + m_sEncoding = "utf-8"; + } + else if( 0x00 == pSource[0] && 0x00 == pSource[1] && 0x00 == pSource[2] && 0x3c == pSource[3] ) { + // UCS-4 big endian + m_sEncoding = "ucs-4"; + } + else if( 0x3c == pSource[0] && 0x00 == pSource[1] && 0x00 == pSource[2] && 0x00 == pSource[3] ) { + // UCS-4 little endian + m_sEncoding = "ucs-4"; + } + else if( 0x4c == pSource[0] && 0x6f == pSource[1] && + 0xa7 == static_cast<unsigned char> (pSource[2]) && + 0x94 == static_cast<unsigned char> (pSource[3]) ) { + // EBCDIC + bReturn = sal_False; // must be extended + } + else { + // other + // UTF8 is directly recognized by the parser. + bReturn = sal_False; + } + + return bReturn; +} + +void XMLFile2UTFConverter::initializeDecoding() +{ + + if( m_sEncoding.getLength() ) + { + rtl_TextEncoding encoding = rtl_getTextEncodingFromMimeCharset( m_sEncoding.getStr() ); + if( encoding != RTL_TEXTENCODING_UTF8 ) + { + m_pText2Unicode = new Text2UnicodeConverter( m_sEncoding ); + m_pUnicode2Text = new Unicode2TextConverter( RTL_TEXTENCODING_UTF8 ); + } + } +} + + +//---------------------------------------------- +// +// Text2UnicodeConverter +// +//---------------------------------------------- +Text2UnicodeConverter::Text2UnicodeConverter( const OString &sEncoding ) +{ + rtl_TextEncoding encoding = rtl_getTextEncodingFromMimeCharset( sEncoding.getStr() ); + if( RTL_TEXTENCODING_DONTKNOW == encoding ) + { + m_bCanContinue = sal_False; + m_bInitialized = sal_False; + } + else + { + init( encoding ); + } +} + +Text2UnicodeConverter::~Text2UnicodeConverter() +{ + if( m_bInitialized ) + { + rtl_destroyTextToUnicodeContext( m_convText2Unicode , m_contextText2Unicode ); + rtl_destroyUnicodeToTextConverter( m_convText2Unicode ); + } +} + +void Text2UnicodeConverter::init( rtl_TextEncoding encoding ) +{ + m_bCanContinue = sal_True; + m_bInitialized = sal_True; + + m_convText2Unicode = rtl_createTextToUnicodeConverter(encoding); + m_contextText2Unicode = rtl_createTextToUnicodeContext( m_convText2Unicode ); + m_rtlEncoding = encoding; +} + + +Sequence<sal_Unicode> Text2UnicodeConverter::convert( const Sequence<sal_Int8> &seqText ) +{ + sal_uInt32 uiInfo; + sal_Size nSrcCvtBytes = 0; + sal_Size nTargetCount = 0; + sal_Size nSourceCount = 0; + + // the whole source size + sal_Int32 nSourceSize = seqText.getLength() + m_seqSource.getLength(); + Sequence<sal_Unicode> seqUnicode ( nSourceSize ); + + const sal_Int8 *pbSource = seqText.getConstArray(); + sal_Int8 *pbTempMem = 0; + + if( m_seqSource.getLength() ) { + // put old rest and new byte sequence into one array + pbTempMem = new sal_Int8[ nSourceSize ]; + memcpy( pbTempMem , m_seqSource.getConstArray() , m_seqSource.getLength() ); + memcpy( &(pbTempMem[ m_seqSource.getLength() ]) , seqText.getConstArray() , seqText.getLength() ); + pbSource = pbTempMem; + + // set to zero again + m_seqSource = Sequence< sal_Int8 >(); + } + + while( sal_True ) { + + /* All invalid characters are transformed to the unicode undefined char */ + nTargetCount += rtl_convertTextToUnicode( + m_convText2Unicode, + m_contextText2Unicode, + ( const sal_Char * ) &( pbSource[nSourceCount] ), + nSourceSize - nSourceCount , + &( seqUnicode.getArray()[ nTargetCount ] ), + seqUnicode.getLength() - nTargetCount, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT, + &uiInfo, + &nSrcCvtBytes ); + nSourceCount += nSrcCvtBytes; + + if( uiInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL ) { + // save necessary bytes for next conversion + seqUnicode.realloc( seqUnicode.getLength() * 2 ); + continue; + } + break; + } + if( uiInfo & RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL ) { + m_seqSource.realloc( nSourceSize - nSourceCount ); + memcpy( m_seqSource.getArray() , &(pbSource[nSourceCount]) , nSourceSize-nSourceCount ); + } + + + if( pbTempMem ) { + delete [] pbTempMem; + } + + // set to correct unicode size + seqUnicode.realloc( nTargetCount ); + + return seqUnicode; +} + + + +//---------------------------------------------- +// +// Unicode2TextConverter +// +//---------------------------------------------- +Unicode2TextConverter::Unicode2TextConverter( rtl_TextEncoding encoding ) +{ + init( encoding ); +} + + +Unicode2TextConverter::~Unicode2TextConverter() +{ + if( m_bInitialized ) { + rtl_destroyUnicodeToTextContext( m_convUnicode2Text , m_contextUnicode2Text ); + rtl_destroyUnicodeToTextConverter( m_convUnicode2Text ); + } +} + + +Sequence<sal_Int8> Unicode2TextConverter::convert(const sal_Unicode *puSource , sal_Int32 nSourceSize) +{ + sal_Unicode *puTempMem = 0; + + if( m_seqSource.getLength() ) { + // For surrogates ! + // put old rest and new byte sequence into one array + // In general when surrogates are used, they should be rarely + // cut off between two convert()-calls. So this code is used + // rarely and the extra copy is acceptable. + puTempMem = new sal_Unicode[ nSourceSize + m_seqSource.getLength()]; + memcpy( puTempMem , + m_seqSource.getConstArray() , + m_seqSource.getLength() * sizeof( sal_Unicode ) ); + memcpy( + &(puTempMem[ m_seqSource.getLength() ]) , + puSource , + nSourceSize*sizeof( sal_Unicode ) ); + puSource = puTempMem; + nSourceSize += m_seqSource.getLength(); + + m_seqSource = Sequence< sal_Unicode > (); + } + + + sal_Size nTargetCount = 0; + sal_Size nSourceCount = 0; + + sal_uInt32 uiInfo; + sal_Size nSrcCvtChars; + + // take nSourceSize * 3 as preference + // this is an upper boundary for converting to utf8, + // which most often used as the target. + sal_Int32 nSeqSize = nSourceSize * 3; + + Sequence<sal_Int8> seqText( nSeqSize ); + sal_Char *pTarget = (sal_Char *) seqText.getArray(); + while( sal_True ) { + + nTargetCount += rtl_convertUnicodeToText( + m_convUnicode2Text, + m_contextUnicode2Text, + &( puSource[nSourceCount] ), + nSourceSize - nSourceCount , + &( pTarget[nTargetCount] ), + nSeqSize - nTargetCount, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT | + RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT , + &uiInfo, + &nSrcCvtChars); + nSourceCount += nSrcCvtChars; + + if( uiInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL ) { + nSeqSize = nSeqSize *2; + seqText.realloc( nSeqSize ); // double array size + pTarget = ( sal_Char * ) seqText.getArray(); + continue; + } + break; + } + + // for surrogates + if( uiInfo & RTL_UNICODETOTEXT_INFO_SRCBUFFERTOSMALL ) { + m_seqSource.realloc( nSourceSize - nSourceCount ); + memcpy( m_seqSource.getArray() , + &(puSource[nSourceCount]), + (nSourceSize - nSourceCount) * sizeof( sal_Unicode ) ); + } + + if( puTempMem ) { + delete [] puTempMem; + } + + // reduce the size of the buffer (fast, no copy necessary) + seqText.realloc( nTargetCount ); + + return seqText; +} + +void Unicode2TextConverter::init( rtl_TextEncoding encoding ) +{ + m_bCanContinue = sal_True; + m_bInitialized = sal_True; + + m_convUnicode2Text = rtl_createUnicodeToTextConverter( encoding ); + m_contextUnicode2Text = rtl_createUnicodeToTextContext( m_convUnicode2Text ); + m_rtlEncoding = encoding; +}; + + +} diff --git a/sax/source/fastparser/facreg.cxx b/sax/source/fastparser/facreg.cxx new file mode 100644 index 000000000000..98a55823271e --- /dev/null +++ b/sax/source/fastparser/facreg.cxx @@ -0,0 +1,78 @@ +#include <cppuhelper/factory.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase2.hxx> + +#include "../tools/fastserializer.hxx" +#include "fastparser.hxx" + +using namespace sax_fastparser; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::registry; +using ::rtl::OUString; +using namespace ::com::sun::star::lang; + +namespace sax_fastparser +{ + +//-------------------------------------- +// the extern interface +//--------------------------------------- +Reference< XInterface > SAL_CALL FastSaxParser_CreateInstance( const Reference< XMultiServiceFactory > & ) throw(Exception) +{ + FastSaxParser *p = new FastSaxParser; + return Reference< XInterface > ( (OWeakObject * ) p ); +} + +Reference< XInterface > SAL_CALL FastSaxSerializer_CreateInstance( const Reference< XMultiServiceFactory > & ) throw(Exception) +{ + FastSaxSerializer *p = new FastSaxSerializer; + return Reference< XInterface > ( (OWeakObject * ) p ); +} +} + +extern "C" +{ + +void SAL_CALL component_getImplementationEnvironment( + const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ ) +{ + *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +} + +void * SAL_CALL component_getFactory( const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) +{ + void * pRet = 0; + + if (pServiceManager ) + { + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > xSMgr( reinterpret_cast< XMultiServiceFactory * > ( pServiceManager ) ); + + OUString aImplementationName( OUString::createFromAscii( pImplName ) ); + + if (aImplementationName == OUString( RTL_CONSTASCII_USTRINGPARAM( PARSER_IMPLEMENTATION_NAME ) ) ) + { + xRet = createSingleFactory( xSMgr, aImplementationName, + FastSaxParser_CreateInstance, + FastSaxParser::getSupportedServiceNames_Static() ); + } + else if (aImplementationName == OUString( RTL_CONSTASCII_USTRINGPARAM( SERIALIZER_IMPLEMENTATION_NAME ) ) ) + { + xRet = createSingleFactory( xSMgr, aImplementationName, + FastSaxSerializer_CreateInstance, + FastSaxSerializer::getSupportedServiceNames_Static() ); + } + + if (xRet.is()) + { + xRet->acquire(); + pRet = xRet.get(); + } + } + + return pRet; +} + + +} diff --git a/sax/source/fastparser/fastparser.cxx b/sax/source/fastparser/fastparser.cxx new file mode 100644 index 000000000000..1a4cc9278e1e --- /dev/null +++ b/sax/source/fastparser/fastparser.cxx @@ -0,0 +1,953 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +//#include <stdlib.h> +//#include <sal/alloca.h> + +#include <boost/scoped_ptr.hpp> + +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/xml/sax/XFastContextHandler.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/xml/sax/FastToken.hpp> + +#include "fastparser.hxx" + +#include <string.h> + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using namespace ::std; +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::xml::sax; +//using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; + +namespace sax_fastparser { + +// -------------------------------------------------------------------- + +struct SaxContextImpl +{ + Reference< XFastContextHandler > mxContext; + sal_uInt32 mnNamespaceCount; + sal_Int32 mnElementToken; + OUString maNamespace; + OUString maElementName; + + SaxContextImpl() { mnNamespaceCount = 0; mnElementToken = 0; } + SaxContextImpl( const SaxContextImplPtr& p ) { mnNamespaceCount = p->mnNamespaceCount; mnElementToken = p->mnElementToken; maNamespace = p->maNamespace; } +}; + +// -------------------------------------------------------------------- + +struct NamespaceDefine +{ + OString maPrefix; + sal_Int32 mnToken; + OUString maNamespaceURL; + + NamespaceDefine( const OString& rPrefix, sal_Int32 nToken, const OUString& rNamespaceURL ) : maPrefix( rPrefix ), mnToken( nToken ), maNamespaceURL( rNamespaceURL ) {} +}; + +// -------------------------------------------------------------------- +// FastLocatorImpl +// -------------------------------------------------------------------- + +class FastSaxParser; + +class FastLocatorImpl : public WeakImplHelper1< XLocator > +{ +public: + FastLocatorImpl( FastSaxParser *p ) : mpParser(p) {} + + void dispose() { mpParser = 0; } + void checkDispose() throw (RuntimeException) { if( !mpParser ) throw DisposedException(); } + + //XLocator + virtual sal_Int32 SAL_CALL getColumnNumber(void) throw (RuntimeException); + virtual sal_Int32 SAL_CALL getLineNumber(void) throw (RuntimeException); + virtual OUString SAL_CALL getPublicId(void) throw (RuntimeException); + virtual OUString SAL_CALL getSystemId(void) throw (RuntimeException); + +private: + FastSaxParser *mpParser; +}; + +// -------------------------------------------------------------------- +// FastSaxParser +// -------------------------------------------------------------------- + +//--------------------------------------------- +// the implementation part +//--------------------------------------------- + +extern "C" { + +static void call_callbackStartElement(void *userData, const XML_Char *name , const XML_Char **atts) +{ + FastSaxParser* pFastParser = reinterpret_cast< FastSaxParser* >( userData ); + pFastParser->callbackStartElement( name, atts ); +} + +static void call_callbackEndElement(void *userData, const XML_Char *name) +{ + FastSaxParser* pFastParser = reinterpret_cast< FastSaxParser* >( userData ); + pFastParser->callbackEndElement( name ); +} + +static void call_callbackCharacters( void *userData , const XML_Char *s , int nLen ) +{ + FastSaxParser* pFastParser = reinterpret_cast< FastSaxParser* >( userData ); + pFastParser->callbackCharacters( s, nLen ); +} + +static int call_callbackExternalEntityRef( XML_Parser parser, + const XML_Char *openEntityNames, const XML_Char *base, const XML_Char *systemId, const XML_Char *publicId ) +{ + FastSaxParser* pFastParser = reinterpret_cast< FastSaxParser* >( XML_GetUserData( parser ) ); + return pFastParser->callbackExternalEntityRef( parser, openEntityNames, base, systemId, publicId ); +} + +} // extern "C" + +// -------------------------------------------------------------------- +// FastLocatorImpl implementation +// -------------------------------------------------------------------- + +sal_Int32 SAL_CALL FastLocatorImpl::getColumnNumber(void) throw (RuntimeException) +{ + checkDispose(); + return XML_GetCurrentColumnNumber( mpParser->getEntity().mpParser ); +} + +// -------------------------------------------------------------------- + +sal_Int32 SAL_CALL FastLocatorImpl::getLineNumber(void) throw (RuntimeException) +{ + checkDispose(); + return XML_GetCurrentLineNumber( mpParser->getEntity().mpParser ); +} + +// -------------------------------------------------------------------- + +OUString SAL_CALL FastLocatorImpl::getPublicId(void) throw (RuntimeException) +{ + checkDispose(); + return mpParser->getEntity().maStructSource.sPublicId; +} +// -------------------------------------------------------------------- + +OUString SAL_CALL FastLocatorImpl::getSystemId(void) throw (RuntimeException) +{ + checkDispose(); + return mpParser->getEntity().maStructSource.sSystemId; +} + +// -------------------------------------------------------------------- + +ParserData::ParserData() +{ +} + +ParserData::~ParserData() +{ +} + +// -------------------------------------------------------------------- + +Entity::Entity( const ParserData& rData ) : + ParserData( rData ) +{ + // performance-Improvment. Reference is needed when calling the startTag callback. + // Handing out the same object with every call is allowed (see sax-specification) + mxAttributes.set( new FastAttributeList( mxTokenHandler ) ); +} + +Entity::~Entity() +{ +} + +// -------------------------------------------------------------------- +// FastSaxParser implementation +// -------------------------------------------------------------------- + +FastSaxParser::FastSaxParser() +{ + mxDocumentLocator.set( new FastLocatorImpl( this ) ); +} + +// -------------------------------------------------------------------- + +FastSaxParser::~FastSaxParser() +{ + if( mxDocumentLocator.is() ) + mxDocumentLocator->dispose(); +} + +// -------------------------------------------------------------------- + +void FastSaxParser::pushContext() +{ + Entity& rEntity = getEntity(); + if( rEntity.maContextStack.empty() ) + { + rEntity.maContextStack.push( SaxContextImplPtr( new SaxContextImpl ) ); + DefineNamespace( OString("xml"), "http://www.w3.org/XML/1998/namespace"); + } + else + { + rEntity.maContextStack.push( SaxContextImplPtr( new SaxContextImpl( rEntity.maContextStack.top() ) ) ); + } +} + +// -------------------------------------------------------------------- + +void FastSaxParser::popContext() +{ + Entity& rEntity = getEntity(); + OSL_ENSURE( !rEntity.maContextStack.empty(), "sax::FastSaxParser::popContext(), pop without push?" ); + if( !rEntity.maContextStack.empty() ) + rEntity.maContextStack.pop(); +} + +// -------------------------------------------------------------------- + +void FastSaxParser::DefineNamespace( const OString& rPrefix, const sal_Char* pNamespaceURL ) +{ + Entity& rEntity = getEntity(); + OSL_ENSURE( !rEntity.maContextStack.empty(), "sax::FastSaxParser::DefineNamespace(), I need a context!" ); + if( !rEntity.maContextStack.empty() ) + { + sal_uInt32 nOffset = rEntity.maContextStack.top()->mnNamespaceCount++; + + if( rEntity.maNamespaceDefines.size() <= nOffset ) + rEntity.maNamespaceDefines.resize( rEntity.maNamespaceDefines.size() + 64 ); + + const OUString aNamespaceURL( pNamespaceURL, strlen( pNamespaceURL ), RTL_TEXTENCODING_UTF8 ); + rEntity.maNamespaceDefines[nOffset].reset( new NamespaceDefine( rPrefix, GetNamespaceToken( aNamespaceURL ), aNamespaceURL ) ); + } +} + +// -------------------------------------------------------------------- + +sal_Int32 FastSaxParser::GetToken( const OString& rToken ) +{ + Sequence< sal_Int8 > aSeq( (sal_Int8*)rToken.getStr(), rToken.getLength() ); + + return getEntity().mxTokenHandler->getTokenFromUTF8( aSeq ); +} + +sal_Int32 FastSaxParser::GetToken( const sal_Char* pToken, sal_Int32 nLen /* = 0 */ ) +{ + if( !nLen ) + nLen = strlen( pToken ); + + Sequence< sal_Int8 > aSeq( (sal_Int8*)pToken, nLen ); + + return getEntity().mxTokenHandler->getTokenFromUTF8( aSeq ); +} + +// -------------------------------------------------------------------- + +sal_Int32 FastSaxParser::GetTokenWithPrefix( const OString& rPrefix, const OString& rName ) throw (SAXException) +{ + sal_Int32 nNamespaceToken = FastToken::DONTKNOW; + + Entity& rEntity = getEntity(); + sal_uInt32 nNamespace = rEntity.maContextStack.top()->mnNamespaceCount; + while( nNamespace-- ) + { + if( rEntity.maNamespaceDefines[nNamespace]->maPrefix == rPrefix ) + { + nNamespaceToken = rEntity.maNamespaceDefines[nNamespace]->mnToken; + break; + } + + if( !nNamespace ) + throw SAXException(); // prefix that has no defined namespace url + } + + if( nNamespaceToken != FastToken::DONTKNOW ) + { + sal_Int32 nNameToken = GetToken( rName.getStr(), rName.getLength() ); + if( nNameToken != FastToken::DONTKNOW ) + return nNamespaceToken | nNameToken; + } + + return FastToken::DONTKNOW; +} + +sal_Int32 FastSaxParser::GetTokenWithPrefix( const sal_Char*pPrefix, int nPrefixLen, const sal_Char* pName, int nNameLen ) throw (SAXException) +{ + sal_Int32 nNamespaceToken = FastToken::DONTKNOW; + + Entity& rEntity = getEntity(); + sal_uInt32 nNamespace = rEntity.maContextStack.top()->mnNamespaceCount; + while( nNamespace-- ) + { + const OString& rPrefix( rEntity.maNamespaceDefines[nNamespace]->maPrefix ); + if( (rPrefix.getLength() == nPrefixLen) && + (strncmp( rPrefix.getStr(), pPrefix, nPrefixLen ) == 0 ) ) + { + nNamespaceToken = rEntity.maNamespaceDefines[nNamespace]->mnToken; + break; + } + + if( !nNamespace ) + throw SAXException(); // prefix that has no defined namespace url + } + + if( nNamespaceToken != FastToken::DONTKNOW ) + { + sal_Int32 nNameToken = GetToken( pName, nNameLen ); + if( nNameToken != FastToken::DONTKNOW ) + return nNamespaceToken | nNameToken; + } + + return FastToken::DONTKNOW; +} + +// -------------------------------------------------------------------- + +sal_Int32 FastSaxParser::GetNamespaceToken( const OUString& rNamespaceURL ) +{ + NamespaceMap::iterator aIter( maNamespaceMap.find( rNamespaceURL ) ); + if( aIter != maNamespaceMap.end() ) + return (*aIter).second; + else + return FastToken::DONTKNOW; +} + +// -------------------------------------------------------------------- + +OUString FastSaxParser::GetNamespaceURL( const OString& rPrefix ) throw (SAXException) +{ + Entity& rEntity = getEntity(); + if( !rEntity.maContextStack.empty() ) + { + sal_uInt32 nNamespace = rEntity.maContextStack.top()->mnNamespaceCount; + while( nNamespace-- ) + if( rEntity.maNamespaceDefines[nNamespace]->maPrefix == rPrefix ) + return rEntity.maNamespaceDefines[nNamespace]->maNamespaceURL; + } + + throw SAXException(); // prefix that has no defined namespace url +} + +OUString FastSaxParser::GetNamespaceURL( const sal_Char*pPrefix, int nPrefixLen ) throw(SAXException) +{ + Entity& rEntity = getEntity(); + if( pPrefix && !rEntity.maContextStack.empty() ) + { + sal_uInt32 nNamespace = rEntity.maContextStack.top()->mnNamespaceCount; + while( nNamespace-- ) + { + const OString& rPrefix( rEntity.maNamespaceDefines[nNamespace]->maPrefix ); + if( (rPrefix.getLength() == nPrefixLen) && + (strncmp( rPrefix.getStr(), pPrefix, nPrefixLen ) == 0 ) ) + { + return rEntity.maNamespaceDefines[nNamespace]->maNamespaceURL; + } + } + } + + throw SAXException(); // prefix that has no defined namespace url +} + +// -------------------------------------------------------------------- + +sal_Int32 FastSaxParser::GetTokenWithNamespaceURL( const OUString& rNamespaceURL, const sal_Char* pName, int nNameLen ) +{ + sal_Int32 nNamespaceToken = GetNamespaceToken( rNamespaceURL ); + + if( nNamespaceToken != FastToken::DONTKNOW ) + { + sal_Int32 nNameToken = GetToken( pName, nNameLen ); + if( nNameToken != FastToken::DONTKNOW ) + return nNamespaceToken | nNameToken; + } + + return FastToken::DONTKNOW; +} + +// -------------------------------------------------------------------- + +void FastSaxParser::splitName( const XML_Char *pwName, const XML_Char *&rpPrefix, sal_Int32 &rPrefixLen, const XML_Char *&rpName, sal_Int32 &rNameLen ) +{ + XML_Char *p; + for( p = const_cast< XML_Char* >( pwName ), rNameLen = 0, rPrefixLen = 0; *p; p++ ) + { + if( *p == ':' ) + { + rPrefixLen = p - pwName; + rNameLen = 0; + } + else + { + rNameLen++; + } + } + if( rPrefixLen ) + { + rpPrefix = pwName; + rpName = &pwName[ rPrefixLen + 1 ]; + } + else + { + rpPrefix = 0; + rpName = pwName; + } +} + +/*************** +* +* parseStream does Parser-startup initializations. The FastSaxParser::parse() method does +* the file-specific initialization work. (During a parser run, external files may be opened) +* +****************/ +void FastSaxParser::parseStream( const InputSource& maStructSource) throw (SAXException, IOException, RuntimeException) +{ + // Only one text at one time + MutexGuard guard( maMutex ); + + Entity entity( maData ); + entity.maStructSource = maStructSource; + + if( !entity.maStructSource.aInputStream.is() ) + throw SAXException( OUString( RTL_CONSTASCII_USTRINGPARAM( "No input source" ) ), Reference< XInterface >(), Any() ); + + entity.maConverter.setInputStream( entity.maStructSource.aInputStream ); + if( entity.maStructSource.sEncoding.getLength() ) + entity.maConverter.setEncoding( OUStringToOString( entity.maStructSource.sEncoding, RTL_TEXTENCODING_ASCII_US ) ); + + // create parser with proper encoding + entity.mpParser = XML_ParserCreate( 0 ); + if( !entity.mpParser ) + throw SAXException( OUString( RTL_CONSTASCII_USTRINGPARAM( "Couldn't create parser" ) ), Reference< XInterface >(), Any() ); + + // set all necessary C-Callbacks + XML_SetUserData( entity.mpParser, this ); + XML_SetElementHandler( entity.mpParser, call_callbackStartElement, call_callbackEndElement ); + XML_SetCharacterDataHandler( entity.mpParser, call_callbackCharacters ); + XML_SetExternalEntityRefHandler( entity.mpParser, call_callbackExternalEntityRef ); + + pushEntity( entity ); + try + { + // start the document + if( entity.mxDocumentHandler.is() ) + { + Reference< XLocator > xLoc( mxDocumentLocator.get() ); + entity.mxDocumentHandler->setDocumentLocator( xLoc ); + entity.mxDocumentHandler->startDocument(); + } + + parse(); + + // finish document + if( entity.mxDocumentHandler.is() ) + { + entity.mxDocumentHandler->endDocument(); + } + } + catch( SAXException & ) + { + popEntity(); + XML_ParserFree( entity.mpParser ); + throw; + } + catch( IOException & ) + { + popEntity(); + XML_ParserFree( entity.mpParser ); + throw; + } + catch( RuntimeException & ) + { + popEntity(); + XML_ParserFree( entity.mpParser ); + throw; + } + + popEntity(); + XML_ParserFree( entity.mpParser ); +} + +void FastSaxParser::setFastDocumentHandler( const Reference< XFastDocumentHandler >& Handler ) throw (RuntimeException) +{ + maData.mxDocumentHandler = Handler; +} + +void SAL_CALL FastSaxParser::setTokenHandler( const Reference< XFastTokenHandler >& Handler ) throw (RuntimeException) +{ + maData.mxTokenHandler = Handler; +} + +void SAL_CALL FastSaxParser::registerNamespace( const OUString& NamespaceURL, sal_Int32 NamespaceToken ) throw (IllegalArgumentException, RuntimeException) +{ + if( NamespaceToken >= FastToken::NAMESPACE ) + { + if( GetNamespaceToken( NamespaceURL ) == FastToken::DONTKNOW ) + { + maNamespaceMap[ NamespaceURL ] = NamespaceToken; + return; + } + } + throw IllegalArgumentException(); +} + +void FastSaxParser::setErrorHandler(const Reference< XErrorHandler > & Handler) throw (RuntimeException) +{ + maData.mxErrorHandler = Handler; +} + +void FastSaxParser::setEntityResolver(const Reference < XEntityResolver > & Resolver) throw (RuntimeException) +{ + maData.mxEntityResolver = Resolver; +} + +void FastSaxParser::setLocale( const Locale & Locale ) throw (RuntimeException) +{ + maData.maLocale = Locale; +} + +Sequence< OUString > FastSaxParser::getSupportedServiceNames_Static(void) +{ + Sequence<OUString> aRet(1); + aRet.getArray()[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(PARSER_SERVICE_NAME) ); + return aRet; +} + +// XServiceInfo +OUString FastSaxParser::getImplementationName() throw (RuntimeException) +{ + return OUString::createFromAscii( PARSER_IMPLEMENTATION_NAME ); +} + +// XServiceInfo +sal_Bool FastSaxParser::supportsService(const OUString& ServiceName) throw (RuntimeException) +{ + Sequence< OUString > aSNL = getSupportedServiceNames(); + const OUString * pArray = aSNL.getConstArray(); + + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + + return sal_False; +} + +// XServiceInfo +Sequence< OUString > FastSaxParser::getSupportedServiceNames(void) throw (RuntimeException) +{ + + Sequence<OUString> seq(1); + seq.getArray()[0] = OUString::createFromAscii( PARSER_SERVICE_NAME ); + return seq; +} + + +/*--------------------------------------- +* +* Helper functions and classes +* +*-------------------------------------------*/ + +namespace { + +OUString lclGetErrorMessage( XML_Error xmlE, const OUString& sSystemId, sal_Int32 nLine ) +{ + const sal_Char* pMessage = ""; + switch( xmlE ) + { + case XML_ERROR_NONE: pMessage = "No"; break; + case XML_ERROR_NO_MEMORY: pMessage = "no memory"; break; + case XML_ERROR_SYNTAX: pMessage = "syntax"; break; + case XML_ERROR_NO_ELEMENTS: pMessage = "no elements"; break; + case XML_ERROR_INVALID_TOKEN: pMessage = "invalid token"; break; + case XML_ERROR_UNCLOSED_TOKEN: pMessage = "unclosed token"; break; + case XML_ERROR_PARTIAL_CHAR: pMessage = "partial char"; break; + case XML_ERROR_TAG_MISMATCH: pMessage = "tag mismatch"; break; + case XML_ERROR_DUPLICATE_ATTRIBUTE: pMessage = "duplicate attribute"; break; + case XML_ERROR_JUNK_AFTER_DOC_ELEMENT: pMessage = "junk after doc element"; break; + case XML_ERROR_PARAM_ENTITY_REF: pMessage = "parameter entity reference"; break; + case XML_ERROR_UNDEFINED_ENTITY: pMessage = "undefined entity"; break; + case XML_ERROR_RECURSIVE_ENTITY_REF: pMessage = "recursive entity reference"; break; + case XML_ERROR_ASYNC_ENTITY: pMessage = "async entity"; break; + case XML_ERROR_BAD_CHAR_REF: pMessage = "bad char reference"; break; + case XML_ERROR_BINARY_ENTITY_REF: pMessage = "binary entity reference"; break; + case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: pMessage = "attribute external entity reference"; break; + case XML_ERROR_MISPLACED_XML_PI: pMessage = "misplaced xml processing instruction"; break; + case XML_ERROR_UNKNOWN_ENCODING: pMessage = "unknown encoding"; break; + case XML_ERROR_INCORRECT_ENCODING: pMessage = "incorrect encoding"; break; + case XML_ERROR_UNCLOSED_CDATA_SECTION: pMessage = "unclosed cdata section"; break; + case XML_ERROR_EXTERNAL_ENTITY_HANDLING: pMessage = "external entity reference"; break; + case XML_ERROR_NOT_STANDALONE: pMessage = "not standalone"; break; + default:; + } + + OUStringBuffer aBuffer( sal_Unicode( '[' ) ); + aBuffer.append( sSystemId ); + aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( " line " ) ); + aBuffer.append( nLine ); + aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "]: " ) ); + aBuffer.appendAscii( pMessage ); + aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( " error" ) ); + return aBuffer.makeStringAndClear(); +} + +} // namespace + +// starts parsing with actual parser ! +void FastSaxParser::parse() +{ + const int BUFFER_SIZE = 16 * 1024; + Sequence< sal_Int8 > seqOut( BUFFER_SIZE ); + + Entity& rEntity = getEntity(); + int nRead = 0; + do + { + nRead = rEntity.maConverter.readAndConvert( seqOut, BUFFER_SIZE ); + if( nRead <= 0 ) + { + XML_Parse( rEntity.mpParser, (const char*) seqOut.getConstArray(), 0, 1 ); + break; + } + + bool bContinue = XML_Parse( rEntity.mpParser, (const char*) seqOut.getConstArray(), nRead, 0 ) != 0; + // callbacks used inside XML_Parse may have caught an exception + if( !bContinue || rEntity.maSavedException.hasValue() ) + { + // Error during parsing ! + XML_Error xmlE = XML_GetErrorCode( rEntity.mpParser ); + OUString sSystemId = mxDocumentLocator->getSystemId(); + sal_Int32 nLine = mxDocumentLocator->getLineNumber(); + + SAXParseException aExcept( + lclGetErrorMessage( xmlE, sSystemId, nLine ), + Reference< XInterface >(), + Any( &rEntity.maSavedException, getCppuType( &rEntity.maSavedException ) ), + mxDocumentLocator->getPublicId(), + mxDocumentLocator->getSystemId(), + mxDocumentLocator->getLineNumber(), + mxDocumentLocator->getColumnNumber() + ); + + // error handler is set, it may throw the exception + if( rEntity.mxErrorHandler.is() ) + rEntity.mxErrorHandler->fatalError( Any( aExcept ) ); + + // error handler has not thrown, but parsing cannot go on, the + // exception MUST be thrown + throw aExcept; + } + } + while( nRead > 0 ); +} + +//------------------------------------------ +// +// The C-Callbacks +// +//----------------------------------------- + +namespace { + +struct AttributeData +{ + OString maPrefix; + OString maName; + OString maValue; +}; + +} // namespace + +void FastSaxParser::callbackStartElement( const XML_Char* pwName, const XML_Char** awAttributes ) +{ + Reference< XFastContextHandler > xParentContext; + Entity& rEntity = getEntity(); + if( !rEntity.maContextStack.empty() ) + { + xParentContext = rEntity.maContextStack.top()->mxContext; + if( !xParentContext.is() ) + { + // we ignore current elements, so no processing needed + pushContext(); + return; + } + } + + pushContext(); + + rEntity.mxAttributes->clear(); + + // create attribute map and process namespace instructions + int i = 0; + sal_Int32 nNameLen, nPrefixLen; + const XML_Char *pName; + const XML_Char *pPrefix; + + try + { + /* #158414# Each element may define new namespaces, also for attribues. + First, process all namespace attributes and cache other attributes in a + vector. Second, process the attributes after namespaces have been + initialized. */ + ::std::vector< AttributeData > aAttribs; + + // #158414# first: get namespaces + for( ; awAttributes[i]; i += 2 ) + { + OSL_ASSERT( awAttributes[i+1] ); + + splitName( awAttributes[i], pPrefix, nPrefixLen, pName, nNameLen ); + if( nPrefixLen ) + { + if( (nPrefixLen == 5) && (strncmp( pPrefix, "xmlns", 5 ) == 0) ) + { + DefineNamespace( OString( pName, nNameLen ), awAttributes[i+1] ); + } + else + { + aAttribs.resize( aAttribs.size() + 1 ); + aAttribs.back().maPrefix = OString( pPrefix, nPrefixLen ); + aAttribs.back().maName = OString( pName, nNameLen ); + aAttribs.back().maValue = OString( awAttributes[i+1] ); + } + } + else + { + if( (nNameLen == 5) && (strcmp( pName, "xmlns" ) == 0) ) + { + // namespace of the element found + rEntity.maContextStack.top()->maNamespace = OUString( awAttributes[i+1], strlen( awAttributes[i+1] ), RTL_TEXTENCODING_UTF8 ); + } + else + { + aAttribs.resize( aAttribs.size() + 1 ); + aAttribs.back().maName = OString( pName, nNameLen ); + aAttribs.back().maValue = OString( awAttributes[i+1] ); + } + } + } + + // #158414# second: fill attribute list with other attributes + for( ::std::vector< AttributeData >::const_iterator aIt = aAttribs.begin(), aEnd = aAttribs.end(); aIt != aEnd; ++aIt ) + { + if( aIt->maPrefix.getLength() > 0 ) + { + sal_Int32 nAttributeToken = GetTokenWithPrefix( aIt->maPrefix, aIt->maName ); + if( nAttributeToken != FastToken::DONTKNOW ) + rEntity.mxAttributes->add( nAttributeToken, aIt->maValue ); + else + rEntity.mxAttributes->addUnknown( GetNamespaceURL( aIt->maPrefix ), aIt->maName, aIt->maValue ); + } + else + { + sal_Int32 nAttributeToken = GetToken( aIt->maName ); + if( nAttributeToken != FastToken::DONTKNOW ) + rEntity.mxAttributes->add( nAttributeToken, aIt->maValue ); + else + rEntity.mxAttributes->addUnknown( aIt->maName, aIt->maValue ); + } + } + + sal_Int32 nElementToken; + splitName( pwName, pPrefix, nPrefixLen, pName, nNameLen ); + if( nPrefixLen > 0 ) + nElementToken = GetTokenWithPrefix( pPrefix, nPrefixLen, pName, nNameLen ); + else if( rEntity.maContextStack.top()->maNamespace.getLength() > 0 ) + nElementToken = GetTokenWithNamespaceURL( rEntity.maContextStack.top()->maNamespace, pName, nNameLen ); + else + nElementToken = GetToken( pName ); + rEntity.maContextStack.top()->mnElementToken = nElementToken; + + Reference< XFastAttributeList > xAttr( rEntity.mxAttributes.get() ); + Reference< XFastContextHandler > xContext; + if( nElementToken == FastToken::DONTKNOW ) + { + if( nPrefixLen > 0 ) + rEntity.maContextStack.top()->maNamespace = GetNamespaceURL( pPrefix, nPrefixLen ); + + const OUString aNamespace( rEntity.maContextStack.top()->maNamespace ); + const OUString aElementName( pPrefix, nPrefixLen, RTL_TEXTENCODING_UTF8 ); + rEntity.maContextStack.top()->maElementName = aElementName; + + if( xParentContext.is() ) + xContext = xParentContext->createUnknownChildContext( aNamespace, aElementName, xAttr ); + else + xContext = rEntity.mxDocumentHandler->createUnknownChildContext( aNamespace, aElementName, xAttr ); + + if( xContext.is() ) + { + rEntity.maContextStack.top()->mxContext = xContext; + xContext->startUnknownElement( aNamespace, aElementName, xAttr ); + } + } + else + { + if( xParentContext.is() ) + xContext = xParentContext->createFastChildContext( nElementToken, xAttr ); + else + xContext = rEntity.mxDocumentHandler->createFastChildContext( nElementToken, xAttr ); + + + if( xContext.is() ) + { + rEntity.maContextStack.top()->mxContext = xContext; + xContext->startFastElement( nElementToken, xAttr ); + } + } + } + catch( Exception& e ) + { + rEntity.maSavedException <<= e; + } +} + +void FastSaxParser::callbackEndElement( const XML_Char* ) +{ + Entity& rEntity = getEntity(); + OSL_ENSURE( !rEntity.maContextStack.empty(), "FastSaxParser::callbackEndElement - no context" ); + if( !rEntity.maContextStack.empty() ) + { + SaxContextImplPtr pContext = rEntity.maContextStack.top(); + const Reference< XFastContextHandler >& xContext( pContext->mxContext ); + if( xContext.is() ) try + { + sal_Int32 nElementToken = pContext->mnElementToken; + if( nElementToken != FastToken::DONTKNOW ) + xContext->endFastElement( nElementToken ); + else + xContext->endUnknownElement( pContext->maNamespace, pContext->maElementName ); + } + catch( Exception& e ) + { + rEntity.maSavedException <<= e; + } + + popContext(); + } +} + + +void FastSaxParser::callbackCharacters( const XML_Char* s, int nLen ) +{ + Entity& rEntity = getEntity(); + const Reference< XFastContextHandler >& xContext( rEntity.maContextStack.top()->mxContext ); + if( xContext.is() ) try + { + xContext->characters( OUString( s, nLen, RTL_TEXTENCODING_UTF8 ) ); + } + catch( Exception& e ) + { + rEntity.maSavedException <<= e; + } +} + +int FastSaxParser::callbackExternalEntityRef( XML_Parser parser, + const XML_Char *context, const XML_Char * /*base*/, const XML_Char *systemId, const XML_Char *publicId ) +{ + bool bOK = true; + InputSource source; + + Entity& rCurrEntity = getEntity(); + Entity aNewEntity( rCurrEntity ); + + if( rCurrEntity.mxEntityResolver.is() ) try + { + aNewEntity.maStructSource = rCurrEntity.mxEntityResolver->resolveEntity( + OUString( publicId, strlen( publicId ), RTL_TEXTENCODING_UTF8 ) , + OUString( systemId, strlen( systemId ), RTL_TEXTENCODING_UTF8 ) ); + } + catch( SAXParseException & e ) + { + rCurrEntity.maSavedException <<= e; + bOK = false; + } + catch( SAXException & e ) + { + rCurrEntity.maSavedException <<= SAXParseException( + e.Message, e.Context, e.WrappedException, + mxDocumentLocator->getPublicId(), + mxDocumentLocator->getSystemId(), + mxDocumentLocator->getLineNumber(), + mxDocumentLocator->getColumnNumber() ); + bOK = false; + } + + if( aNewEntity.maStructSource.aInputStream.is() ) + { + aNewEntity.mpParser = XML_ExternalEntityParserCreate( parser, context, 0 ); + if( !aNewEntity.mpParser ) + { + return false; + } + + aNewEntity.maConverter.setInputStream( aNewEntity.maStructSource.aInputStream ); + pushEntity( aNewEntity ); + try + { + parse(); + } + catch( SAXParseException & e ) + { + rCurrEntity.maSavedException <<= e; + bOK = false; + } + catch( IOException &e ) + { + SAXException aEx; + aEx.WrappedException <<= e; + rCurrEntity.maSavedException <<= aEx; + bOK = false; + } + catch( RuntimeException &e ) + { + SAXException aEx; + aEx.WrappedException <<= e; + rCurrEntity.maSavedException <<= aEx; + bOK = false; + } + + popEntity(); + XML_ParserFree( aNewEntity.mpParser ); + } + + return bOK; +} + +} // namespace sax_fastparser diff --git a/sax/source/fastparser/fastparser.hxx b/sax/source/fastparser/fastparser.hxx new file mode 100644 index 000000000000..a0331fe4035a --- /dev/null +++ b/sax/source/fastparser/fastparser.hxx @@ -0,0 +1,164 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SAX_FASTPARSER_HXX_ +#define _SAX_FASTPARSER_HXX_ + +#include <vector> +#include <stack> +#include <hash_map> +#include <boost/shared_ptr.hpp> +#include <rtl/ref.hxx> +#include <com/sun/star/xml/sax/XFastParser.hpp> +#include <com/sun/star/xml/sax/XFastTokenHandler.hpp> +#include <com/sun/star/xml/sax/XFastDocumentHandler.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase2.hxx> + +#include <expat.h> +#include "xml2utf.hxx" + +#include <sax/fastattribs.hxx> + +#define PARSER_IMPLEMENTATION_NAME "com.sun.star.comp.extensions.xml.sax.FastParser" +#define PARSER_SERVICE_NAME "com.sun.star.xml.sax.FastParser" + +namespace sax_fastparser { + +class FastLocatorImpl; +struct NamespaceDefine; +struct SaxContextImpl; + +typedef ::boost::shared_ptr< SaxContextImpl > SaxContextImplPtr; +typedef ::boost::shared_ptr< NamespaceDefine > NamespaceDefineRef; + +typedef ::std::hash_map< ::rtl::OUString, sal_Int32, + ::rtl::OUStringHash, ::std::equal_to< ::rtl::OUString > > NamespaceMap; + +// -------------------------------------------------------------------- + +struct ParserData +{ + ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastDocumentHandler > mxDocumentHandler; + ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler > mxTokenHandler; + ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XErrorHandler > mxErrorHandler; + ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XEntityResolver > mxEntityResolver; + ::com::sun::star::lang::Locale maLocale; + + ParserData(); + ~ParserData(); +}; + +// -------------------------------------------------------------------- + +// Entity binds all information needed for a single file +struct Entity : public ParserData +{ + ::com::sun::star::xml::sax::InputSource maStructSource; + XML_Parser mpParser; + ::sax_expatwrap::XMLFile2UTFConverter maConverter; + ::rtl::Reference< FastAttributeList > mxAttributes; + + // Exceptions cannot be thrown through the C-XmlParser (possible resource leaks), + // therefore the exception must be saved somewhere. + ::com::sun::star::uno::Any maSavedException; + + ::std::stack< SaxContextImplPtr > maContextStack; + ::std::vector< NamespaceDefineRef > maNamespaceDefines; + + explicit Entity( const ParserData& rData ); + ~Entity(); +}; + +// -------------------------------------------------------------------- + +// This class implements the external Parser interface +class FastSaxParser : public ::cppu::WeakImplHelper2< ::com::sun::star::xml::sax::XFastParser, ::com::sun::star::lang::XServiceInfo > +{ +public: + FastSaxParser(); + virtual ~FastSaxParser(); + + // The implementation details + static ::com::sun::star::uno::Sequence< ::rtl::OUString > getSupportedServiceNames_Static(void); + + // XFastParser + virtual void SAL_CALL parseStream( const ::com::sun::star::xml::sax::InputSource& aInputSource ) throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setFastDocumentHandler( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastDocumentHandler >& Handler ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setTokenHandler( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler >& Handler ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL registerNamespace( const ::rtl::OUString& NamespaceURL, sal_Int32 NamespaceToken ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setErrorHandler( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XErrorHandler >& Handler ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setEntityResolver( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XEntityResolver >& Resolver ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setLocale( const ::com::sun::star::lang::Locale& rLocale ) throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException); + + // called by the C callbacks of the expat parser + void callbackStartElement( const XML_Char* name, const XML_Char** atts ); + void callbackEndElement( const XML_Char* name ); + void callbackCharacters( const XML_Char* s, int nLen ); + int callbackExternalEntityRef( XML_Parser parser, const XML_Char *openEntityNames, const XML_Char *base, const XML_Char *systemId, const XML_Char *publicId); + + inline void pushEntity( const Entity& rEntity ) { maEntities.push( rEntity ); } + inline void popEntity() { maEntities.pop(); } + Entity& getEntity() { return maEntities.top(); } + +private: + void parse(); + + sal_Int32 GetToken( const ::rtl::OString& rToken ); + sal_Int32 GetToken( const sal_Char* pToken, sal_Int32 nTokenLen = 0 ); + sal_Int32 GetTokenWithPrefix( const ::rtl::OString& rPrefix, const ::rtl::OString& rName ) throw (::com::sun::star::xml::sax::SAXException); + sal_Int32 GetTokenWithPrefix( const sal_Char*pPrefix, int nPrefixLen, const sal_Char* pName, int nNameLen ) throw (::com::sun::star::xml::sax::SAXException); + ::rtl::OUString GetNamespaceURL( const ::rtl::OString& rPrefix ) throw (::com::sun::star::xml::sax::SAXException); + ::rtl::OUString GetNamespaceURL( const sal_Char*pPrefix, int nPrefixLen ) throw (::com::sun::star::xml::sax::SAXException); + sal_Int32 GetNamespaceToken( const ::rtl::OUString& rNamespaceURL ); + sal_Int32 GetTokenWithNamespaceURL( const ::rtl::OUString& rNamespaceURL, const sal_Char* pName, int nNameLen ); + void DefineNamespace( const ::rtl::OString& rPrefix, const sal_Char* pNamespaceURL ); + sal_Int32 CreateCustomToken( const sal_Char* pToken, int len = 0 ); + + void pushContext(); + void popContext(); + + void splitName( const XML_Char *pwName, const XML_Char *&rpPrefix, sal_Int32 &rPrefixLen, const XML_Char *&rpName, sal_Int32 &rNameLen ); + +private: + ::osl::Mutex maMutex; + + ::rtl::Reference< FastLocatorImpl > mxDocumentLocator; + NamespaceMap maNamespaceMap; + + ParserData maData; /// Cached parser configuration for next call of parseStream(). + ::std::stack< Entity > maEntities; /// Entity stack for each call of parseStream(). +}; + +} + +#endif // _SAX_FASTPARSER_HXX_ diff --git a/sax/source/fastparser/fastsax.component b/sax/source/fastparser/fastsax.component new file mode 100644 index 000000000000..a184a76d2d83 --- /dev/null +++ b/sax/source/fastparser/fastsax.component @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--********************************************************************** +* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* Copyright 2000, 2010 Oracle and/or its affiliates. +* +* OpenOffice.org - a multi-platform office productivity suite +* +* This file is part of OpenOffice.org. +* +* OpenOffice.org is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 3 +* only, as published by the Free Software Foundation. +* +* OpenOffice.org is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License version 3 for more details +* (a copy is included in the LICENSE file that accompanied this code). +* +* You should have received a copy of the GNU Lesser General Public License +* version 3 along with OpenOffice.org. If not, see +* <http://www.openoffice.org/license.html> +* for a copy of the LGPLv3 License. +* +**********************************************************************--> + +<component loader="com.sun.star.loader.SharedLibrary" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.extensions.xml.sax.FastParser"> + <service name="com.sun.star.xml.sax.FastParser"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.xml.sax.FastSerializer"> + <service name="com.sun.star.xml.sax.FastSerializer"/> + </implementation> +</component> diff --git a/sax/source/fastparser/makefile.mk b/sax/source/fastparser/makefile.mk new file mode 100644 index 000000000000..d8f9378c19b9 --- /dev/null +++ b/sax/source/fastparser/makefile.mk @@ -0,0 +1,79 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ=..$/.. + +PRJNAME=sax +TARGET=fastsax.uno +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +DLLPRE = + +.IF "$(SYSTEM_ZLIB)" == "YES" +CFLAGS+=-DSYSTEM_ZLIB +.ENDIF + +.IF "$(SYSTEM_EXPAT)" == "YES" +CFLAGS+=-DSYSTEM_EXPAT +.ENDIF + +#----------------------------------------------------------- + +SLOFILES =\ + $(SLO)$/facreg.obj\ + $(SLO)$/fastparser.obj\ + $(SLO)$/xml2utf.obj + +SHL1TARGET= $(TARGET) +SHL1IMPLIB= i$(TARGET) + +SHL1STDLIBS= \ + $(SALLIB) \ + $(SAXLIB) \ + $(CPPULIB) \ + $(CPPUHELPERLIB)\ + $(EXPATASCII3RDLIB) + +SHL1DEPN= +SHL1VERSIONMAP= $(SOLARENV)$/src$/component.map +SHL1LIBS= $(SLB)$/$(TARGET).lib +SHL1DEF= $(MISC)$/$(SHL1TARGET).def +DEF1NAME= $(SHL1TARGET) + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +ALLTAR : $(MISC)/fastsax.component + +$(MISC)/fastsax.component .ERRREMOVE : $(SOLARENV)/bin/createcomponent.xslt \ + fastsax.component + $(XSLTPROC) --nonet --stringparam uri \ + '$(COMPONENTPREFIX_BASIS_NATIVE)$(SHL1TARGETN:f)' -o $@ \ + $(SOLARENV)/bin/createcomponent.xslt fastsax.component diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx new file mode 100644 index 000000000000..5df3044bd6d3 --- /dev/null +++ b/sax/source/tools/converter.cxx @@ -0,0 +1,2098 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +#include <com/sun/star/i18n/UnicodeType.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Duration.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include <rtl/ustrbuf.hxx> +#include <rtl/math.hxx> +#include "sax/tools/converter.hxx" + +using namespace rtl; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::util; +//using namespace com::sun::star::text; +//using namespace com::sun::star::style; +using namespace ::com::sun::star::i18n; + +namespace sax { + +static const sal_Char* gpsMM = "mm"; +static const sal_Char* gpsCM = "cm"; +static const sal_Char* gpsPT = "pt"; +static const sal_Char* gpsINCH = "in"; +static const sal_Char* gpsPC = "pc"; + +const sal_Int8 XML_MAXDIGITSCOUNT_TIME = 11; +const sal_Int8 XML_MAXDIGITSCOUNT_DATETIME = 6; +#define XML_NULLDATE "NullDate" + +/** convert string to measure using optional min and max values*/ +bool Converter::convertMeasure( sal_Int32& rValue, + const OUString& rString, + sal_Int16 nTargetUnit /* = MeasureUnit::MM_100TH */, + sal_Int32 nMin /* = SAL_MIN_INT32 */, + sal_Int32 nMax /* = SAL_MAX_INT32 */ ) +{ + bool bNeg = false; + double nVal = 0; + + sal_Int32 nPos = 0L; + sal_Int32 nLen = rString.getLength(); + + // skip white space + while( (nPos < nLen) && (rString[nPos] <= sal_Unicode(' ')) ) + nPos++; + + if( nPos < nLen && sal_Unicode('-') == rString[nPos] ) + { + bNeg = true; + nPos++; + } + + // get number + while( nPos < nLen && + sal_Unicode('0') <= rString[nPos] && + sal_Unicode('9') >= rString[nPos] ) + { + // TODO: check overflow! + nVal *= 10; + nVal += (rString[nPos] - sal_Unicode('0')); + nPos++; + } + double nDiv = 1.; + if( nPos < nLen && sal_Unicode('.') == rString[nPos] ) + { + nPos++; + + while( nPos < nLen && + sal_Unicode('0') <= rString[nPos] && + sal_Unicode('9') >= rString[nPos] ) + { + // TODO: check overflow! + nDiv *= 10; + nVal += ( ((double)(rString[nPos] - sal_Unicode('0'))) / nDiv ); + nPos++; + } + } + + // skip white space + while( (nPos < nLen) && (rString[nPos] <= sal_Unicode(' ')) ) + nPos++; + + if( nPos < nLen ) + { + + if( MeasureUnit::PERCENT == nTargetUnit ) + { + if( sal_Unicode('%') != rString[nPos] ) + return false; + } + else if( MeasureUnit::PIXEL == nTargetUnit ) + { + if( nPos + 1 >= nLen || + (sal_Unicode('p') != rString[nPos] && + sal_Unicode('P') != rString[nPos])|| + (sal_Unicode('x') != rString[nPos+1] && + sal_Unicode('X') != rString[nPos+1]) ) + return false; + } + else + { + OSL_ENSURE( MeasureUnit::TWIP == nTargetUnit || MeasureUnit::POINT == nTargetUnit || + MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit, "unit is not supported"); + const sal_Char *aCmpsL[2] = { 0, 0 }; + const sal_Char *aCmpsU[2] = { 0, 0 }; + double aScales[2] = { 1., 1. }; + + if( MeasureUnit::TWIP == nTargetUnit ) + { + switch( rString[nPos] ) + { + case sal_Unicode('c'): + case sal_Unicode('C'): + aCmpsL[0] = "cm"; + aCmpsU[0] = "CM"; + aScales[0] = (72.*20.)/2.54; // twip + break; + case sal_Unicode('i'): + case sal_Unicode('I'): + aCmpsL[0] = "in"; + aCmpsU[0] = "IN"; + aScales[0] = 72.*20.; // twip + break; + case sal_Unicode('m'): + case sal_Unicode('M'): + aCmpsL[0] = "mm"; + aCmpsU[0] = "MM"; + aScales[0] = (72.*20.)/25.4; // twip + break; + case sal_Unicode('p'): + case sal_Unicode('P'): + aCmpsL[0] = "pt"; + aCmpsU[0] = "PT"; + aScales[0] = 20.; // twip + + aCmpsL[1] = "pc"; + aCmpsU[1] = "PC"; + aScales[1] = 12.*20.; // twip + break; + } + } + else if( MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit ) + { + double nScaleFactor = (MeasureUnit::MM_100TH == nTargetUnit) ? 100.0 : 10.0; + switch( rString[nPos] ) + { + case sal_Unicode('c'): + case sal_Unicode('C'): + aCmpsL[0] = "cm"; + aCmpsU[0] = "CM"; + aScales[0] = 10.0 * nScaleFactor; // mm/100 + break; + case sal_Unicode('i'): + case sal_Unicode('I'): + aCmpsL[0] = "in"; + aCmpsU[0] = "IN"; + aScales[0] = 1000.*2.54; // mm/100 + break; + case sal_Unicode('m'): + case sal_Unicode('M'): + aCmpsL[0] = "mm"; + aCmpsU[0] = "MM"; + aScales[0] = 1.0 * nScaleFactor; // mm/100 + break; + case sal_Unicode('p'): + case sal_Unicode('P'): + aCmpsL[0] = "pt"; + aCmpsU[0] = "PT"; + aScales[0] = (10.0 * nScaleFactor*2.54)/72.; // mm/100 + + aCmpsL[1] = "pc"; + aCmpsU[1] = "PC"; + aScales[1] = (10.0 * nScaleFactor*2.54)/12.; // mm/100 + break; + } + } + else if( MeasureUnit::POINT == nTargetUnit ) + { + if( rString[nPos] == 'p' || rString[nPos] == 'P' ) + { + aCmpsL[0] = "pt"; + aCmpsU[0] = "PT"; + aScales[0] = 1; + } + } + + if( aCmpsL[0] == NULL ) + return false; + + double nScale = 0.; + for( sal_uInt16 i= 0; i < 2; i++ ) + { + const sal_Char *pL = aCmpsL[i]; + if( pL ) + { + const sal_Char *pU = aCmpsU[i]; + while( nPos < nLen && *pL ) + { + sal_Unicode c = rString[nPos]; + if( c != *pL && c != *pU ) + break; + pL++; + pU++; + nPos++; + } + if( !*pL && (nPos == nLen || ' ' == rString[nPos]) ) + { + nScale = aScales[i]; + break; + } + } + } + + if( 0. == nScale ) + return false; + + // TODO: check overflow + if( nScale != 1. ) + nVal *= nScale; + } + } + + nVal += .5; + if( bNeg ) + nVal = -nVal; + + if( nVal <= (double)nMin ) + rValue = nMin; + else if( nVal >= (double)nMax ) + rValue = nMax; + else + rValue = (sal_Int32)nVal; + + return true; +} + +/** convert measure in given unit to string with given unit */ +void Converter::convertMeasure( OUStringBuffer& rBuffer, + sal_Int32 nMeasure, + sal_Int16 nSourceUnit /* = MeasureUnit::MM_100TH */, + sal_Int16 nTargetUnit /* = MeasureUnit::INCH */ ) +{ + OSL_ENSURE( false, "Converter::convertMeasure - not implemented, tools/BigInt needs replacement" ); + (void)rBuffer; + (void)nMeasure; + (void)nSourceUnit; + (void)nTargetUnit; +#if 0 + if( nSourceUnit == MeasureUnit::PERCENT ) + { + OSL_ENSURE( nTargetUnit == MeasureUnit::PERCENT, + "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" ); + + rBuffer.append( nMeasure ); + rBuffer.append( sal_Unicode('%' ) ); + } + else + { + // the sign is processed seperatly + if( nMeasure < 0 ) + { + nMeasure = -nMeasure; + rBuffer.append( sal_Unicode('-') ); + } + + // The new length is (nVal * nMul)/(nDiv*nFac*10) + long nMul = 1000; + long nDiv = 1; + long nFac = 100; + const sal_Char* psUnit = 0; + switch( nSourceUnit ) + { + case MeasureUnit::TWIP: + switch( nTargetUnit ) + { + case MeasureUnit::MM_100TH: + case MeasureUnit::MM_10TH: + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,"output unit not supported for twip values" ); + case MeasureUnit::MM: + // 0.01mm = 0.57twip (exactly) + nMul = 25400; // 25.4 * 1000 + nDiv = 1440; // 72 * 20; + nFac = 100; + psUnit = gpsMM; + break; + + case MeasureUnit::CM: + // 0.001cm = 0.57twip (exactly) + nMul = 25400; // 2.54 * 10000 + nDiv = 1440; // 72 * 20; + nFac = 1000; + psUnit = gpsCM; + break; + + case MeasureUnit::POINT: + // 0.01pt = 0.2twip (exactly) + nMul = 1000; + nDiv = 20; + nFac = 100; + psUnit = gpsPT; + break; + + case MeasureUnit::INCH: + default: + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, + "output unit not supported for twip values" ); + // 0.0001in = 0.144twip (exactly) + nMul = 100000; + nDiv = 1440; // 72 * 20; + nFac = 10000; + psUnit = gpsINCH; + break; + } + break; + + case MeasureUnit::POINT: + // 1pt = 1pt (exactly) + OSL_ENSURE( MeasureUnit::POINT == nTargetUnit, + "output unit not supported for pt values" ); + nMul = 10; + nDiv = 1; + nFac = 1; + psUnit = gpsPT; + break; + case MeasureUnit::MM_10TH: + case MeasureUnit::MM_100TH: + { + long nFac2 = (MeasureUnit::MM_100TH == nSourceUnit) ? 100 : 10; + switch( nTargetUnit ) + { + case MeasureUnit::MM_100TH: + case MeasureUnit::MM_10TH: + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, + "output unit not supported for 1/100mm values" ); + case MeasureUnit::MM: + // 0.01mm = 1 mm/100 (exactly) + nMul = 10; + nDiv = 1; + nFac = nFac2; + psUnit = gpsMM; + break; + + case MeasureUnit::CM: + // 0.001mm = 1 mm/100 (exactly) + nMul = 10; + nDiv = 1; // 72 * 20; + nFac = 10*nFac2; + psUnit = gpsCM; + break; + + case MeasureUnit::POINT: + // 0.01pt = 0.35 mm/100 (exactly) + nMul = 72000; + nDiv = 2540; + nFac = nFac2; + psUnit = gpsPT; + break; + + case MeasureUnit::INCH: + default: + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, + "output unit not supported for 1/100mm values" ); + // 0.0001in = 0.254 mm/100 (exactly) + nMul = 100000; + nDiv = 2540; + nFac = 100*nFac2; + psUnit = gpsINCH; + break; + } + break; + } + } + + long nLongVal = 0; + bool bOutLongVal = true; + if( nMeasure > SAL_INT32_MAX / nMul ) + { + // A big int is required for calculation + BigInt nBigVal( nMeasure ); + BigInt nBigFac( nFac ); + nBigVal *= nMul; + nBigVal /= nDiv; + nBigVal += 5; + nBigVal /= 10; + + if( nBigVal.IsLong() ) + { + // To convert the value into a string a long is sufficient + nLongVal = (long)nBigVal; + } + else + { + BigInt nBigFac2( nFac ); + BigInt nBig10( 10 ); + rBuffer.append( (sal_Int32)(nBigVal / nBigFac2) ); + if( !(nBigVal % nBigFac2).IsZero() ) + { + rBuffer.append( sal_Unicode('.') ); + while( nFac > 1 && !(nBigVal % nBigFac2).IsZero() ) + { + nFac /= 10; + nBigFac2 = nFac; + rBuffer.append( (sal_Int32)((nBigVal / nBigFac2) % nBig10 ) ); + } + } + bOutLongVal = false; + } + } + else + { + nLongVal = nMeasure * nMul; + nLongVal /= nDiv; + nLongVal += 5; + nLongVal /= 10; + } + + if( bOutLongVal ) + { + rBuffer.append( (sal_Int32)(nLongVal / nFac) ); + if( nFac > 1 && (nLongVal % nFac) != 0 ) + { + rBuffer.append( sal_Unicode('.') ); + while( nFac > 1 && (nLongVal % nFac) != 0 ) + { + nFac /= 10; + rBuffer.append( (sal_Int32)((nLongVal / nFac) % 10) ); + } + } + } + + if( psUnit ) + rBuffer.appendAscii( psUnit ); + } +#endif +} + +static const OUString& getTrueString() +{ + static const OUString sTrue( RTL_CONSTASCII_USTRINGPARAM( "true" ) ); + return sTrue; +} + +static const OUString& getFalseString() +{ + static const OUString sFalse( RTL_CONSTASCII_USTRINGPARAM( "false" ) ); + return sFalse; +} + +/** convert string to boolean */ +bool Converter::convertBool( bool& rBool, const OUString& rString ) +{ + rBool = rString == getTrueString(); + + return rBool || (rString == getFalseString()); +} + +/** convert boolean to string */ +void Converter::convertBool( OUStringBuffer& rBuffer, bool bValue ) +{ + rBuffer.append( bValue ? getTrueString() : getFalseString() ); +} + +/** convert string to percent */ +bool Converter::convertPercent( sal_Int32& rPercent, const OUString& rString ) +{ + return convertMeasure( rPercent, rString, MeasureUnit::PERCENT ); +} + +/** convert percent to string */ +void Converter::convertPercent( OUStringBuffer& rBuffer, sal_Int32 nValue ) +{ + rBuffer.append( nValue ); + rBuffer.append( sal_Unicode('%' ) ); +} + +/** convert string to pixel measure */ +bool Converter::convertMeasurePx( sal_Int32& rPixel, const OUString& rString ) +{ + return convertMeasure( rPixel, rString, MeasureUnit::PIXEL ); +} + +/** convert pixel measure to string */ +void Converter::convertMeasurePx( OUStringBuffer& rBuffer, sal_Int32 nValue ) +{ + rBuffer.append( nValue ); + rBuffer.append( sal_Unicode('p' ) ); + rBuffer.append( sal_Unicode('x' ) ); +} + +int lcl_gethex( int nChar ) +{ + if( nChar >= '0' && nChar <= '9' ) + return nChar - '0'; + else if( nChar >= 'a' && nChar <= 'f' ) + return nChar - 'a' + 10; + else if( nChar >= 'A' && nChar <= 'F' ) + return nChar - 'A' + 10; + else + return 0; +} + +/** convert string to color */ +bool Converter::convertColor( sal_Int32& rColor, const OUString& rValue ) +{ + if( rValue.getLength() != 7 || rValue[0] != '#' ) + return false; + + rColor = lcl_gethex( rValue[1] ) * 16 + lcl_gethex( rValue[2] ); + rColor <<= 8; + + rColor |= ( lcl_gethex( rValue[3] ) * 16 + lcl_gethex( rValue[4] ) ); + rColor <<= 8; + + rColor |= ( lcl_gethex( rValue[5] ) * 16 + lcl_gethex( rValue[6] ) ); + + return true; +} + +static sal_Char aHexTab[] = "0123456789abcdef"; + +/** convert color to string */ +void Converter::convertColor( OUStringBuffer& rBuffer, sal_Int32 nColor ) +{ + rBuffer.append( sal_Unicode( '#' ) ); + + sal_uInt8 nCol = (sal_uInt8)(nColor >> 16); + rBuffer.append( sal_Unicode( aHexTab[ nCol >> 4 ] ) ); + rBuffer.append( sal_Unicode( aHexTab[ nCol & 0xf ] ) ); + + nCol = (sal_uInt8)(nColor >> 8); + rBuffer.append( sal_Unicode( aHexTab[ nCol >> 4 ] ) ); + rBuffer.append( sal_Unicode( aHexTab[ nCol & 0xf ] ) ); + + nCol = (sal_uInt8)nColor; + rBuffer.append( sal_Unicode( aHexTab[ nCol >> 4 ] ) ); + rBuffer.append( sal_Unicode( aHexTab[ nCol & 0xf ] ) ); +} + +/** convert number to string */ +void Converter::convertNumber( OUStringBuffer& rBuffer, sal_Int32 nNumber ) +{ + rBuffer.append( nNumber ); +} + +/** convert string to number with optional min and max values */ +bool Converter::convertNumber( sal_Int32& rValue, + const OUString& rString, + sal_Int32 nMin, sal_Int32 nMax ) +{ + bool bNeg = false; + rValue = 0; + + sal_Int32 nPos = 0L; + sal_Int32 nLen = rString.getLength(); + + // skip white space + while( (nPos < nLen) && (rString[nPos] <= sal_Unicode(' ')) ) + nPos++; + + if( nPos < nLen && sal_Unicode('-') == rString[nPos] ) + { + bNeg = true; + nPos++; + } + + // get number + while( nPos < nLen && + sal_Unicode('0') <= rString[nPos] && + sal_Unicode('9') >= rString[nPos] ) + { + // TODO: check overflow! + rValue *= 10; + rValue += (rString[nPos] - sal_Unicode('0')); + nPos++; + } + + if( bNeg ) + rValue *= -1; + + if( rValue < nMin ) + rValue = nMin; + else if( rValue > nMax ) + rValue = nMax; + + return nPos == nLen; +} + +/** convert double number to string (using ::rtl::math) */ +void Converter::convertDouble( OUStringBuffer& rBuffer, + double fNumber, + bool bWriteUnits, + sal_Int16 nSourceUnit, + sal_Int16 nTargetUnit) +{ + if(MeasureUnit::PERCENT == nSourceUnit) + { + OSL_ENSURE( nTargetUnit == MeasureUnit::PERCENT, "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" ); + ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true); + if(bWriteUnits) + rBuffer.append(sal_Unicode('%')); + } + else + { + OUStringBuffer sUnit; + double fFactor = GetConversionFactor(sUnit, nSourceUnit, nTargetUnit); + if(fFactor != 1.0) + fNumber *= fFactor; + ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true); + if(bWriteUnits) + rBuffer.append(sUnit); + } +} + +/** convert double number to string (using ::rtl::math) */ +void Converter::convertDouble( ::rtl::OUStringBuffer& rBuffer, double fNumber) +{ + ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true); +} + +/** convert string to double number (using ::rtl::math) */ +bool Converter::convertDouble(double& rValue, + const ::rtl::OUString& rString, sal_Int16 nTargetUnit) +{ + sal_Int16 nSourceUnit = GetUnitFromString(rString, nTargetUnit); + + return convertDouble(rValue, rString, nSourceUnit, nTargetUnit ); +} + +/** convert string to double number (using ::rtl::math) */ +bool Converter::convertDouble(double& rValue, + const ::rtl::OUString& rString, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit) +{ + rtl_math_ConversionStatus eStatus; + rValue = ::rtl::math::stringToDouble( rString, (sal_Unicode)('.'), (sal_Unicode)(','), &eStatus, NULL ); + + if(eStatus == rtl_math_ConversionStatus_Ok) + { + OUStringBuffer sUnit; + double fFactor = GetConversionFactor(sUnit, nSourceUnit, nTargetUnit); + if(fFactor != 1.0 && fFactor != 0.0) + rValue /= fFactor; + } + + return ( eStatus == rtl_math_ConversionStatus_Ok ); +} + +/** convert string to double number (using ::rtl::math) */ +bool Converter::convertDouble(double& rValue, const ::rtl::OUString& rString) +{ + rtl_math_ConversionStatus eStatus; + rValue = ::rtl::math::stringToDouble( rString, (sal_Unicode)('.'), (sal_Unicode)(','), &eStatus, NULL ); + return ( eStatus == rtl_math_ConversionStatus_Ok ); +} + +/** convert double to ISO "duration" string; negative durations allowed */ +void Converter::convertDuration(::rtl::OUStringBuffer& rBuffer, + const double fTime) +{ + double fValue = fTime; + + // take care of negative durations as specified in: + // XML Schema, W3C Working Draft 07 April 2000, section 3.2.6.1 + if (fValue < 0.0) + { + rBuffer.append(sal_Unicode('-')); + fValue = - fValue; + } + + rBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM( "PT" )); + fValue *= 24; + double fHoursValue = ::rtl::math::approxFloor (fValue); + fValue -= fHoursValue; + fValue *= 60; + double fMinsValue = ::rtl::math::approxFloor (fValue); + fValue -= fMinsValue; + fValue *= 60; + double fSecsValue = ::rtl::math::approxFloor (fValue); + fValue -= fSecsValue; + double f100SecsValue; + if (fValue > 0.00001) + f100SecsValue = ::rtl::math::round( fValue, XML_MAXDIGITSCOUNT_TIME - 5); + else + f100SecsValue = 0.0; + + if (f100SecsValue == 1.0) + { + f100SecsValue = 0.0; + fSecsValue += 1.0; + } + if (fSecsValue >= 60.0) + { + fSecsValue -= 60.0; + fMinsValue += 1.0; + } + if (fMinsValue >= 60.0) + { + fMinsValue -= 60.0; + fHoursValue += 1.0; + } + + if (fHoursValue < 10) + rBuffer.append( sal_Unicode('0')); + rBuffer.append( sal_Int32( fHoursValue)); + rBuffer.append( sal_Unicode('H')); + if (fMinsValue < 10) + rBuffer.append( sal_Unicode('0')); + rBuffer.append( sal_Int32( fMinsValue)); + rBuffer.append( sal_Unicode('M')); + if (fSecsValue < 10) + rBuffer.append( sal_Unicode('0')); + rBuffer.append( sal_Int32( fSecsValue)); + if (f100SecsValue > 0.0) + { + ::rtl::OUString a100th( ::rtl::math::doubleToUString( fValue, + rtl_math_StringFormat_F, XML_MAXDIGITSCOUNT_TIME - 5, '.', + true)); + if ( a100th.getLength() > 2 ) + { + rBuffer.append( sal_Unicode('.')); + rBuffer.append( a100th.copy( 2 ) ); // strip 0. + } + } + rBuffer.append( sal_Unicode('S')); +} + +/** convert ISO "duration" string to double; negative durations allowed */ +bool Converter::convertDuration(double& rfTime, + const ::rtl::OUString& rString) +{ + rtl::OUString aTrimmed = rString.trim().toAsciiUpperCase(); + const sal_Unicode* pStr = aTrimmed.getStr(); + + // negative time duration? + bool bIsNegativeDuration = false; + if ( sal_Unicode('-') == (*pStr) ) + { + bIsNegativeDuration = true; + pStr++; + } + + if ( *(pStr++) != sal_Unicode('P') ) // duration must start with "P" + return false; + + rtl::OUString sDoubleStr; + bool bSuccess = true; + bool bDone = false; + bool bTimePart = false; + bool bIsFraction = false; + sal_Int32 nDays = 0; + sal_Int32 nHours = 0; + sal_Int32 nMins = 0; + sal_Int32 nSecs = 0; + sal_Int32 nTemp = 0; + + while ( bSuccess && !bDone ) + { + sal_Unicode c = *(pStr++); + if ( !c ) // end + bDone = true; + else if ( sal_Unicode('0') <= c && sal_Unicode('9') >= c ) + { + if ( nTemp >= SAL_MAX_INT32 / 10 ) + bSuccess = false; + else + { + if ( !bIsFraction ) + { + nTemp *= 10; + nTemp += (c - sal_Unicode('0')); + } + else + { + sDoubleStr += OUString::valueOf(c); + } + } + } + else if ( bTimePart ) + { + if ( c == sal_Unicode('H') ) + { + nHours = nTemp; + nTemp = 0; + } + else if ( c == sal_Unicode('M') ) + { + nMins = nTemp; + nTemp = 0; + } + else if ( (c == sal_Unicode(',')) || (c == sal_Unicode('.')) ) + { + nSecs = nTemp; + nTemp = 0; + bIsFraction = true; + sDoubleStr = OUString(RTL_CONSTASCII_USTRINGPARAM("0.")); + } + else if ( c == sal_Unicode('S') ) + { + if ( !bIsFraction ) + { + nSecs = nTemp; + nTemp = 0; + sDoubleStr = OUString(RTL_CONSTASCII_USTRINGPARAM("0.0")); + } + } + else + bSuccess = false; // invalid character + } + else + { + if ( c == sal_Unicode('T') ) // "T" starts time part + bTimePart = true; + else if ( c == sal_Unicode('D') ) + { + nDays = nTemp; + nTemp = 0; + } + else if ( c == sal_Unicode('Y') || c == sal_Unicode('M') ) + { + //! how many days is a year or month? + + OSL_ENSURE( false, "years or months in duration: not implemented"); + bSuccess = false; + } + else + bSuccess = false; // invalid character + } + } + + if ( bSuccess ) + { + if ( nDays ) + nHours += nDays * 24; // add the days to the hours part + double fTempTime = 0.0; + double fHour = nHours; + double fMin = nMins; + double fSec = nSecs; + double fSec100 = 0.0; + double fFraction = sDoubleStr.toDouble(); + fTempTime = fHour / 24; + fTempTime += fMin / (24 * 60); + fTempTime += fSec / (24 * 60 * 60); + fTempTime += fSec100 / (24 * 60 * 60 * 60); + fTempTime += fFraction / (24 * 60 * 60); + + // negative duration? + if ( bIsNegativeDuration ) + { + fTempTime = -fTempTime; + } + + rfTime = fTempTime; + } + return bSuccess; +} + +/** convert util::Duration to ISO "duration" string */ +void Converter::convertDuration(::rtl::OUStringBuffer& rBuffer, + const ::util::Duration& rDuration) +{ + if (rDuration.Negative) + { + rBuffer.append(sal_Unicode('-')); + } + rBuffer.append(sal_Unicode('P')); + const bool bHaveDate(static_cast<sal_Int32>(rDuration.Years) + +static_cast<sal_Int32>(rDuration.Months) + +static_cast<sal_Int32>(rDuration.Days)); + if (rDuration.Years) + { + rBuffer.append(static_cast<sal_Int32>(rDuration.Years)); + rBuffer.append(sal_Unicode('Y')); + } + if (rDuration.Months) + { + rBuffer.append(static_cast<sal_Int32>(rDuration.Months)); + rBuffer.append(sal_Unicode('M')); + } + if (rDuration.Days) + { + rBuffer.append(static_cast<sal_Int32>(rDuration.Days)); + rBuffer.append(sal_Unicode('D')); + } + const sal_Int32 nMSecs(static_cast<sal_Int32>(rDuration.Seconds) + + static_cast<sal_Int32>(rDuration.MilliSeconds)); + if (static_cast<sal_Int32>(rDuration.Hours) + + static_cast<sal_Int32>(rDuration.Minutes) + nMSecs) + { + rBuffer.append(sal_Unicode('T')); // time separator + if (rDuration.Hours) + { + rBuffer.append(static_cast<sal_Int32>(rDuration.Hours)); + rBuffer.append(sal_Unicode('H')); + } + if (rDuration.Minutes) + { + rBuffer.append(static_cast<sal_Int32>(rDuration.Minutes)); + rBuffer.append(sal_Unicode('M')); + } + if (nMSecs) + { + // seconds must not be omitted (i.e. ".42S" is not valid) + rBuffer.append(static_cast<sal_Int32>(rDuration.Seconds)); + if (rDuration.MilliSeconds) + { + rBuffer.append(sal_Unicode('.')); + const sal_Int32 nMilliSeconds(rDuration.MilliSeconds % 1000); + if (nMilliSeconds < 100) + { + rBuffer.append(sal_Unicode('0')); + } + if (nMilliSeconds < 10) + { + rBuffer.append(sal_Unicode('0')); + } + if (0 == (nMilliSeconds % 10)) + { + if (0 == (nMilliSeconds % 100)) + { + rBuffer.append(nMilliSeconds / 100); + } + else + { + rBuffer.append(nMilliSeconds / 10); + } + } + else + { + rBuffer.append(nMilliSeconds); + } + } + rBuffer.append(sal_Unicode('S')); + } + } + else if (!bHaveDate) + { + // zero duration: XMLSchema-2 says there must be at least one component + rBuffer.append(sal_Unicode('0')); + rBuffer.append(sal_Unicode('D')); + } +} + +enum Result { R_NOTHING, R_OVERFLOW, R_SUCCESS }; + +static Result +readUnsignedNumber(const ::rtl::OUString & rString, + sal_Int32 & io_rnPos, sal_Int32 & o_rNumber) +{ + bool bOverflow(false); + sal_Int32 nTemp(0); + sal_Int32 nPos(io_rnPos); + + while (nPos < rString.getLength()) + { + const sal_Unicode c = rString[nPos]; + if ((sal_Unicode('0') <= c) && (c <= sal_Unicode('9'))) + { + nTemp *= 10; + nTemp += (c - sal_Unicode('0')); + if (nTemp >= SAL_MAX_INT16) + { + bOverflow = true; + } + } + else + { + break; + } + ++nPos; + } + + if (io_rnPos == nPos) // read something? + { + o_rNumber = -1; + return R_NOTHING; + } + + io_rnPos = nPos; + o_rNumber = nTemp; + return (bOverflow) ? R_OVERFLOW : R_SUCCESS; +} + +static bool +readDurationT(const ::rtl::OUString & rString, sal_Int32 & io_rnPos) +{ + if ((io_rnPos < rString.getLength()) && + (rString[io_rnPos] == sal_Unicode('T'))) + { + ++io_rnPos; + return true; + } + return false; +} + +static bool +readDurationComponent(const ::rtl::OUString & rString, + sal_Int32 & io_rnPos, sal_Int32 & io_rnTemp, bool & io_rbTimePart, + sal_Int32 & o_rnTarget, const sal_Unicode c) +{ + if ((io_rnPos < rString.getLength())) + { + if (c == rString[io_rnPos]) + { + ++io_rnPos; + if (-1 != io_rnTemp) + { + o_rnTarget = io_rnTemp; + io_rnTemp = -1; + if (!io_rbTimePart) + { + io_rbTimePart = readDurationT(rString, io_rnPos); + } + return (R_OVERFLOW != + readUnsignedNumber(rString, io_rnPos, io_rnTemp)); + } + else + { + return false; + } + } + } + return true; +} + +/** convert ISO "duration" string to util::Duration */ +bool Converter::convertDuration(util::Duration& rDuration, + const ::rtl::OUString& rString) +{ + const ::rtl::OUString string = rString.trim().toAsciiUpperCase(); + sal_Int32 nPos(0); + + bool bIsNegativeDuration(false); + if (string.getLength() && (sal_Unicode('-') == string[0])) + { + bIsNegativeDuration = true; + ++nPos; + } + + if ((nPos < string.getLength()) + && (string[nPos] != sal_Unicode('P'))) // duration must start with "P" + { + return false; + } + + ++nPos; + + /// last read number; -1 == no valid number! always reset after using! + sal_Int32 nTemp(-1); + bool bTimePart(false); // have we read 'T'? + bool bSuccess(false); + sal_Int32 nYears(0); + sal_Int32 nMonths(0); + sal_Int32 nDays(0); + sal_Int32 nHours(0); + sal_Int32 nMinutes(0); + sal_Int32 nSeconds(0); + sal_Int32 nMilliSeconds(0); + + bTimePart = readDurationT(string, nPos); + bSuccess = (R_SUCCESS == readUnsignedNumber(string, nPos, nTemp)); + + if (!bTimePart && bSuccess) + { + bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart, + nYears, sal_Unicode('Y')); + } + + if (!bTimePart && bSuccess) + { + bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart, + nMonths, sal_Unicode('M')); + } + + if (!bTimePart && bSuccess) + { + bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart, + nDays, sal_Unicode('D')); + } + + if (bTimePart) + { + if (-1 == nTemp) // a 'T' must be followed by a component + { + bSuccess = false; + } + + if (bSuccess) + { + bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart, + nHours, sal_Unicode('H')); + } + + if (bSuccess) + { + bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart, + nMinutes, sal_Unicode('M')); + } + + // eeek! seconds are icky. + if ((nPos < string.getLength()) && bSuccess) + { + if (sal_Unicode('.') == string[nPos]) + { + ++nPos; + if (-1 != nTemp) + { + nSeconds = nTemp; + nTemp = -1; + const sal_Int32 nStart(nPos); + bSuccess = + (R_NOTHING != readUnsignedNumber(string, nPos, nTemp)); + if ((nPos < string.getLength()) && bSuccess) + { + if (-1 != nTemp) + { + nTemp = -1; + const sal_Int32 nDigits = nPos - nStart; + OSL_ENSURE(nDigits > 0, "bad code monkey"); + const sal_Unicode cZero('0'); + nMilliSeconds = 100 * (string[nStart] - cZero); + if (nDigits >= 2) + { + nMilliSeconds += 10 * + (string[nStart+1] - cZero); + if (nDigits >= 3) + { + nMilliSeconds += (string[nStart+2] - cZero); + } + } + + if (sal_Unicode('S') == string[nPos]) + { + ++nPos; + } + else + { + bSuccess = false; + } + } + else + { + bSuccess = false; + } + } + } + else + { + bSuccess = false; + } + } + else if (sal_Unicode('S') == string[nPos]) + { + ++nPos; + if (-1 != nTemp) + { + nSeconds = nTemp; + nTemp = -1; + } + else + { + bSuccess = false; + } + } + } + } + + if (nPos != string.getLength()) // string not processed completely? + { + bSuccess = false; + } + + if (nTemp != -1) // unprocessed number? + { + bSuccess = false; + } + + if (bSuccess) + { + rDuration.Negative = bIsNegativeDuration; + rDuration.Years = static_cast<sal_Int16>(nYears); + rDuration.Months = static_cast<sal_Int16>(nMonths); + rDuration.Days = static_cast<sal_Int16>(nDays); + rDuration.Hours = static_cast<sal_Int16>(nHours); + rDuration.Minutes = static_cast<sal_Int16>(nMinutes); + rDuration.Seconds = static_cast<sal_Int16>(nSeconds); + rDuration.MilliSeconds = static_cast<sal_Int16>(nMilliSeconds); + } + + return bSuccess; +} + + +/** convert util::Date to ISO "date" string */ +void Converter::convertDate( + ::rtl::OUStringBuffer& i_rBuffer, + const util::Date& i_rDate) +{ + const util::DateTime dt( + 0, 0, 0, 0, i_rDate.Day, i_rDate.Month, i_rDate.Year); + convertDateTime(i_rBuffer, dt, false); +} + +/** convert util::DateTime to ISO "date" or "dateTime" string */ +void Converter::convertDateTime( + ::rtl::OUStringBuffer& i_rBuffer, + const com::sun::star::util::DateTime& i_rDateTime, + bool i_bAddTimeIf0AM ) +{ + const sal_Unicode dash('-'); + const sal_Unicode col (':'); + const sal_Unicode dot ('.'); + const sal_Unicode zero('0'); + const sal_Unicode tee ('T'); + + if (i_rDateTime.Year < 1000) { + i_rBuffer.append(zero); + } + if (i_rDateTime.Year < 100) { + i_rBuffer.append(zero); + } + if (i_rDateTime.Year < 10) { + i_rBuffer.append(zero); + } + i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Year) ).append(dash); + if( i_rDateTime.Month < 10 ) { + i_rBuffer.append(zero); + } + i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Month) ).append(dash); + if( i_rDateTime.Day < 10 ) { + i_rBuffer.append(zero); + } + i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Day) ); + + if( i_rDateTime.Seconds != 0 || + i_rDateTime.Minutes != 0 || + i_rDateTime.Hours != 0 || + i_bAddTimeIf0AM ) + { + i_rBuffer.append(tee); + if( i_rDateTime.Hours < 10 ) { + i_rBuffer.append(zero); + } + i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Hours) ) + .append(col); + if( i_rDateTime.Minutes < 10 ) { + i_rBuffer.append(zero); + } + i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Minutes) ) + .append(col); + if( i_rDateTime.Seconds < 10 ) { + i_rBuffer.append(zero); + } + i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Seconds) ); + if( i_rDateTime.HundredthSeconds > 0 ) { + i_rBuffer.append(dot); + if( i_rDateTime.HundredthSeconds < 10 ) { + i_rBuffer.append(zero); + } + i_rBuffer.append( + static_cast<sal_Int32>(i_rDateTime.HundredthSeconds) ); + } + } +} + +/** convert ISO "date" or "dateTime" string to util::DateTime */ +bool Converter::convertDateTime( util::DateTime& rDateTime, + const ::rtl::OUString& rString ) +{ + bool isDateTime; + util::Date date; + if (convertDateOrDateTime(date, rDateTime, isDateTime, rString)) + { + if (!isDateTime) + { + rDateTime.Year = date.Year; + rDateTime.Month = date.Month; + rDateTime.Day = date.Day; + rDateTime.Hours = 0; + rDateTime.Minutes = 0; + rDateTime.Seconds = 0; + rDateTime.HundredthSeconds = 0; + } + return true; + } + else + { + return false; + } +} + +static bool +readDateTimeComponent(const ::rtl::OUString & rString, + sal_Int32 & io_rnPos, sal_Int32 & o_rnTarget, + const sal_Int32 nMinLength, const bool bExactLength) +{ + const sal_Int32 nOldPos(io_rnPos); + sal_Int32 nTemp(0); + if (R_SUCCESS != readUnsignedNumber(rString, io_rnPos, nTemp)) + { + return false; + } + const sal_Int32 nTokenLength(io_rnPos - nOldPos); + if ((nTokenLength < nMinLength) || + (bExactLength && (nTokenLength > nMinLength))) + { + return false; // bad length + } + o_rnTarget = nTemp; + return true; +} + +static bool lcl_isLeapYear(const sal_uInt32 nYear) +{ + return ((nYear % 4) == 0) + && !(((nYear % 100) == 0) || ((nYear % 400) == 0)); +} + +static sal_uInt16 +lcl_MaxDaysPerMonth(const sal_Int32 nMonth, const sal_Int32 nYear) +{ + static sal_uInt16 s_MaxDaysPerMonth[12] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + OSL_ASSERT(0 < nMonth && nMonth <= 12); + if ((2 == nMonth) && lcl_isLeapYear(nYear)) + { + return 29; + } + return s_MaxDaysPerMonth[nMonth - 1]; +} + +/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */ +bool Converter::convertDateOrDateTime( + util::Date & rDate, util::DateTime & rDateTime, + bool & rbDateTime, const ::rtl::OUString & rString ) +{ + bool bSuccess = true; + + const ::rtl::OUString string = rString.trim().toAsciiUpperCase(); + sal_Int32 nPos(0); + bool bNegative(false); + if ((string.getLength() > nPos) && (sal_Unicode('-') == string[nPos])) + { + ++nPos; + bNegative = true; + } + + sal_Int32 nYear(0); + { + bSuccess = readDateTimeComponent(string, nPos, nYear, 4, false); + bSuccess &= (0 < nYear); + bSuccess &= (nPos < string.getLength()); // not last token + } + if (bSuccess && (sal_Unicode('-') != string[nPos])) // separator + { + bSuccess = false; + } + if (bSuccess) + { + ++nPos; + } + + sal_Int32 nMonth(0); + if (bSuccess) + { + bSuccess = readDateTimeComponent(string, nPos, nMonth, 2, true); + bSuccess &= (0 < nMonth) && (nMonth <= 12); + bSuccess &= (nPos < string.getLength()); // not last token + } + if (bSuccess && (sal_Unicode('-') != string[nPos])) // separator + { + bSuccess = false; + } + if (bSuccess) + { + ++nPos; + } + + sal_Int32 nDay(0); + if (bSuccess) + { + bSuccess = readDateTimeComponent(string, nPos, nDay, 2, true); + bSuccess &= (0 < nDay) && (nDay <= lcl_MaxDaysPerMonth(nMonth, nYear)); + } + + bool bHaveTime(false); + if (bSuccess && (nPos < string.getLength())) + { + if (sal_Unicode('T') == string[nPos]) // time separator + { + bHaveTime = true; + ++nPos; + } + } + + sal_Int32 nHours(0); + sal_Int32 nMinutes(0); + sal_Int32 nSeconds(0); + sal_Int32 nMilliSeconds(0); + if (bSuccess && bHaveTime) + { + { + bSuccess = readDateTimeComponent(string, nPos, nHours, 2, true); + bSuccess &= (0 <= nHours) && (nHours <= 24); + bSuccess &= (nPos < string.getLength()); // not last token + } + if (bSuccess && (sal_Unicode(':') != string[nPos])) // separator + { + bSuccess = false; + } + if (bSuccess) + { + ++nPos; + } + + if (bSuccess) + { + bSuccess = readDateTimeComponent(string, nPos, nMinutes, 2, true); + bSuccess &= (0 <= nMinutes) && (nMinutes < 60); + bSuccess &= (nPos < string.getLength()); // not last token + } + if (bSuccess && (sal_Unicode(':') != string[nPos])) // separator + { + bSuccess = false; + } + if (bSuccess) + { + ++nPos; + } + + if (bSuccess) + { + bSuccess = readDateTimeComponent(string, nPos, nSeconds, 2, true); + bSuccess &= (0 <= nSeconds) && (nSeconds < 60); + } + if (bSuccess && (nPos < string.getLength()) && + (sal_Unicode('.') == string[nPos])) // fraction separator + { + ++nPos; + const sal_Int32 nStart(nPos); + sal_Int32 nTemp(0); + if (R_NOTHING == readUnsignedNumber(string, nPos, nTemp)) + { + bSuccess = false; + } + if (bSuccess) + { + // cannot use nTemp because of possible leading zeros + // and possible overflow => read digits directly + const sal_Int32 nDigits(nPos - nStart); + OSL_ENSURE(nDigits > 0, "bad code monkey"); + const sal_Unicode cZero('0'); + nMilliSeconds = 100 * (string[nStart] - cZero); + if (nDigits >= 2) + { + nMilliSeconds += 10 * (string[nStart+1] - cZero); + if (nDigits >= 3) + { + nMilliSeconds += (string[nStart+2] - cZero); + } + } + } + } + + if (bSuccess && (nHours == 24)) + { + if (!((0 == nMinutes) && (0 == nSeconds) && (0 == nMilliSeconds))) + { + bSuccess = false; // only 24:00:00 is valid + } +#if 0 + else + { + nHours = 0; // normalize 24:00:00 to 00:00:00 of next day + lcl_addDay(bNegative, nYear, nMonth, nDay, 1); + } +#endif + } + } + + bool bHaveTimezone(false); + bool bHaveTimezonePlus(false); + bool bHaveTimezoneMinus(false); + if (bSuccess && (nPos < string.getLength())) + { + const sal_Unicode c(string[nPos]); + if (sal_Unicode('+') == c) + { + bHaveTimezone = true; + bHaveTimezonePlus = true; + ++nPos; + } + else if (sal_Unicode('-') == c) + { + bHaveTimezone = true; + bHaveTimezoneMinus = true; + ++nPos; + } + else if (sal_Unicode('Z') == c) + { + bHaveTimezone = true; + ++nPos; + } + else + { + bSuccess = false; + } + } + sal_Int32 nTimezoneHours(0); + sal_Int32 nTimezoneMinutes(0); + if (bSuccess && (bHaveTimezonePlus || bHaveTimezoneMinus)) + { + bSuccess = readDateTimeComponent( + string, nPos, nTimezoneHours, 2, true); + bSuccess &= (0 <= nTimezoneHours) && (nTimezoneHours <= 14); + bSuccess &= (nPos < string.getLength()); // not last token + if (bSuccess && (sal_Unicode(':') != string[nPos])) // separator + { + bSuccess = false; + } + if (bSuccess) + { + ++nPos; + } + if (bSuccess) + { + bSuccess = readDateTimeComponent( + string, nPos, nTimezoneMinutes, 2, true); + bSuccess &= (0 <= nTimezoneMinutes) && (nTimezoneMinutes < 60); + } + if (bSuccess && (nTimezoneHours == 14)) + { + if (0 != nTimezoneMinutes) + { + bSuccess = false; // only +-14:00 is valid + } + } + } + + bSuccess &= (nPos == string.getLength()); // trailing junk? + + if (bSuccess && bHaveTimezone) + { + // util::DateTime does not support timezones! +#if 0 + // do not add timezone, just strip it (as suggested by er) + lcl_addTimezone(bNegative, nYear, nMonth, nDay, nHours, nMinutes, + !bHaveTimezoneMinus, nTimezoneHours, nTimezoneMinutes); +#endif + } + + if (bSuccess) + { + if (bHaveTime) // time is optional + { + // util::DateTime does not support negative years! + rDateTime.Year = static_cast<sal_uInt16>(nYear); + rDateTime.Month = static_cast<sal_uInt16>(nMonth); + rDateTime.Day = static_cast<sal_uInt16>(nDay); + rDateTime.Hours = static_cast<sal_uInt16>(nHours); + rDateTime.Minutes = static_cast<sal_uInt16>(nMinutes); + rDateTime.Seconds = static_cast<sal_uInt16>(nSeconds); + // util::DateTime does not support 3 decimal digits of precision! + rDateTime.HundredthSeconds = + static_cast<sal_uInt16>(nMilliSeconds / 10); + rbDateTime = true; + } + else + { + rDate.Year = static_cast<sal_uInt16>(nYear); + rDate.Month = static_cast<sal_uInt16>(nMonth); + rDate.Day = static_cast<sal_uInt16>(nDay); + rbDateTime = false; + } + } + return bSuccess; +} + + +/** gets the position of the first comma after npos in the string + rStr. Commas inside '"' pairs are not matched */ +sal_Int32 Converter::indexOfComma( const OUString& rStr, + sal_Int32 nPos ) +{ + sal_Unicode cQuote = 0; + sal_Int32 nLen = rStr.getLength(); + for( ; nPos < nLen; nPos++ ) + { + sal_Unicode c = rStr[nPos]; + switch( c ) + { + case sal_Unicode('\''): + if( 0 == cQuote ) + cQuote = c; + else if( '\'' == cQuote ) + cQuote = 0; + break; + + case sal_Unicode('"'): + if( 0 == cQuote ) + cQuote = c; + else if( '\"' == cQuote ) + cQuote = 0; + break; + + case sal_Unicode(','): + if( 0 == cQuote ) + return nPos; + break; + } + } + + return -1; +} + +const + sal_Char aBase64EncodeTable[] = + { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; + +const + sal_uInt8 aBase64DecodeTable[] = + { 62,255,255,255, 63, // 43-47 +// + / + + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, // 48-63 +// 0 1 2 3 4 5 6 7 8 9 = + + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 +// A B C D E F G H I J K L M N O + + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, // 80-95 +// P Q R S T U V W X Y Z + + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 +// a b c d e f g h i j k l m n o + + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; // 112-123 +// p q r s t u v w x y z + + + +void ThreeByteToFourByte (const sal_Int8* pBuffer, const sal_Int32 nStart, const sal_Int32 nFullLen, rtl::OUStringBuffer& sBuffer) +{ + sal_Int32 nLen(nFullLen - nStart); + if (nLen > 3) + nLen = 3; + if (nLen == 0) + { + sBuffer.setLength(0); + return; + } + + sal_Int32 nBinaer; + switch (nLen) + { + case 1: + { + nBinaer = ((sal_uInt8)pBuffer[nStart + 0]) << 16; + } + break; + case 2: + { + nBinaer = (((sal_uInt8)pBuffer[nStart + 0]) << 16) + + (((sal_uInt8)pBuffer[nStart + 1]) << 8); + } + break; + default: + { + nBinaer = (((sal_uInt8)pBuffer[nStart + 0]) << 16) + + (((sal_uInt8)pBuffer[nStart + 1]) << 8) + + ((sal_uInt8)pBuffer[nStart + 2]); + } + break; + } + + sBuffer.appendAscii("===="); + + sal_uInt8 nIndex (static_cast<sal_uInt8>((nBinaer & 0xFC0000) >> 18)); + sBuffer.setCharAt(0, aBase64EncodeTable [nIndex]); + + nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F000) >> 12); + sBuffer.setCharAt(1, aBase64EncodeTable [nIndex]); + if (nLen == 1) + return; + + nIndex = static_cast<sal_uInt8>((nBinaer & 0xFC0) >> 6); + sBuffer.setCharAt(2, aBase64EncodeTable [nIndex]); + if (nLen == 2) + return; + + nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F)); + sBuffer.setCharAt(3, aBase64EncodeTable [nIndex]); +} + +void Converter::encodeBase64(rtl::OUStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass) +{ + sal_Int32 i(0); + sal_Int32 nBufferLength(aPass.getLength()); + const sal_Int8* pBuffer = aPass.getConstArray(); + while (i < nBufferLength) + { + rtl::OUStringBuffer sBuffer; + ThreeByteToFourByte (pBuffer, i, nBufferLength, sBuffer); + aStrBuffer.append(sBuffer); + i += 3; + } +} + +void Converter::decodeBase64(uno::Sequence<sal_Int8>& aBuffer, const rtl::OUString& sBuffer) +{ +#if OSL_DEBUG_LEVEL > 0 + sal_Int32 nCharsDecoded = +#endif + decodeBase64SomeChars( aBuffer, sBuffer ); + OSL_ENSURE( nCharsDecoded == sBuffer.getLength(), "some bytes left in base64 decoding!" ); +} + +sal_Int32 Converter::decodeBase64SomeChars( + uno::Sequence<sal_Int8>& rOutBuffer, + const rtl::OUString& rInBuffer) +{ + sal_Int32 nInBufferLen = rInBuffer.getLength(); + sal_Int32 nMinOutBufferLen = (nInBufferLen / 4) * 3; + if( rOutBuffer.getLength() < nMinOutBufferLen ) + rOutBuffer.realloc( nMinOutBufferLen ); + + const sal_Unicode *pInBuffer = rInBuffer.getStr(); + sal_Int8 *pOutBuffer = rOutBuffer.getArray(); + sal_Int8 *pOutBufferStart = pOutBuffer; + sal_Int32 nCharsDecoded = 0; + + sal_uInt8 aDecodeBuffer[4]; + sal_Int32 nBytesToDecode = 0; + sal_Int32 nBytesGotFromDecoding = 3; + sal_Int32 nInBufferPos= 0; + while( nInBufferPos < nInBufferLen ) + { + sal_Unicode cChar = *pInBuffer; + if( cChar >= '+' && cChar <= 'z' ) + { + sal_uInt8 nByte = aBase64DecodeTable[cChar-'+']; + if( nByte != 255 ) + { + // We have found a valid character! + aDecodeBuffer[nBytesToDecode++] = nByte; + + // One '=' character at the end means 2 out bytes + // Two '=' characters at the end mean 1 out bytes + if( '=' == cChar && nBytesToDecode > 2 ) + nBytesGotFromDecoding--; + if( 4 == nBytesToDecode ) + { + // Four characters found, so we may convert now! + sal_uInt32 nOut = (aDecodeBuffer[0] << 18) + + (aDecodeBuffer[1] << 12) + + (aDecodeBuffer[2] << 6) + + aDecodeBuffer[3]; + + *pOutBuffer++ = (sal_Int8)((nOut & 0xff0000) >> 16); + if( nBytesGotFromDecoding > 1 ) + *pOutBuffer++ = (sal_Int8)((nOut & 0xff00) >> 8); + if( nBytesGotFromDecoding > 2 ) + *pOutBuffer++ = (sal_Int8)(nOut & 0xff); + nCharsDecoded = nInBufferPos + 1; + nBytesToDecode = 0; + nBytesGotFromDecoding = 3; + } + } + else + { + nCharsDecoded++; + } + } + else + { + nCharsDecoded++; + } + + nInBufferPos++; + pInBuffer++; + } + if( (pOutBuffer - pOutBufferStart) != rOutBuffer.getLength() ) + rOutBuffer.realloc( pOutBuffer - pOutBufferStart ); + + return nCharsDecoded; +} + +void Converter::clearUndefinedChars(rtl::OUString& rTarget, const rtl::OUString& rSource) +{ + sal_uInt32 nLength(rSource.getLength()); + rtl::OUStringBuffer sBuffer(nLength); + for (sal_uInt32 i = 0; i < nLength; i++) + { + sal_Unicode cChar = rSource[i]; + if (!(cChar < 0x0020) || + (cChar == 0x0009) || // TAB + (cChar == 0x000A) || // LF + (cChar == 0x000D)) // legal character + sBuffer.append(cChar); + } + rTarget = sBuffer.makeStringAndClear(); +} + +double Converter::GetConversionFactor(::rtl::OUStringBuffer& rUnit, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit) +{ + double fRetval(1.0); + rUnit.setLength(0L); + + const sal_Char* psUnit = 0; + + if(nSourceUnit != nTargetUnit) + { + switch(nSourceUnit) + { + case MeasureUnit::TWIP: + { + switch(nTargetUnit) + { + case MeasureUnit::MM_100TH: + case MeasureUnit::MM_10TH: + { + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values"); + } + case MeasureUnit::MM: + { + // 0.01mm = 0.57twip (exactly) + fRetval = ((25400.0 / 1440.0) / 1000.0); + psUnit = gpsMM; + break; + } + case MeasureUnit::CM: + { + // 0.001cm = 0.57twip (exactly) + fRetval = ((25400.0 / 1440.0) / 10000.0); + psUnit = gpsCM; + break; + } + case MeasureUnit::POINT: + { + // 0.01pt = 0.2twip (exactly) + fRetval = ((1000.0 / 20.0) / 1000.0); + psUnit = gpsPT; + break; + } + case MeasureUnit::INCH: + default: + { + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values"); + // 0.0001in = 0.144twip (exactly) + fRetval = ((100000.0 / 1440.0) / 100000.0); + psUnit = gpsINCH; + break; + } + } + break; + } + case MeasureUnit::POINT: + { + switch(nTargetUnit) + { + case MeasureUnit::MM: + // 1mm = 72 / 25.4 pt (exactly) + fRetval = ( 25.4 / 72.0 ); + psUnit = gpsMM; + break; + + case MeasureUnit::CM: + // 1cm = 72 / 2.54 pt (exactly) + fRetval = ( 2.54 / 72.0 ); + psUnit = gpsCM; + break; + + case MeasureUnit::TWIP: + // 1twip = 72 / 1440 pt (exactly) + fRetval = 20.0; // 1440.0 / 72.0 + psUnit = gpsPC; + break; + + case MeasureUnit::INCH: + default: + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for pt values"); + // 1in = 72 pt (exactly) + fRetval = ( 1.0 / 72.0 ); + psUnit = gpsINCH; + break; + } + break; + } + case MeasureUnit::MM_10TH: + { + switch(nTargetUnit) + { + case MeasureUnit::MM_100TH: + case MeasureUnit::MM_10TH: + { + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values"); + } + case MeasureUnit::MM: + { + // 0.01mm = 1 mm/100 (exactly) + fRetval = ((10.0 / 1.0) / 100.0); + psUnit = gpsMM; + break; + } + case MeasureUnit::CM: + { + // 0.001mm = 1 mm/100 (exactly) + fRetval = ((10.0 / 1.0) / 1000.0); + psUnit = gpsCM; + break; + } + case MeasureUnit::POINT: + { + // 0.01pt = 0.35 mm/100 (exactly) + fRetval = ((72000.0 / 2540.0) / 100.0); + psUnit = gpsPT; + break; + } + case MeasureUnit::INCH: + default: + { + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values"); + // 0.0001in = 0.254 mm/100 (exactly) + fRetval = ((100000.0 / 2540.0) / 10000.0); + psUnit = gpsINCH; + break; + } + } + break; + } + case MeasureUnit::MM_100TH: + { + switch(nTargetUnit) + { + case MeasureUnit::MM_100TH: + case MeasureUnit::MM_10TH: + { + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values"); + } + case MeasureUnit::MM: + { + // 0.01mm = 1 mm/100 (exactly) + fRetval = ((10.0 / 1.0) / 1000.0); + psUnit = gpsMM; + break; + } + case MeasureUnit::CM: + { + // 0.001mm = 1 mm/100 (exactly) + fRetval = ((10.0 / 1.0) / 10000.0); + psUnit = gpsCM; + break; + } + case MeasureUnit::POINT: + { + // 0.01pt = 0.35 mm/100 (exactly) + fRetval = ((72000.0 / 2540.0) / 1000.0); + psUnit = gpsPT; + break; + } + case MeasureUnit::INCH: + default: + { + OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values"); + // 0.0001in = 0.254 mm/100 (exactly) + fRetval = ((100000.0 / 2540.0) / 100000.0); + psUnit = gpsINCH; + break; + } + } + break; + } + } + + if( psUnit ) + rUnit.appendAscii( psUnit ); + } + + return fRetval; +} + +sal_Int16 Converter::GetUnitFromString(const ::rtl::OUString& rString, sal_Int16 nDefaultUnit) +{ + sal_Int32 nPos = 0L; + sal_Int32 nLen = rString.getLength(); + sal_Int16 nRetUnit = nDefaultUnit; + + // skip white space + while( nPos < nLen && sal_Unicode(' ') == rString[nPos] ) + nPos++; + + // skip negative + if( nPos < nLen && sal_Unicode('-') == rString[nPos] ) + nPos++; + + // skip number + while( nPos < nLen && sal_Unicode('0') <= rString[nPos] && sal_Unicode('9') >= rString[nPos] ) + nPos++; + + if( nPos < nLen && sal_Unicode('.') == rString[nPos] ) + { + nPos++; + while( nPos < nLen && sal_Unicode('0') <= rString[nPos] && sal_Unicode('9') >= rString[nPos] ) + nPos++; + } + + // skip white space + while( nPos < nLen && sal_Unicode(' ') == rString[nPos] ) + nPos++; + + if( nPos < nLen ) + { + switch(rString[nPos]) + { + case sal_Unicode('%') : + { + nRetUnit = MeasureUnit::PERCENT; + break; + } + case sal_Unicode('c'): + case sal_Unicode('C'): + { + if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('m') + || rString[nPos+1] == sal_Unicode('M'))) + nRetUnit = MeasureUnit::CM; + break; + } + case sal_Unicode('e'): + case sal_Unicode('E'): + { + // CSS1_EMS or CSS1_EMX later + break; + } + case sal_Unicode('i'): + case sal_Unicode('I'): + { + if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('n') + || rString[nPos+1] == sal_Unicode('n'))) + nRetUnit = MeasureUnit::INCH; + break; + } + case sal_Unicode('m'): + case sal_Unicode('M'): + { + if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('m') + || rString[nPos+1] == sal_Unicode('M'))) + nRetUnit = MeasureUnit::MM; + break; + } + case sal_Unicode('p'): + case sal_Unicode('P'): + { + if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('t') + || rString[nPos+1] == sal_Unicode('T'))) + nRetUnit = MeasureUnit::POINT; + if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('c') + || rString[nPos+1] == sal_Unicode('C'))) + nRetUnit = MeasureUnit::TWIP; + break; + } + } + } + + return nRetUnit; +} + +} diff --git a/sax/source/tools/fastattribs.cxx b/sax/source/tools/fastattribs.cxx new file mode 100644 index 000000000000..4bf9d55c75b8 --- /dev/null +++ b/sax/source/tools/fastattribs.cxx @@ -0,0 +1,168 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <algorithm> +#include <boost/bind.hpp> + +#include <sax/fastattribs.hxx> + +using ::rtl::OUString; +using ::rtl::OString; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml; +using namespace ::com::sun::star::xml::sax; +namespace sax_fastparser +{ + +UnknownAttribute::UnknownAttribute( const OUString& rNamespaceURL, const OString& rName, const OString& rValue ) + : maNamespaceURL( rNamespaceURL ), maName( rName ), maValue( rValue ) +{ +} + +UnknownAttribute::UnknownAttribute( const OString& rName, const OString& rValue ) + : maName( rName ), maValue( rValue ) +{ +} + +void UnknownAttribute::FillAttribute( Attribute* pAttrib ) const +{ + if( pAttrib ) + { + pAttrib->Name = OStringToOUString( maName, RTL_TEXTENCODING_UTF8 ); + pAttrib->NamespaceURL = maNamespaceURL; + pAttrib->Value = OStringToOUString( maValue, RTL_TEXTENCODING_UTF8 ); + } +} + +FastAttributeList::FastAttributeList( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler >& xTokenHandler ) +: mxTokenHandler( xTokenHandler ) +{ + maLastIter = maAttributes.end(); +} + +FastAttributeList::~FastAttributeList() +{ +} + +void FastAttributeList::clear() +{ + maAttributes.clear(); + maUnknownAttributes.clear(); + maLastIter = maAttributes.end(); +} + +void FastAttributeList::add( sal_Int32 nToken, const OString& rValue ) +{ + maAttributes[nToken] = rValue; +} + +void FastAttributeList::addUnknown( const OUString& rNamespaceURL, const OString& rName, const OString& rValue ) +{ + maUnknownAttributes.push_back( UnknownAttribute( rNamespaceURL, rName, rValue ) ); +} + +void FastAttributeList::addUnknown( const OString& rName, const OString& rValue ) +{ + maUnknownAttributes.push_back( UnknownAttribute( rName, rValue ) ); +} + +// XFastAttributeList +sal_Bool FastAttributeList::hasAttribute( ::sal_Int32 Token ) throw (RuntimeException) +{ + maLastIter = maAttributes.find( Token ); + return ( maLastIter != maAttributes.end() ) ? sal_True : sal_False; +} + +sal_Int32 FastAttributeList::getValueToken( ::sal_Int32 Token ) throw (SAXException, RuntimeException) +{ + if( ( maLastIter == maAttributes.end() ) || ( ( *maLastIter ).first != Token ) ) + maLastIter = maAttributes.find( Token ); + + if( maLastIter == maAttributes.end() ) + throw SAXException(); + + Sequence< sal_Int8 > aSeq( (sal_Int8*)(*maLastIter).second.getStr(), (*maLastIter).second.getLength() ) ; + return mxTokenHandler->getTokenFromUTF8( aSeq ); +} + +sal_Int32 FastAttributeList::getOptionalValueToken( ::sal_Int32 Token, ::sal_Int32 Default ) throw (RuntimeException) +{ + if( ( maLastIter == maAttributes.end() ) || ( ( *maLastIter ).first != Token ) ) + maLastIter = maAttributes.find( Token ); + + if( maLastIter == maAttributes.end() ) + return Default; + + Sequence< sal_Int8 > aSeq( (sal_Int8*)(*maLastIter).second.getStr(), (*maLastIter).second.getLength() ) ; + return mxTokenHandler->getTokenFromUTF8( aSeq ); +} + +OUString FastAttributeList::getValue( ::sal_Int32 Token ) throw (SAXException, RuntimeException) +{ + if( ( maLastIter == maAttributes.end() ) || ( ( *maLastIter ).first != Token ) ) + maLastIter = maAttributes.find( Token ); + + if( maLastIter == maAttributes.end() ) + throw SAXException(); + + return OStringToOUString( (*maLastIter).second, RTL_TEXTENCODING_UTF8 ); +} + +OUString FastAttributeList::getOptionalValue( ::sal_Int32 Token ) throw (RuntimeException) +{ + if( ( maLastIter == maAttributes.end() ) || ( ( *maLastIter ).first != Token ) ) + maLastIter = maAttributes.find( Token ); + + OUString aRet; + if( maLastIter != maAttributes.end() ) + aRet = OStringToOUString( (*maLastIter).second, RTL_TEXTENCODING_UTF8 ); + + return aRet; +} +Sequence< Attribute > FastAttributeList::getUnknownAttributes( ) throw (RuntimeException) +{ + Sequence< Attribute > aSeq( maUnknownAttributes.size() ); + Attribute* pAttr = aSeq.getArray(); + for( UnknownAttributeList::iterator attrIter = maUnknownAttributes.begin(); attrIter != maUnknownAttributes.end(); attrIter++ ) + (*attrIter).FillAttribute( pAttr++ ); + return aSeq; +} +Sequence< FastAttribute > FastAttributeList::getFastAttributes( ) throw (RuntimeException) +{ + Sequence< FastAttribute > aSeq( maAttributes.size() ); + FastAttribute* pAttr = aSeq.getArray(); + FastAttributeMap::iterator fastAttrIter = maAttributes.begin(); + for(; fastAttrIter != maAttributes.end(); fastAttrIter++ ) + { + pAttr->Token = fastAttrIter->first; + pAttr->Value = OStringToOUString( fastAttrIter->second, RTL_TEXTENCODING_UTF8 ); + pAttr++; + } + return aSeq; +} + +} diff --git a/sax/source/tools/fastserializer.cxx b/sax/source/tools/fastserializer.cxx new file mode 100644 index 000000000000..af89761a2c86 --- /dev/null +++ b/sax/source/tools/fastserializer.cxx @@ -0,0 +1,403 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "fastserializer.hxx" +#include <rtl/ustrbuf.hxx> +#include <rtl/byteseq.hxx> + +#include <com/sun/star/xml/Attribute.hpp> +#include <com/sun/star/xml/FastAttribute.hpp> +#include <com/sun/star/xml/sax/XFastAttributeList.hpp> + +#include <string.h> + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OUStringToOString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::toUnoSequence; +using ::com::sun::star::xml::FastAttribute; +using ::com::sun::star::xml::Attribute; +using ::com::sun::star::xml::sax::SAXException; +using ::com::sun::star::xml::sax::XFastAttributeList; +using ::com::sun::star::xml::sax::XFastTokenHandler; +using ::com::sun::star::xml::sax::XFastSerializer; +using ::com::sun::star::io::XOutputStream; +using ::com::sun::star::io::NotConnectedException; +using ::com::sun::star::io::IOException; +using ::com::sun::star::io::BufferSizeExceededException; + +static rtl::ByteSequence aClosingBracket((const sal_Int8 *)">", 1); +static rtl::ByteSequence aSlashAndClosingBracket((const sal_Int8 *)"/>", 2); +static rtl::ByteSequence aColon((const sal_Int8 *)":", 1); +static rtl::ByteSequence aOpeningBracket((const sal_Int8 *)"<", 1); +static rtl::ByteSequence aOpeningBracketAndSlash((const sal_Int8 *)"</", 2); +static rtl::ByteSequence aQuote((const sal_Int8 *)"\"", 1); +static rtl::ByteSequence aEqualSignAndQuote((const sal_Int8 *)"=\"", 2); +static rtl::ByteSequence aSpace((const sal_Int8 *)" ", 1); +static rtl::ByteSequence aXmlHeader((const sal_Int8*) "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n", 56); + +#define HAS_NAMESPACE(x) ((x & 0xffff0000) != 0) +#define NAMESPACE(x) (x >> 16) +#define TOKEN(x) (x & 0xffff) + +namespace sax_fastparser { + FastSaxSerializer::FastSaxSerializer( ) : mxOutputStream(), mxFastTokenHandler(), maMarkStack() {} + FastSaxSerializer::~FastSaxSerializer() {} + + void SAL_CALL FastSaxSerializer::startDocument( ) throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + writeBytes(toUnoSequence(aXmlHeader)); + } + + OUString FastSaxSerializer::escapeXml( const OUString& s ) + { + ::rtl::OUStringBuffer sBuf( s.getLength() ); + const sal_Unicode* pStr = s; + sal_Int32 nLen = s.getLength(); + for( sal_Int32 i = 0; i < nLen; ++i) + { + sal_Unicode c = pStr[ i ]; + switch( c ) + { + case '<': sBuf.appendAscii( "<" ); break; + case '>': sBuf.appendAscii( ">" ); break; + case '&': sBuf.appendAscii( "&" ); break; + case '\'': sBuf.appendAscii( "'" ); break; + case '"': sBuf.appendAscii( """ ); break; + default: sBuf.append( c ); break; + } + } + return sBuf.makeStringAndClear(); + } + + void FastSaxSerializer::write( const OUString& s ) + { + OString sOutput( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ) ); + writeBytes( Sequence< sal_Int8 >( + reinterpret_cast< const sal_Int8*>( sOutput.getStr() ), + sOutput.getLength() ) ); + } + + void SAL_CALL FastSaxSerializer::endDocument( ) throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + } + + void SAL_CALL FastSaxSerializer::writeId( ::sal_Int32 nElement ) + { + if( HAS_NAMESPACE( nElement ) ) { + writeBytes(mxFastTokenHandler->getUTF8Identifier(NAMESPACE(nElement))); + writeBytes(toUnoSequence(aColon)); + writeBytes(mxFastTokenHandler->getUTF8Identifier(TOKEN(nElement))); + } else + writeBytes(mxFastTokenHandler->getUTF8Identifier(nElement)); + } + + void SAL_CALL FastSaxSerializer::startFastElement( ::sal_Int32 Element, const Reference< XFastAttributeList >& Attribs ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + writeBytes(toUnoSequence(aOpeningBracket)); + + writeId(Element); + writeFastAttributeList(Attribs); + + writeBytes(toUnoSequence(aClosingBracket)); + } + + void SAL_CALL FastSaxSerializer::startUnknownElement( const OUString& Namespace, const OUString& Name, const Reference< XFastAttributeList >& Attribs ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + writeBytes(toUnoSequence(aOpeningBracket)); + + if (Namespace.getLength()) + { + write(Namespace); + writeBytes(toUnoSequence(aColon)); + } + + write(Name); + + writeFastAttributeList(Attribs); + + writeBytes(toUnoSequence(aClosingBracket)); + } + + void SAL_CALL FastSaxSerializer::endFastElement( ::sal_Int32 Element ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + writeBytes(toUnoSequence(aOpeningBracketAndSlash)); + + writeId(Element); + + writeBytes(toUnoSequence(aClosingBracket)); + } + + void SAL_CALL FastSaxSerializer::endUnknownElement( const OUString& Namespace, const OUString& Name ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + writeBytes(toUnoSequence(aOpeningBracketAndSlash)); + + if (Namespace.getLength()) + { + write(Namespace); + writeBytes(toUnoSequence(aColon)); + } + + write(Name); + + writeBytes(toUnoSequence(aClosingBracket)); + } + + void SAL_CALL FastSaxSerializer::singleFastElement( ::sal_Int32 Element, const Reference< XFastAttributeList >& Attribs ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + writeBytes(toUnoSequence(aOpeningBracket)); + + writeId(Element); + writeFastAttributeList(Attribs); + + writeBytes(toUnoSequence(aSlashAndClosingBracket)); + } + + void SAL_CALL FastSaxSerializer::singleUnknownElement( const OUString& Namespace, const OUString& Name, const Reference< XFastAttributeList >& Attribs ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + writeBytes(toUnoSequence(aOpeningBracket)); + + if (Namespace.getLength()) + { + write(Namespace); + writeBytes(toUnoSequence(aColon)); + } + + write(Name); + + writeFastAttributeList(Attribs); + + writeBytes(toUnoSequence(aSlashAndClosingBracket)); + } + + void SAL_CALL FastSaxSerializer::characters( const OUString& aChars ) + throw (SAXException, RuntimeException) + { + if (!mxOutputStream.is()) + return; + + write( aChars ); + } + + void SAL_CALL FastSaxSerializer::setOutputStream( const ::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream >& xOutputStream ) + throw (::com::sun::star::uno::RuntimeException) + { + mxOutputStream = xOutputStream; + } + + void SAL_CALL FastSaxSerializer::setFastTokenHandler( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler >& xFastTokenHandler ) + throw (::com::sun::star::uno::RuntimeException) + { + mxFastTokenHandler = xFastTokenHandler; + } + void FastSaxSerializer::writeFastAttributeList( const Reference< XFastAttributeList >& Attribs ) + { + Sequence< Attribute > aAttrSeq = Attribs->getUnknownAttributes(); + const Attribute *pAttr = aAttrSeq.getConstArray(); + sal_Int32 nAttrLength = aAttrSeq.getLength(); + for (sal_Int32 i = 0; i < nAttrLength; i++) + { + writeBytes(toUnoSequence(aSpace)); + + write(pAttr[i].Name); + writeBytes(toUnoSequence(aEqualSignAndQuote)); + write(escapeXml(pAttr[i].Value)); + writeBytes(toUnoSequence(aQuote)); + } + + Sequence< FastAttribute > aFastAttrSeq = Attribs->getFastAttributes(); + const FastAttribute *pFastAttr = aFastAttrSeq.getConstArray(); + sal_Int32 nFastAttrLength = aFastAttrSeq.getLength(); + for (sal_Int32 j = 0; j < nFastAttrLength; j++) + { + writeBytes(toUnoSequence(aSpace)); + + sal_Int32 nToken = pFastAttr[j].Token; + writeId(nToken); + + writeBytes(toUnoSequence(aEqualSignAndQuote)); + + write(escapeXml(Attribs->getValue(pFastAttr[j].Token))); + + writeBytes(toUnoSequence(aQuote)); + } + } + + // XServiceInfo + OUString FastSaxSerializer::getImplementationName() throw (RuntimeException) + { + return OUString::createFromAscii( SERIALIZER_IMPLEMENTATION_NAME ); + } + + // XServiceInfo + sal_Bool FastSaxSerializer::supportsService(const OUString& ServiceName) throw (RuntimeException) + { + Sequence< OUString > aSNL = getSupportedServiceNames(); + const OUString * pArray = aSNL.getConstArray(); + + for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) + if( pArray[i] == ServiceName ) + return sal_True; + + return sal_False; + } + + // XServiceInfo + Sequence< OUString > FastSaxSerializer::getSupportedServiceNames(void) throw (RuntimeException) + { + Sequence<OUString> seq(1); + seq.getArray()[0] = OUString::createFromAscii( SERIALIZER_SERVICE_NAME ); + return seq; + } + + OUString FastSaxSerializer::getImplementationName_Static() + { + return OUString::createFromAscii( SERIALIZER_IMPLEMENTATION_NAME ); + } + + Sequence< OUString > FastSaxSerializer::getSupportedServiceNames_Static(void) + { + Sequence<OUString> aRet(1); + aRet.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM(SERIALIZER_SERVICE_NAME) ); + return aRet; + } + + void FastSaxSerializer::mark() + { + maMarkStack.push( ForMerge() ); + } + + void FastSaxSerializer::mergeTopMarks( sax_fastparser::MergeMarksEnum eMergeType ) + { + if ( maMarkStack.empty() ) + return; + + if ( maMarkStack.size() == 1 ) + { + mxOutputStream->writeBytes( maMarkStack.top().getData() ); + maMarkStack.pop(); + return; + } + + const Int8Sequence aMerge( maMarkStack.top().getData() ); + maMarkStack.pop(); + + switch ( eMergeType ) + { + case MERGE_MARKS_APPEND: maMarkStack.top().append( aMerge ); break; + case MERGE_MARKS_PREPEND: maMarkStack.top().prepend( aMerge ); break; + case MERGE_MARKS_POSTPONE: maMarkStack.top().postpone( aMerge ); break; + } + } + + void FastSaxSerializer::writeBytes( const Sequence< ::sal_Int8 >& aData ) throw ( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException ) + { + if ( maMarkStack.empty() ) + mxOutputStream->writeBytes( aData ); + else + maMarkStack.top().append( aData ); + } + + FastSaxSerializer::Int8Sequence& FastSaxSerializer::ForMerge::getData() + { + merge( maData, maPostponed, true ); + maPostponed.realloc( 0 ); + + return maData; + } + + void FastSaxSerializer::ForMerge::prepend( const Int8Sequence &rWhat ) + { + merge( maData, rWhat, false ); + } + + void FastSaxSerializer::ForMerge::append( const Int8Sequence &rWhat ) + { + merge( maData, rWhat, true ); + } + + void FastSaxSerializer::ForMerge::postpone( const Int8Sequence &rWhat ) + { + merge( maPostponed, rWhat, true ); + } + + void FastSaxSerializer::ForMerge::merge( Int8Sequence &rTop, const Int8Sequence &rMerge, bool bAppend ) + { + sal_Int32 nMergeLen = rMerge.getLength(); + if ( nMergeLen > 0 ) + { + sal_Int32 nTopLen = rTop.getLength(); + + rTop.realloc( nTopLen + nMergeLen ); + if ( bAppend ) + { + // append the rMerge to the rTop + memcpy( rTop.getArray() + nTopLen, rMerge.getConstArray(), nMergeLen ); + } + else + { + // prepend the rMerge to the rTop + memmove( rTop.getArray() + nMergeLen, rTop.getConstArray(), nTopLen ); + memcpy( rTop.getArray(), rMerge.getConstArray(), nMergeLen ); + } + } + } + +} // namespace sax_fastparser + diff --git a/sax/source/tools/fastserializer.hxx b/sax/source/tools/fastserializer.hxx new file mode 100644 index 000000000000..a98a0ff7a67d --- /dev/null +++ b/sax/source/tools/fastserializer.hxx @@ -0,0 +1,161 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef SAX_FASTSERIALIZER_HXX +#define SAX_FASTSERIALIZER_HXX + +#include <com/sun/star/xml/sax/XFastSerializer.hpp> +#include <com/sun/star/xml/sax/XFastTokenHandler.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <cppuhelper/implbase2.hxx> + +#include <stack> + +#include "sax/dllapi.h" +#include "sax/fshelper.hxx" + +#define SERIALIZER_IMPLEMENTATION_NAME "com.sun.star.comp.extensions.xml.sax.FastSerializer" +#define SERIALIZER_SERVICE_NAME "com.sun.star.xml.sax.FastSerializer" + +namespace sax_fastparser { + +class SAX_DLLPUBLIC FastSaxSerializer : public ::cppu::WeakImplHelper2< ::com::sun::star::xml::sax::XFastSerializer, ::com::sun::star::lang::XServiceInfo > +{ +public: + explicit FastSaxSerializer( ); + virtual ~FastSaxSerializer(); + + ::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream > getOutputStream() {return mxOutputStream;} + + // The implementation details + static ::com::sun::star::uno::Sequence< ::rtl::OUString > getSupportedServiceNames_Static(void); + static ::rtl::OUString getImplementationName_Static(); + + // XFastSerializer + virtual void SAL_CALL startDocument( ) throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL endDocument( ) throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL startFastElement( ::sal_Int32 Element, const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastAttributeList >& Attribs ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL startUnknownElement( const ::rtl::OUString& Namespace, const ::rtl::OUString& Name, const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastAttributeList >& Attribs ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL endFastElement( ::sal_Int32 Element ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL endUnknownElement( const ::rtl::OUString& Namespace, const ::rtl::OUString& Name ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL singleFastElement( ::sal_Int32 Element, const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastAttributeList >& Attribs ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL singleUnknownElement( const ::rtl::OUString& Namespace, const ::rtl::OUString& Name, const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastAttributeList >& Attribs ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL characters( const ::rtl::OUString& aChars ) + throw (::com::sun::star::xml::sax::SAXException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setOutputStream( const ::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream >& xOutputStream ) + throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setFastTokenHandler( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler >& xFastTokenHandler ) + throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName( ) throw ( ::com::sun::star::uno::RuntimeException ); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw ( ::com::sun::star::uno::RuntimeException ); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw ( ::com::sun::star::uno::RuntimeException ); + + // C++ helpers + virtual void SAL_CALL writeId( ::sal_Int32 Element ); + + static ::rtl::OUString escapeXml( const ::rtl::OUString& s ); + +public: + /** From now on, don't write directly to the stream, but to top of a stack. + + This is to be able to change the order of the data being written. + If you need to write eg. + p, r, rPr, [something], /rPr, t, [text], /r, /p, + but get it in order + p, r, t, [text], /t, rPr, [something], /rPr, /r, /p, + simply do + p, r, mark(), t, [text], /t, mark(), rPr, [something], /rPr, + mergeTopMarks( true ), mergeTopMarks(), /r, /p + and you are done. + */ + void mark(); + + /** Merge 2 topmost marks. + + There are 3 possibilities - prepend the top before the second top-most + mark, append it, or append it later; prepending brings the possibility + to switch parts of the output, appending later allows to write some + output in advance. + + Writes the result to the output stream if the mark stack becomes empty + by the operation. + + When the MERGE_MARKS_POSTPONE is specified, the merge happens just + before the next merge. + + @see mark() + */ + void mergeTopMarks( sax_fastparser::MergeMarksEnum eMergeType = sax_fastparser::MERGE_MARKS_APPEND ); + +private: + ::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream > mxOutputStream; + ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastTokenHandler > mxFastTokenHandler; + + typedef ::com::sun::star::uno::Sequence< ::sal_Int8 > Int8Sequence; + class ForMerge + { + Int8Sequence maData; + Int8Sequence maPostponed; + + public: + ForMerge() : maData(), maPostponed() {} + + Int8Sequence& getData(); + + void prepend( const Int8Sequence &rWhat ); + void append( const Int8Sequence &rWhat ); + void postpone( const Int8Sequence &rWhat ); + + private: + static void merge( Int8Sequence &rTop, const Int8Sequence &rMerge, bool bAppend ); + }; + + ::std::stack< ForMerge > maMarkStack; + + void writeFastAttributeList( const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XFastAttributeList >& Attribs ); + void write( const ::rtl::OUString& s ); + +protected: + /** Forward the call to the output stream, or write to the stack. + + The latter in the case that we are inside a mark(). + */ + void writeBytes( const ::com::sun::star::uno::Sequence< ::sal_Int8 >& aData ) throw (::com::sun::star::io::NotConnectedException, ::com::sun::star::io::BufferSizeExceededException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException); +}; + +} // namespace sax_fastparser + +#endif diff --git a/sax/source/tools/fshelper.cxx b/sax/source/tools/fshelper.cxx new file mode 100644 index 000000000000..743f499fb4f0 --- /dev/null +++ b/sax/source/tools/fshelper.cxx @@ -0,0 +1,201 @@ +#include <sax/fshelper.hxx> +#include "fastserializer.hxx" +#include <com/sun/star/xml/sax/XFastTokenHandler.hpp> +#include <comphelper/processfactory.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sax_fastparser { + +FastSerializerHelper::FastSerializerHelper(const Reference< io::XOutputStream >& xOutputStream ) : + mpSerializer(new FastSaxSerializer()) +{ + Reference< lang::XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory(); + mxTokenHandler = Reference<xml::sax::XFastTokenHandler> ( xFactory->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.xml.sax.FastTokenHandler") ) ), UNO_QUERY_THROW ); + + mpSerializer->setFastTokenHandler( mxTokenHandler ); + mpSerializer->setOutputStream( xOutputStream ); + mpSerializer->startDocument(); +} + +FastSerializerHelper::~FastSerializerHelper() +{ + mpSerializer->endDocument(); + + if (mpSerializer) { + delete mpSerializer; + mpSerializer = NULL; + } +} + +void FastSerializerHelper::startElement(const char* elementName, ...) +{ + FastAttributeList* pAttrList = new FastAttributeList( mxTokenHandler ); + va_list args; + va_start(args, elementName); + while (true) + { + const char* pName = va_arg(args, const char*); + if (!pName) + break; + const char* pValue = va_arg(args, const char*); + if (pValue) + pAttrList->addUnknown(pName, pValue); + } + va_end(args); + const com::sun::star::uno::Reference<com::sun::star::xml::sax::XFastAttributeList> xAttrList(pAttrList); + mpSerializer->startUnknownElement(::rtl::OUString(), ::rtl::OUString::createFromAscii(elementName), xAttrList); +} + +void FastSerializerHelper::singleElement(const char* elementName, ...) +{ + FastAttributeList* pAttrList = new FastAttributeList( mxTokenHandler ); + va_list args; + va_start(args, elementName); + while (true) + { + const char* pName = va_arg(args, const char*); + if (!pName) + break; + const char* pValue = va_arg(args, const char*); + if (pValue) + pAttrList->addUnknown(pName, pValue); + } + va_end(args); + const com::sun::star::uno::Reference<com::sun::star::xml::sax::XFastAttributeList> xAttrList(pAttrList); + mpSerializer->singleUnknownElement(::rtl::OUString(), ::rtl::OUString::createFromAscii(elementName), xAttrList); +} + +void FastSerializerHelper::endElement(const char* elementName) +{ + mpSerializer->endUnknownElement(::rtl::OUString(), ::rtl::OUString::createFromAscii(elementName)); +} + +void FastSerializerHelper::startElementV(sal_Int32 elementTokenId, va_list args) +{ + FastAttributeList* pAttrList = new FastAttributeList( mxTokenHandler ); + + while (true) + { + sal_Int32 nName = va_arg(args, sal_Int32); + if (nName == FSEND) + break; + const char* pValue = va_arg(args, const char*); + if (pValue) + pAttrList->add(nName, pValue); + } + + const com::sun::star::uno::Reference<com::sun::star::xml::sax::XFastAttributeList> xAttrList(pAttrList); + mpSerializer->startFastElement(elementTokenId, xAttrList); +} + +void FastSerializerHelper::singleElementV(sal_Int32 elementTokenId, va_list args) +{ + FastAttributeList* pAttrList = new FastAttributeList( mxTokenHandler ); + + while (true) + { + sal_Int32 nName = va_arg(args, sal_Int32); + if (nName == FSEND) + break; + const char* pValue = va_arg(args, const char*); + if (pValue) + pAttrList->add(nName, pValue); + } + + const com::sun::star::uno::Reference<com::sun::star::xml::sax::XFastAttributeList> xAttrList(pAttrList); + mpSerializer->singleFastElement(elementTokenId, xAttrList); +} + +void FastSerializerHelper::endElement(sal_Int32 elementTokenId) +{ + mpSerializer->endFastElement(elementTokenId); +} + +void FastSerializerHelper::startElementV(sal_Int32 elementTokenId, XFastAttributeListRef xAttrList) +{ + mpSerializer->startFastElement(elementTokenId, xAttrList); +} + + +void FastSerializerHelper::singleElement(const char* elementName, XFastAttributeListRef xAttrList) +{ + mpSerializer->singleUnknownElement(::rtl::OUString(), ::rtl::OUString::createFromAscii(elementName), xAttrList); +} + +void FastSerializerHelper::singleElementV(sal_Int32 elementTokenId, XFastAttributeListRef xAttrList) +{ + mpSerializer->singleFastElement(elementTokenId, xAttrList); +} + +FastSerializerHelper* FastSerializerHelper::write(const char* value) +{ + return write(rtl::OUString::createFromAscii(value)); +} + +FastSerializerHelper* FastSerializerHelper::write(const rtl::OUString& value) +{ + mpSerializer->characters(value); + return this; +} + +FastSerializerHelper* FastSerializerHelper::write(sal_Int32 value) +{ + return write(::rtl::OUString::valueOf(value)); +} + +FastSerializerHelper* FastSerializerHelper::write(sal_Int64 value) +{ + return write(::rtl::OUString::valueOf(value)); +} + +FastSerializerHelper* FastSerializerHelper::write(float value) +{ + return write(::rtl::OUString::valueOf(value)); +} + +FastSerializerHelper* FastSerializerHelper::write(double value) +{ + return write(::rtl::OUString::valueOf(value)); +} + +FastSerializerHelper* FastSerializerHelper::writeEscaped(const char* value) +{ + return writeEscaped(::rtl::OUString::createFromAscii(value)); +} + +FastSerializerHelper* FastSerializerHelper::writeEscaped(const ::rtl::OUString& value) +{ + return write(FastSaxSerializer::escapeXml(value)); +} + +FastSerializerHelper* FastSerializerHelper::writeId(sal_Int32 tokenId) +{ + mpSerializer->writeId(tokenId); + return this; +} + +::com::sun::star::uno::Reference< ::com::sun::star::io::XOutputStream > FastSerializerHelper::getOutputStream() +{ + return mpSerializer->getOutputStream(); +} + +void FastSerializerHelper::mark() +{ + mpSerializer->mark(); +} + +void FastSerializerHelper::mergeTopMarks( MergeMarksEnum eMergeType ) +{ + mpSerializer->mergeTopMarks( eMergeType ); +} + +FastAttributeList * FastSerializerHelper::createAttrList() +{ + return new FastAttributeList( mxTokenHandler ); +} + + +} diff --git a/sax/source/tools/makefile.mk b/sax/source/tools/makefile.mk new file mode 100644 index 000000000000..5923fc5a7785 --- /dev/null +++ b/sax/source/tools/makefile.mk @@ -0,0 +1,67 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=sax +TARGET=sax +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES = \ + $(SLO)$/converter.obj \ + $(SLO)$/fastattribs.obj \ + $(SLO)$/fastserializer.obj \ + $(SLO)$/fshelper.obj + +SHL1TARGET= $(TARGET)$(DLLPOSTFIX) +SHL1IMPLIB= i$(TARGET) + +SHL1STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB)\ + $(COMPHELPERLIB)\ + $(RTLLIB) \ + $(SALLIB) \ + $(ONELIB) + +SHL1DEPN= +SHL1OBJS= $(SLOFILES) +SHL1USE_EXPORTS=name +SHL1DEF= $(MISC)$/$(SHL1TARGET).def +DEF1NAME= $(SHL1TARGET) +DEFLIB1NAME= $(TARGET) + +# --- Targets ------------------------------------------------------- + +.INCLUDE : target.mk diff --git a/sax/test/makefile.mk b/sax/test/makefile.mk new file mode 100644 index 000000000000..7fb7837a4ff3 --- /dev/null +++ b/sax/test/makefile.mk @@ -0,0 +1,62 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=.. + +PRJNAME=extensions +TARGET=workben +LIBTARGET=NO + +TARGETTYPE=CUI +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +# --- Files -------------------------------------------------------- + + + +# +# std testcomponent +# +APP1TARGET = testcomponent +APP2TARGET = saxdemo + +APP1OBJS = $(OBJ)$/testcomponent.obj +APP1STDLIBS = $(SALLIB) \ + $(CPPULIB)\ + $(CPPUHELPERLIB) + +APP2OBJS = $(OBJ)$/saxdemo.obj +APP2STDLIBS = $(SALLIB) \ + $(CPPULIB) \ + $(CPPUHELPERLIB) + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/sax/test/sax/exports.dxp b/sax/test/sax/exports.dxp new file mode 100644 index 000000000000..ce95ae0f8deb --- /dev/null +++ b/sax/test/sax/exports.dxp @@ -0,0 +1,3 @@ +component_getImplementationEnvironment +component_getFactory +component_writeInfo
\ No newline at end of file diff --git a/sax/test/sax/factory.hxx b/sax/test/sax/factory.hxx new file mode 100644 index 000000000000..4ab5553a8b78 --- /dev/null +++ b/sax/test/sax/factory.hxx @@ -0,0 +1,89 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include <rtl/strbuf.hxx> + +namespace sax_test { +Reference< XInterface > SAL_CALL OSaxWriterTest_CreateInstance( + const Reference< XMultiServiceFactory > & rSMgr ) throw ( Exception ); +OUString OSaxWriterTest_getServiceName( ) throw(); +OUString OSaxWriterTest_getImplementationName( ) throw(); +Sequence<OUString> OSaxWriterTest_getSupportedServiceNames( ) throw(); +} +#define BUILD_ERROR(expr, Message)\ + {\ + m_seqErrors.realloc( m_seqErrors.getLength() + 1 ); \ + m_seqExceptions.realloc( m_seqExceptions.getLength() + 1 ); \ + OStringBuffer str(128); \ + str.append( __FILE__ );\ + str.append( " " ); \ + str.append( "(" ); \ + str.append( OString::valueOf( (sal_Int32)__LINE__) );\ + str.append(")\n" );\ + str.append( "[ " ); \ + str.append( #expr ); \ + str.append( " ] : " ); \ + str.append( Message ); \ + m_seqErrors.getArray()[ m_seqErrors.getLength()-1] =\ + OStringToOUString( str.makeStringAndClear() , RTL_TEXTENCODING_ASCII_US ); \ + }\ + ((void)0) + + +#define WARNING_ASSERT(expr, Message) \ + if( ! (expr) ) { \ + m_seqWarnings.realloc( m_seqErrors.getLength() +1 ); \ + OStringBuffer str(128);\ + str.append( __FILE__);\ + str.append( " "); \ + str.append( "(" ); \ + str.append(OString::valueOf( (sal_Int32)__LINE__)) ;\ + str.append( ")\n");\ + str.append( "[ " ); \ + str.append( #expr ); \ + str.append( " ] : ") ; \ + str.append( Message); \ + m_seqWarnings.getArray()[ m_seqWarnings.getLength()-1] =\ + OStringToOUString( str.makeStringAndClear() , RTL_TEXTENCODING_ASCII_US ); \ + return; \ + }\ + ((void)0) + +#define ERROR_ASSERT(expr, Message) \ + if( ! (expr) ) { \ + BUILD_ERROR(expr, Message );\ + return; \ + }\ + ((void)0) + +#define ERROR_EXCEPTION_ASSERT(expr, Message, Exception) \ + if( !(expr)) { \ + BUILD_ERROR(expr,Message);\ + m_seqExceptions.getArray()[ m_seqExceptions.getLength()-1] = Any( Exception );\ + return; \ + } \ + ((void)0) + diff --git a/sax/test/sax/makefile.mk b/sax/test/sax/makefile.mk new file mode 100644 index 000000000000..b283366ff8a3 --- /dev/null +++ b/sax/test/sax/makefile.mk @@ -0,0 +1,61 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ=..$/.. + +PRJNAME=extensions +TARGET=testsax +USE_DEFFILE=TRUE +ENABLE_EXCEPTIONS=TRUE +# --- Settings ----------------------------------------------------- +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + + +SLOFILES = $(SLO)$/testsax.obj \ + $(SLO)$/testwriter.obj + +SHL1TARGET= $(TARGET) +SHL1IMPLIB= i$(TARGET) + +SHL1STDLIBS= \ + $(SALLIB) \ + $(CPPULIB) \ + $(CPPUHELPERLIB) + + +SHL1LIBS= $(SLB)$/$(TARGET).lib +SHL1DEPN= makefile.mk $(SHL1LIBS) +SHL1DEF= $(MISC)$/$(SHL1TARGET).def + +DEF1NAME= $(SHL1TARGET) +DEF1EXPORTFILE= exports.dxp + + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/sax/test/sax/testsax.cxx b/sax/test/sax/testsax.cxx new file mode 100644 index 000000000000..b51f4cb59181 --- /dev/null +++ b/sax/test/sax/testsax.cxx @@ -0,0 +1,870 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <stdio.h> +#include <string.h> + +#include <osl/time.h> +#include <osl/diagnose.h> + + +#include <com/sun/star/test/XSimpleTest.hpp> + +#include <com/sun/star/io/XOutputStream.hpp> + +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase1.hxx> +#include <cppuhelper/implbase3.hxx> + +using namespace ::rtl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::test; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::xml::sax; + +#include "factory.hxx" + +/**** +* test szenarios : +* +* +* +****/ + +namespace sax_test { + +class OSaxParserTest : public WeakImplHelper1< XSimpleTest > +{ +public: + OSaxParserTest( const Reference < XMultiServiceFactory > & rFactory ) : m_rFactory( rFactory ) + { + } +public: + virtual void SAL_CALL testInvariant( + const OUString& TestName, + const Reference < XInterface >& TestObject) + throw ( IllegalArgumentException, RuntimeException); + + virtual sal_Int32 SAL_CALL test( + const OUString& TestName, + const Reference < XInterface >& TestObject, + sal_Int32 hTestHandle) + throw ( IllegalArgumentException,RuntimeException); + + virtual sal_Bool SAL_CALL testPassed(void) throw ( RuntimeException); + virtual Sequence< OUString > SAL_CALL getErrors(void) throw (RuntimeException); + virtual Sequence< Any > SAL_CALL getErrorExceptions(void) throw (RuntimeException); + virtual Sequence< OUString > SAL_CALL getWarnings(void) throw (RuntimeException); + +private: + void testSimple( const Reference < XParser > &r ); + void testNamespaces( const Reference < XParser > &r ); + void testFile( const Reference < XParser > &r ); + void testEncoding( const Reference < XParser > &rParser ); + void testPerformance( const Reference < XParser > &rParser ); + +private: + Sequence<Any> m_seqExceptions; + Sequence<OUString> m_seqErrors; + Sequence<OUString> m_seqWarnings; + Reference < XMultiServiceFactory > m_rFactory; +}; + + + +/** +* for external binding +* +* +**/ +Reference < XInterface > SAL_CALL OSaxParserTest_CreateInstance( const Reference < XMultiServiceFactory > & rSMgr ) throw(Exception) +{ + OSaxParserTest *p = new OSaxParserTest( rSMgr ); + return Reference < XInterface > ( SAL_STATIC_CAST( OWeakObject * , p ) ); +} + + +OUString OSaxParserTest_getServiceName( ) throw () +{ + return OUString( RTL_CONSTASCII_USTRINGPARAM("test.com.sun.star.xml.sax.Parser" )); +} + +OUString OSaxParserTest_getImplementationName( ) throw () +{ + return OUString( RTL_CONSTASCII_USTRINGPARAM("test.extensions.xml.sax.Parser")); +} + +Sequence<OUString> OSaxParserTest_getSupportedServiceNames( ) throw () +{ + Sequence<OUString> aRet(1); + + aRet.getArray()[0] = OSaxParserTest_getImplementationName( ); + + return aRet; +} + + + + +void OSaxParserTest::testInvariant( + const OUString& TestName, + const Reference < XInterface >& TestObject ) + throw ( IllegalArgumentException, RuntimeException) +{ + if( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Parser")) == TestName ) { + Reference < XParser > parser( TestObject , UNO_QUERY ); + + ERROR_ASSERT( parser.is() , "XDataInputStream cannot be queried" ); + } +} + + +sal_Int32 OSaxParserTest::test( + const OUString& TestName, + const Reference < XInterface >& TestObject, + sal_Int32 hTestHandle) + throw ( IllegalArgumentException, RuntimeException) +{ + if( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Parser")) == TestName ) { + try + { + if( 0 == hTestHandle ) { + testInvariant( TestName , TestObject ); + } + else { + + Reference < XParser > parser( TestObject , UNO_QUERY ); + + if( 1 == hTestHandle ) { + testSimple( parser ); + } + else if( 2 == hTestHandle ) { + testNamespaces( parser ); + } + else if( 3 == hTestHandle ) { + testEncoding( parser ); + } + else if( 4 == hTestHandle ) { + testFile( parser ); + } + else if( 5 == hTestHandle ) { + testPerformance( parser ); + } + } + } + catch( Exception & e ) + { + OString o = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US); + BUILD_ERROR( 0 , o.getStr() ); + } + catch( ... ) + { + BUILD_ERROR( 0 , "unknown exception (Exception is not base class)" ); + } + + hTestHandle ++; + + if( hTestHandle >= 6) { + // all tests finished. + hTestHandle = -1; + } + } + else { + BUILD_ERROR( 0 , "service not supported by test." ); + } + return hTestHandle; +} + + + +sal_Bool OSaxParserTest::testPassed(void) throw (RuntimeException) +{ + return m_seqErrors.getLength() == 0; +} + + +Sequence< OUString > OSaxParserTest::getErrors(void) throw (RuntimeException) +{ + return m_seqErrors; +} + + +Sequence< Any > OSaxParserTest::getErrorExceptions(void) throw (RuntimeException) +{ + return m_seqExceptions; +} + + +Sequence< OUString > OSaxParserTest::getWarnings(void) throw (RuntimeException) +{ + return m_seqWarnings; +} + +Reference < XInputStream > createStreamFromSequence( + const Sequence<sal_Int8> seqBytes , + const Reference < XMultiServiceFactory > &xSMgr ) +{ + Reference < XInterface > xOutStreamService = + xSMgr->createInstance( OUString::createFromAscii("com.sun.star.io.Pipe") ); + OSL_ASSERT( xOutStreamService.is() ); + Reference< XOutputStream > rOutStream( xOutStreamService , UNO_QUERY ); + OSL_ASSERT( rOutStream.is() ); + + Reference< XInputStream > rInStream( xOutStreamService , UNO_QUERY ); + OSL_ASSERT( rInStream.is() ); + + rOutStream->writeBytes( seqBytes ); + rOutStream->flush(); + rOutStream->closeOutput(); + + return rInStream; +} + +Reference< XInputStream > createStreamFromFile( + const char *pcFile , + const Reference < XMultiServiceFactory > &xSMgr ) +{ + FILE *f = fopen( pcFile , "rb" ); + Reference< XInputStream > r; + + if( f ) { + fseek( f , 0 , SEEK_END ); + int nLength = ftell( f ); + fseek( f , 0 , SEEK_SET ); + + Sequence<sal_Int8> seqIn(nLength); + fread( seqIn.getArray() , nLength , 1 , f ); + + r = createStreamFromSequence( seqIn , xSMgr ); + fclose( f ); + } + return r; +} + + + + + + + + + +// #define PCHAR_TO_OUSTRING(x) OStringToOUString(x,CHARSET_PC_1252) +// #define USTRING_TO_PCHAR(x) UStringToString(x,CHARSET_PC_437).GetStr() + + + +class TestDocumentHandler : + public WeakImplHelper3< XExtendedDocumentHandler , XEntityResolver , XErrorHandler > +{ +public: + TestDocumentHandler( const Reference < XMultiServiceFactory > &r , sal_Bool bPrint ) + { + m_xSMgr = r; + m_bPrint = bPrint; + } + +public: // Error handler + virtual void SAL_CALL error(const Any& aSAXParseException) throw (SAXException, RuntimeException) + { + printf( "Error !\n" ); + throw SAXException( + OUString( RTL_CONSTASCII_USTRINGPARAM("error from error handler")) , + Reference < XInterface >() , + aSAXParseException ); + } + virtual void SAL_CALL fatalError(const Any& aSAXParseException) throw (SAXException, RuntimeException) + { + printf( "Fatal Error !\n" ); + } + virtual void SAL_CALL warning(const Any& aSAXParseException) throw (SAXException, RuntimeException) + { + printf( "Warning !\n" ); + } + + +public: // ExtendedDocumentHandler + + virtual void SAL_CALL startDocument(void) throw (SAXException, RuntimeException) + { + m_iLevel = 0; + m_iElementCount = 0; + m_iAttributeCount = 0; + m_iWhitespaceCount =0; + m_iCharCount=0; + if( m_bPrint ) { + printf( "document started\n" ); + } + } + virtual void SAL_CALL endDocument(void) throw (SAXException, RuntimeException) + { + if( m_bPrint ) { + printf( "document finished\n" ); + printf( "(ElementCount %d),(AttributeCount %d),(WhitespaceCount %d),(CharCount %d)\n", + m_iElementCount, m_iAttributeCount, m_iWhitespaceCount , m_iCharCount ); + } + } + virtual void SAL_CALL startElement(const OUString& aName, + const Reference< XAttributeList > & xAttribs) + throw (SAXException,RuntimeException) + { + + if( m_rLocator.is() ) { + if( m_bPrint ) + { + OString o = OUStringToOString( m_rLocator->getSystemId() , RTL_TEXTENCODING_UTF8 ); + printf( "%s(%d):" , o.getStr() , m_rLocator->getLineNumber() ); + } + } + if( m_bPrint ) { + int i; + for( i = 0; i < m_iLevel ; i ++ ) { + printf( " " ); + } + OString o = OUStringToOString(aName , RTL_TEXTENCODING_UTF8 ); + printf( "<%s> " , aName.getStr() ); + + for( i = 0 ; i < xAttribs->getLength() ; i ++ ) + { + OString o1 = OUStringToOString(xAttribs->getNameByIndex( i ), RTL_TEXTENCODING_UTF8 ); + OString o2 = OUStringToOString(xAttribs->getTypeByIndex( i ), RTL_TEXTENCODING_UTF8 ); + OString o3 = OUStringToOString(xAttribs->getValueByIndex( i ) , RTL_TEXTENCODING_UTF8 ); + printf( "(%s,%s,'%s')" , o1.getStr(), o2.getStr(), o3.getStr() ); + } + printf( "\n" ); + } + m_iLevel ++; + m_iElementCount ++; + m_iAttributeCount += xAttribs->getLength(); + } + + virtual void SAL_CALL endElement(const OUString& aName) throw (SAXException,RuntimeException) + { + OSL_ASSERT( m_iLevel ); + m_iLevel --; + if( m_bPrint ) { + int i; + for( i = 0; i < m_iLevel ; i ++ ) { + printf( " " ); + } + OString o = OUStringToOString(aName , RTL_TEXTENCODING_UTF8 ); + printf( "</%s>\n" , o.getStr() ); + } + } + + virtual void SAL_CALL characters(const OUString& aChars) throw (SAXException,RuntimeException) + { + if( m_bPrint ) { + int i; + for( i = 0; i < m_iLevel ; i ++ ) { + printf( " " ); + } + OString o = OUStringToOString(aChars , RTL_TEXTENCODING_UTF8 ); + printf( "%s\n" , o.getStr() ); + } + m_iCharCount += aChars.getLength(); + } + virtual void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) throw (SAXException,RuntimeException) + { + m_iWhitespaceCount += aWhitespaces.getLength(); + } + + virtual void SAL_CALL processingInstruction(const OUString& aTarget, const OUString& aData) throw (SAXException,RuntimeException) + { + if( m_bPrint ) + { + OString o1 = OUStringToOString(aTarget, RTL_TEXTENCODING_UTF8 ); + OString o2 = OUStringToOString(aData, RTL_TEXTENCODING_UTF8 ); + printf( "PI : %s,%s\n" , o1.getStr() , o2.getStr() ); + } + } + + virtual void SAL_CALL setDocumentLocator(const Reference< XLocator> & xLocator) + throw (SAXException,RuntimeException) + { + m_rLocator = xLocator; + } + + virtual InputSource SAL_CALL resolveEntity( + const OUString& sPublicId, + const OUString& sSystemId) + throw (SAXException,RuntimeException) + { + InputSource source; + source.sSystemId = sSystemId; + source.sPublicId = sPublicId; + + source.aInputStream = createStreamFromFile( + OUStringToOString( sSystemId , RTL_TEXTENCODING_ASCII_US) , m_xSMgr ); + + return source; + } + + virtual void SAL_CALL startCDATA(void) throw (SAXException,RuntimeException) + { + if( m_bPrint ) { + printf( "CDataStart :\n" ); + } + } + virtual void SAL_CALL endCDATA(void) throw (SAXException,RuntimeException) + { + if( m_bPrint ) { + printf( "CEndStart :\n" ); + } + } + virtual void SAL_CALL comment(const OUString& sComment) throw (SAXException,RuntimeException) + { + if( m_bPrint ) { + OString o1 = OUStringToOString(sComment, RTL_TEXTENCODING_UTF8 ); + printf( "<!--%s-->\n" , o1.getStr() ); + } + } + virtual void SAL_CALL unknown(const OUString& sString) throw (SAXException,RuntimeException) + { + if( m_bPrint ) + { + OString o1 = OUStringToOString(sString, RTL_TEXTENCODING_UTF8 ); + printf( "UNKNOWN : {%s}\n" , o1.getStr() ); + } + } + + virtual void SAL_CALL allowLineBreak( void) throw (SAXException, RuntimeException ) + { + + } + + +public: + int m_iLevel; + int m_iElementCount; + int m_iAttributeCount; + int m_iWhitespaceCount; + int m_iCharCount; + sal_Bool m_bPrint; + + Reference < XMultiServiceFactory > m_xSMgr; + Reference < XLocator > m_rLocator; +}; + + +void OSaxParserTest::testSimple( const Reference < XParser > &rParser ) +{ + + char TestString[] = "<!DOCTYPE personnel [\n" + "<!ENTITY testInternal \"internal Test!\">\n" + "<!ENTITY test SYSTEM \"external_entity.xml\">\n" + "]>\n" + "<personnel>\n" + "<person> fjklsfdklsdfkl\n" + "fjklsfdklsdfkl\n" + "<?testpi pidata?>\n" + "&testInternal;\n" + "<HUHU x='5' y='kjfd'> blahuhu\n" + "<HI> blahi\n" + " <![CDATA[<greeting>Hello, '+1+12world!</greeting>]]>\n" + " <!-- huhu <jdk> -->\n" + "<?testpi pidata?>\n" + "</HI>\n" + "aus XMLTest\n" + "</HUHU>\n" + "</person>\n" + "</personnel>\n\n\n"; + + Sequence< sal_Int8> seqBytes( strlen( TestString ) ); + memcpy( seqBytes.getArray() , TestString , strlen( TestString ) ); + + + Reference< XInputStream > rInStream; + OUString sInput; + rInStream = createStreamFromSequence( seqBytes , m_rFactory ); + sInput = OUString( OUString( RTL_CONSTASCII_USTRINGPARAM("internal")) ); + + if( rParser.is() ) { + InputSource source; + + source.aInputStream = rInStream; + source.sSystemId = sInput; + + TestDocumentHandler *pDocHandler = new TestDocumentHandler( m_rFactory , sal_False ); + Reference < XDocumentHandler > rDocHandler( (XDocumentHandler *) pDocHandler , UNO_QUERY ); + Reference< XEntityResolver > + rEntityResolver( (XEntityResolver *) pDocHandler , UNO_QUERY ); + + rParser->setDocumentHandler( rDocHandler ); + rParser->setEntityResolver( rEntityResolver ); + + try + { + rParser->parseStream( source ); + ERROR_ASSERT( pDocHandler->m_iElementCount == 4 , "wrong element count" ); + ERROR_ASSERT( pDocHandler->m_iAttributeCount == 2 , "wrong attribut count" ); + ERROR_ASSERT( pDocHandler->m_iCharCount == 130 , "wrong char count" ); + ERROR_ASSERT( pDocHandler->m_iWhitespaceCount == 0, "wrong whitespace count" ); + } + catch( SAXParseException & e ) + { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + BUILD_ERROR( 1 , o1.getStr() ); + } + catch( SAXException & e ) + { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + BUILD_ERROR( 1 , o1.getStr() ); + } + catch( Exception & e ) + { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + BUILD_ERROR( 1 , o1.getStr() ); + } + catch( ... ) + { + BUILD_ERROR( 1 , "unknown exception" ); + } + } +} + +void OSaxParserTest::testNamespaces( const Reference < XParser > &rParser ) +{ + + char TestString[] = + "<?xml version='1.0'?>\n" + "<!-- all elements here are explicitly in the HTML namespace -->\n" + "<html:html xmlns:html='http://www.w3.org/TR/REC-html40'>\n" + "<html:head><html:title>Frobnostication</html:title></html:head>\n" + "<html:body><html:p>Moved to \n" + "<html:a href='http://frob.com'>here.</html:a></html:p></html:body>\n" + "</html:html>\n"; + + Sequence<sal_Int8> seqBytes( strlen( TestString ) ); + memcpy( seqBytes.getArray() , TestString , strlen( TestString ) ); + + + Reference< XInputStream > rInStream; + OUString sInput; + + rInStream = createStreamFromSequence( seqBytes , m_rFactory ); + sInput = OUString( RTL_CONSTASCII_USTRINGPARAM( "internal" )); + + if( rParser.is() ) { + InputSource source; + + source.aInputStream = rInStream; + source.sSystemId = sInput; + + TestDocumentHandler *pDocHandler = new TestDocumentHandler( m_rFactory , sal_False ); + Reference < XDocumentHandler > rDocHandler( (XDocumentHandler *) pDocHandler , UNO_QUERY ); + Reference< XEntityResolver > rEntityResolver( + (XEntityResolver *) pDocHandler , UNO_QUERY ); + + rParser->setDocumentHandler( rDocHandler ); + rParser->setEntityResolver( rEntityResolver ); + + try + { + rParser->parseStream( source ); + ERROR_ASSERT( pDocHandler->m_iElementCount == 6 , "wrong element count" ); + ERROR_ASSERT( pDocHandler->m_iAttributeCount == 2 , "wrong attribut count" ); + ERROR_ASSERT( pDocHandler->m_iCharCount == 33, "wrong char count" ); + ERROR_ASSERT( pDocHandler->m_iWhitespaceCount == 0 , "wrong whitespace count" ); + } + catch( Exception & e ) { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + BUILD_ERROR( 1 , o1.getStr() ); + } + catch( ... ) + { + BUILD_ERROR( 1 , "unknown exception" ); + } + } +} + +void OSaxParserTest::testEncoding( const Reference < XParser > &rParser ) +{ + char TestString[] = + "<?xml version='1.0' encoding=\"iso-8859-1\"?>\n" + "<!-- all elements here are explicitly in the HTML namespace -->\n" + "<html:html xmlns:html='http://www.w3.org/TR/REC-html40'>\n" + "<html:head><html:title>Frobnostication</html:title></html:head>\n" + "<html:body><html:p>Moved to ß\n" + "<html:a href='http://frob.com'>here.</html:a></html:p></html:body>\n" + "</html:html>\n"; + + Sequence<sal_Int8> seqBytes( strlen( TestString ) ); + memcpy( seqBytes.getArray() , TestString , strlen( TestString ) ); + + + Reference< XInputStream > rInStream; + OUString sInput; + + rInStream = createStreamFromSequence( seqBytes , m_rFactory ); + sInput = OUString( RTL_CONSTASCII_USTRINGPARAM("internal") ); + + if( rParser.is() ) { + InputSource source; + + source.aInputStream = rInStream; + source.sSystemId = sInput; + + TestDocumentHandler *pDocHandler = new TestDocumentHandler( m_rFactory , sal_False ); + Reference < XDocumentHandler > rDocHandler( (XDocumentHandler *) pDocHandler , UNO_QUERY ); + Reference< XEntityResolver > rEntityResolver( (XEntityResolver *) pDocHandler , UNO_QUERY ); + + rParser->setDocumentHandler( rDocHandler ); + rParser->setEntityResolver( rEntityResolver ); + try + { + rParser->parseStream( source ); + } + catch( Exception & e ) + { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + BUILD_ERROR( 1 , o1.getStr() ); + } + catch ( ... ) + { + BUILD_ERROR( 1 , "unknown exception" ); + } + } +} + +void OSaxParserTest::testFile( const Reference < XParser > & rParser ) +{ + + Reference< XInputStream > rInStream = createStreamFromFile( "testsax.xml" , m_rFactory ); + OUString sInput = OUString( RTL_CONSTASCII_USTRINGPARAM( "testsax.xml" ) ); + + + if( rParser.is() && rInStream.is() ) { + InputSource source; + + source.aInputStream = rInStream; + source.sSystemId = sInput; + + TestDocumentHandler *pDocHandler = new TestDocumentHandler( m_rFactory , sal_True ); + Reference < XDocumentHandler > rDocHandler( (XDocumentHandler *) pDocHandler , UNO_QUERY ); + Reference < XEntityResolver > rEntityResolver( (XEntityResolver *) pDocHandler , UNO_QUERY ); + Reference < XErrorHandler > rErrorHandler( ( XErrorHandler * )pDocHandler , UNO_QUERY ); + + rParser->setDocumentHandler( rDocHandler ); + rParser->setEntityResolver( rEntityResolver ); + rParser->setErrorHandler( rErrorHandler ); + + try + { + rParser->parseStream( source ); + } + catch( SAXParseException & e ) { + Any any; + any <<= e; + + while(sal_True) { + SAXParseException *pEx; + if( any.getValueType() == getCppuType( &e ) ) { + pEx = ( SAXParseException * ) any.getValue(); + OString o1 = OUStringToOString(pEx->Message, RTL_TEXTENCODING_UTF8 ); + printf( "%s\n" , o1.getStr() ); + any = pEx->WrappedException; + } + else { + break; + } + } + } + catch( SAXException & e ) + { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + BUILD_ERROR( 1 , o1.getStr() ); + + } + catch( Exception & e ) { + printf( "normal exception ! %s\n", e.Message ); + } + catch ( ... ) + { + printf( "any exception !!!!\n" ); + } + } +} + +void OSaxParserTest::testPerformance( const Reference < XParser > & rParser ) +{ + + Reference < XInputStream > rInStream = + createStreamFromFile( "testPerformance.xml" , m_rFactory ); + OUString sInput = OUString( RTL_CONSTASCII_USTRINGPARAM( "testperformance.xml") ); + + if( rParser.is() && rInStream.is() ) { + InputSource source; + + source.aInputStream = rInStream; + source.sSystemId = sInput; + + TestDocumentHandler *pDocHandler = new TestDocumentHandler( m_rFactory , sal_False ); + Reference < XDocumentHandler > rDocHandler( (XDocumentHandler *) pDocHandler , UNO_QUERY ); + Reference < XEntityResolver > rEntityResolver( (XEntityResolver *) pDocHandler , UNO_QUERY ); + Reference < XErrorHandler > rErrorHandler( ( XErrorHandler * )pDocHandler , UNO_QUERY ); + + rParser->setDocumentHandler( rDocHandler ); + rParser->setEntityResolver( rEntityResolver ); + rParser->setErrorHandler( rErrorHandler ); + + try + { + TimeValue aStartTime, aEndTime; + osl_getSystemTime( &aStartTime ); + rParser->parseStream( source ); + osl_getSystemTime( &aEndTime ); + + double fStart = (double)aStartTime.Seconds + ((double)aStartTime.Nanosec / 1000000000.0); + double fEnd = (double)aEndTime.Seconds + ((double)aEndTime.Nanosec / 1000000000.0); + + printf( "Performance reading : %g s\n" , fEnd - fStart ); + + } + catch( SAXParseException &e ) { + Any any; + any <<= e; + while(sal_True) { + if( any.getValueType() == getCppuType( &e ) ) { + SAXParseException ex; + any >>= ex; + OString o = OUStringToOString( ex.Message , RTL_TEXTENCODING_ASCII_US ); + printf( "%s\n" , o.getStr() ); + any <<= ex.WrappedException; + } + else { + break; + } + } + } + catch( SAXException &e ) { + OString o = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US ); + printf( "%s\n" , o.getStr() ); + + } + catch( ... ) + { + printf( "any exception !!!!\n" ); + } + } +} +} +using namespace sax_test; + +extern "C" +{ + + +void SAL_CALL component_getImplementationEnvironment( + const sal_Char ** ppEnvTypeName, uno_Environment ** ppEnv ) +{ + *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +} + + +sal_Bool SAL_CALL component_writeInfo( + void * pServiceManager, void * pRegistryKey ) +{ + if (pRegistryKey) + { + try + { + Reference< XRegistryKey > xKey( + reinterpret_cast< XRegistryKey * >( pRegistryKey ) ); + + OUString str = + OUString( RTL_CONSTASCII_USTRINGPARAM("/") ) + + OSaxParserTest_getImplementationName() + + OUString( RTL_CONSTASCII_USTRINGPARAM("/UNO/SERVICES") ); + Reference< XRegistryKey > xNewKey = xKey->createKey( str ); + xNewKey->createKey( OSaxParserTest_getServiceName() ); + + str = + OUString( RTL_CONSTASCII_USTRINGPARAM("/") ) + + OSaxWriterTest_getImplementationName() + + OUString( RTL_CONSTASCII_USTRINGPARAM("/UNO/SERVICES") ); + + xNewKey = xKey->createKey( str ); + xNewKey->createKey( OSaxWriterTest_getServiceName() ); + + return sal_True; + } + catch (InvalidRegistryException &) + { + OSL_ENSURE( sal_False, "### InvalidRegistryException!" ); + } + } + + return sal_False; +} + +void * SAL_CALL component_getFactory( + const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey ) +{ + void * pRet = 0; + + if (pServiceManager ) + { + Reference< XSingleServiceFactory > xRet; + Reference< XMultiServiceFactory > xSMgr = + reinterpret_cast< XMultiServiceFactory * > ( pServiceManager ); + + OUString aImplementationName = OUString::createFromAscii( pImplName ); + + + if (aImplementationName == OSaxWriterTest_getImplementationName() ) + { + xRet = createSingleFactory( xSMgr, aImplementationName, + OSaxWriterTest_CreateInstance, + OSaxWriterTest_getSupportedServiceNames() ); + } + else if (aImplementationName == OSaxParserTest_getImplementationName() ) + { + xRet = createSingleFactory( xSMgr, aImplementationName, + OSaxParserTest_CreateInstance, + OSaxParserTest_getSupportedServiceNames() ); + } + if (xRet.is()) + { + xRet->acquire(); + pRet = xRet.get(); + } + } + + return pRet; +} + +} + + diff --git a/sax/test/sax/testwriter.cxx b/sax/test/sax/testwriter.cxx new file mode 100644 index 000000000000..27c8559d51d4 --- /dev/null +++ b/sax/test/sax/testwriter.cxx @@ -0,0 +1,698 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include <vector> +#include <stdio.h> + +#include <com/sun/star/test/XSimpleTest.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> // for the multiservice-factories + +#include <com/sun/star/io/XActiveDataSource.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +#include <osl/time.h> + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase1.hxx> +#include <cppuhelper/implbase3.hxx> + + +using namespace ::std; +using namespace ::rtl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::test; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::xml::sax; + +#include "factory.hxx" + +namespace sax_test { + +class OFileWriter : + public WeakImplHelper1< XOutputStream > +{ +public: + OFileWriter( char *pcFile ) { strncpy( m_pcFile, pcFile, 256 - 1 ); m_f = 0; } + + +public: + virtual void SAL_CALL writeBytes(const Sequence< sal_Int8 >& aData) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException); + virtual void SAL_CALL flush(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException); + virtual void SAL_CALL closeOutput(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException); +private: + char m_pcFile[256]; + FILE *m_f; +}; + + +void OFileWriter::writeBytes(const Sequence< sal_Int8 >& aData) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException) +{ + if( ! m_f ) { + m_f = fopen( m_pcFile , "w" ); + } + + fwrite( aData.getConstArray() , 1 , aData.getLength() , m_f ); +} + + +void OFileWriter::flush(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException) +{ + fflush( m_f ); +} + +void OFileWriter::closeOutput(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException) +{ + fclose( m_f ); + m_f = 0; +} + + +class OSaxWriterTest : + public WeakImplHelper1< XSimpleTest > +{ +public: + OSaxWriterTest( const Reference < XMultiServiceFactory > & rFactory ) : m_rFactory( rFactory ) + { + + } + ~OSaxWriterTest() {} + + +public: + virtual void SAL_CALL testInvariant( + const OUString& TestName, + const Reference < XInterface >& TestObject) + throw ( IllegalArgumentException, + RuntimeException); + + virtual sal_Int32 SAL_CALL test( + const OUString& TestName, + const Reference < XInterface >& TestObject, + sal_Int32 hTestHandle) + throw ( IllegalArgumentException,RuntimeException); + + virtual sal_Bool SAL_CALL testPassed(void) + throw ( RuntimeException); + virtual Sequence< OUString > SAL_CALL getErrors(void) throw (RuntimeException); + virtual Sequence< Any > SAL_CALL getErrorExceptions(void) throw (RuntimeException); + virtual Sequence< OUString > SAL_CALL getWarnings(void) throw (RuntimeException); + +private: + void testSimple( const Reference< XExtendedDocumentHandler > &r ); + void testExceptions( const Reference< XExtendedDocumentHandler > &r ); + void testDTD( const Reference< XExtendedDocumentHandler > &r ); + void testPerformance( const Reference< XExtendedDocumentHandler > &r ); + void writeParagraph( const Reference< XExtendedDocumentHandler > &r , const OUString & s); + +private: + Sequence<Any> m_seqExceptions; + Sequence<OUString> m_seqErrors; + Sequence<OUString> m_seqWarnings; + Reference < XMultiServiceFactory > m_rFactory; + +}; + + + +/*---------------------------------------- +* +* Attributlist implementation +* +*----------------------------------------*/ +struct AttributeListImpl_impl; +class AttributeListImpl : public WeakImplHelper1< XAttributeList > +{ +public: + AttributeListImpl(); + AttributeListImpl( const AttributeListImpl & ); + ~AttributeListImpl(); + +public: + virtual sal_Int16 SAL_CALL getLength(void) throw (RuntimeException); + virtual OUString SAL_CALL getNameByIndex(sal_Int16 i) throw (RuntimeException); + virtual OUString SAL_CALL getTypeByIndex(sal_Int16 i) throw (RuntimeException); + virtual OUString SAL_CALL getTypeByName(const OUString& aName) throw (RuntimeException); + virtual OUString SAL_CALL getValueByIndex(sal_Int16 i) throw (RuntimeException); + virtual OUString SAL_CALL getValueByName(const OUString& aName) throw (RuntimeException); + +public: + void addAttribute( const OUString &sName , + const OUString &sType , + const OUString &sValue ); + void clear(); + +private: + struct AttributeListImpl_impl *m_pImpl; +}; + + +struct TagAttribute +{ + TagAttribute(){} + TagAttribute( const OUString &sName, + const OUString &sType , + const OUString &sValue ) + { + this->sName = sName; + this->sType = sType; + this->sValue = sValue; + } + + OUString sName; + OUString sType; + OUString sValue; +}; + +struct AttributeListImpl_impl +{ + AttributeListImpl_impl() + { + // performance improvement during adding + vecAttribute.reserve(20); + } + vector<struct TagAttribute> vecAttribute; +}; + + + +sal_Int16 AttributeListImpl::getLength(void) throw (RuntimeException) +{ + return m_pImpl->vecAttribute.size(); +} + + +AttributeListImpl::AttributeListImpl( const AttributeListImpl &r ) +{ + m_pImpl = new AttributeListImpl_impl; + *m_pImpl = *(r.m_pImpl); +} + +OUString AttributeListImpl::getNameByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( i < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sName; + } + return OUString(); +} + + +OUString AttributeListImpl::getTypeByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( i < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sType; + } + return OUString(); +} + +OUString AttributeListImpl::getValueByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( i < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sValue; + } + return OUString(); + +} + +OUString AttributeListImpl::getTypeByName( const OUString& sName ) throw (RuntimeException) +{ + vector<struct TagAttribute>::iterator ii = m_pImpl->vecAttribute.begin(); + + for( ; ii != m_pImpl->vecAttribute.end() ; ii ++ ) { + if( (*ii).sName == sName ) { + return (*ii).sType; + } + } + return OUString(); +} + +OUString AttributeListImpl::getValueByName(const OUString& sName) throw (RuntimeException) +{ + vector<struct TagAttribute>::iterator ii = m_pImpl->vecAttribute.begin(); + + for( ; ii != m_pImpl->vecAttribute.end() ; ii ++ ) { + if( (*ii).sName == sName ) { + return (*ii).sValue; + } + } + return OUString(); +} + + + +AttributeListImpl::AttributeListImpl() +{ + m_pImpl = new AttributeListImpl_impl; +} + + + +AttributeListImpl::~AttributeListImpl() +{ + delete m_pImpl; +} + + +void AttributeListImpl::addAttribute( const OUString &sName , + const OUString &sType , + const OUString &sValue ) +{ + m_pImpl->vecAttribute.push_back( TagAttribute( sName , sType , sValue ) ); +} + +void AttributeListImpl::clear() +{ + m_pImpl->vecAttribute.clear(); + +} + + + + + + + + + + + +/** +* for external binding +* +* +**/ +Reference < XInterface > SAL_CALL OSaxWriterTest_CreateInstance( const Reference < XMultiServiceFactory > & rSMgr ) throw (Exception) +{ + OSaxWriterTest *p = new OSaxWriterTest( rSMgr ); + Reference < XInterface > xService = *p; + return xService; +} + +OUString OSaxWriterTest_getServiceName( ) throw () +{ + return OUString( RTL_CONSTASCII_USTRINGPARAM("test.com.sun.star.xml.sax.Writer")); +} + +OUString OSaxWriterTest_getImplementationName( ) throw () +{ + return OUString( RTL_CONSTASCII_USTRINGPARAM("test.extensions.xml.sax.Writer")); +} + +Sequence<OUString> OSaxWriterTest_getSupportedServiceNames( ) throw () +{ + Sequence<OUString> aRet(1); + + aRet.getArray()[0] = OSaxWriterTest_getImplementationName( ); + + return aRet; +} + + + +void OSaxWriterTest::testInvariant( const OUString& TestName, + const Reference < XInterface >& TestObject ) + throw ( IllegalArgumentException, RuntimeException) +{ + if( OUString::createFromAscii("com.sun.star.xml.sax.Writer") == TestName ) { + Reference< XDocumentHandler > doc( TestObject , UNO_QUERY ); + Reference< XExtendedDocumentHandler > ext( TestObject , UNO_QUERY ); + Reference< XActiveDataSource > source( TestObject , UNO_QUERY ); + + ERROR_ASSERT( doc.is() , "XDocumentHandler cannot be queried" ); + ERROR_ASSERT( ext.is() , "XExtendedDocumentHandler cannot be queried" ); + ERROR_ASSERT( source.is() , "XActiveDataSource cannot be queried" ); + } + else { + BUILD_ERROR( 0 , "wrong test" ); + } +} + + +sal_Int32 OSaxWriterTest::test( + const OUString& TestName, + const Reference < XInterface >& TestObject, + sal_Int32 hTestHandle) + throw ( IllegalArgumentException,RuntimeException) +{ + if( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Writer")) == TestName ) + { + try + { + if( 0 == hTestHandle ) + { + testInvariant( TestName , TestObject ); + } + else + { + Reference< XExtendedDocumentHandler > writer( TestObject , UNO_QUERY ); + + if( 1 == hTestHandle ) { + testSimple( writer ); + } + else if( 2 == hTestHandle ) { + testExceptions( writer ); + } + else if( 3 == hTestHandle ) { + testDTD( writer ); + } + else if( 4 == hTestHandle ) { + testPerformance( writer ); + } + } + } + catch( Exception & e ) { + OString o = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US ); + BUILD_ERROR( 0 , o.getStr() ); + } + catch( ... ) + { + BUILD_ERROR( 0 , "unknown exception (Exception is not base class)" ); + } + + hTestHandle ++; + + if( hTestHandle >= 5) { + // all tests finished. + hTestHandle = -1; + } + } + else { + BUILD_ERROR( 0 , "service not supported by test." ); + } + return hTestHandle; +} + + + +sal_Bool OSaxWriterTest::testPassed(void) throw (RuntimeException) +{ + return m_seqErrors.getLength() == 0; +} + + +Sequence< OUString > OSaxWriterTest::getErrors(void) throw (RuntimeException) +{ + return m_seqErrors; +} + + +Sequence< Any > OSaxWriterTest::getErrorExceptions(void) throw (RuntimeException) +{ + return m_seqExceptions; +} + + +Sequence< OUString > OSaxWriterTest::getWarnings(void) throw (RuntimeException) +{ + return m_seqWarnings; +} + +void OSaxWriterTest::writeParagraph( + const Reference< XExtendedDocumentHandler > &r , + const OUString & s) +{ + int nMax = s.getLength(); + int nStart = 0; + + Sequence<sal_uInt16> seq( s.getLength() ); + memcpy( seq.getArray() , s.getStr() , s.getLength() * sizeof( sal_uInt16 ) ); + + for( int n = 1 ; n < nMax ; n++ ){ + if( 32 == seq.getArray()[n] ) { + r->allowLineBreak(); + r->characters( s.copy( nStart , n - nStart ) ); + nStart = n; + } + } + r->allowLineBreak(); + r->characters( s.copy( nStart , n - nStart ) ); +} + + + +void OSaxWriterTest::testSimple( const Reference< XExtendedDocumentHandler > &r ) +{ + OUString testParagraph = OUString( RTL_CONSTASCII_USTRINGPARAM( + "Dies ist ein bloeder Test um zu uberpruefen, ob der SAXWriter " + "wohl Zeilenumbrueche halbwegs richtig macht oder ob er die Zeile " + "bis zum bitteren Ende schreibt." )); + + OFileWriter *pw = new OFileWriter("output.xml"); + AttributeListImpl *pList = new AttributeListImpl; + + Reference< XAttributeList > rList( (XAttributeList *) pList , UNO_QUERY ); + Reference< XOutputStream > ref( ( XOutputStream * ) pw , UNO_QUERY ); + + Reference< XActiveDataSource > source( r , UNO_QUERY ); + + ERROR_ASSERT( ref.is() , "no output stream" ); + ERROR_ASSERT( source.is() , "no active data source" ); + + source->setOutputStream( ref ); + + r->startDocument(); + + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM("Arg1" )), + OUString( RTL_CONSTASCII_USTRINGPARAM("CDATA")) , + OUString( RTL_CONSTASCII_USTRINGPARAM("bla\n u")) ); + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM("Arg2")) , + OUString( RTL_CONSTASCII_USTRINGPARAM("CDATA")) , + OUString( RTL_CONSTASCII_USTRINGPARAM("blub")) ); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("tag1")) , rList ); + r->ignorableWhitespace( OUString() ); + + r->characters( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) ); + r->ignorableWhitespace( OUString() ); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("hi")) , rList ); + r->ignorableWhitespace( OUString() ); + + // the enpassant must be converted & -> & + r->characters( OUString( RTL_CONSTASCII_USTRINGPARAM("ü")) ); + + // Test added for mib. Tests if errors during conversions occurs + r->ignorableWhitespace( OUString() ); + sal_Char array[256]; + for( sal_Int32 n = 32 ; n < 254 ; n ++ ) { + array[n-32] = n; + } + array[254-32] = 0; + r->characters( + OStringToOUString( array , RTL_TEXTENCODING_SYMBOL ) + ); + r->ignorableWhitespace( OUString() ); + + // '>' must not be converted + r->startCDATA(); + r->characters( OUString( RTL_CONSTASCII_USTRINGPARAM(">fsfsdf<")) ); + r->endCDATA(); + r->ignorableWhitespace( OUString() ); + + writeParagraph( r , testParagraph ); + + + r->ignorableWhitespace( OUString() ); + r->comment( OUString( RTL_CONSTASCII_USTRINGPARAM("Dies ist ein Kommentar !")) ); + r->ignorableWhitespace( OUString() ); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("emptytagtest")) , rList ); + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("emptytagtest")) ); + + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("hi")) ); + r->ignorableWhitespace( OUString() ); + + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("tag1")) ); + r->endDocument(); + +} + +void OSaxWriterTest::testExceptions( const Reference< XExtendedDocumentHandler > & r ) +{ + + OFileWriter *pw = new OFileWriter("output2.xml"); + AttributeListImpl *pList = new AttributeListImpl; + + Reference< XAttributeList > rList( (XAttributeList *) pList , UNO_QUERY ); + Reference< XOutputStream > ref( ( XOutputStream * ) pw , UNO_QUERY ); + + Reference< XActiveDataSource > source( r , UNO_QUERY ); + + ERROR_ASSERT( ref.is() , "no output stream" ); + ERROR_ASSERT( source.is() , "no active data source" ); + + source->setOutputStream( ref ); + + { // startDocument must be called before start element + sal_Bool bException = sal_True; + try + { + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) , rList ); + bException = sal_False; + } + catch( SAXException &e ) + { + + } + ERROR_ASSERT( bException , "expected exception not thrown !" ); + } + + r->startDocument(); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) , rList ); + r->startCDATA(); + + { + sal_Bool bException = sal_True; + try{ + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) , rList ); + bException = sal_False; + } + catch( SAXException &e ) { + + } + ERROR_ASSERT( bException , "expected exception not thrown !" ); + } + + r->endCDATA(); + + { + sal_Unicode array[] = { 'a' , 'b' , 4 , 9 , 10 }; + OUString o( array , 5 ); + try + { + r->characters( o ); + ERROR_ASSERT( 0 , "Writer allowed to write forbidden characters" ); + } + catch( SAXException & e ) + { + + } + } + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) ); + + r->endDocument(); +} + + +void OSaxWriterTest::testDTD(const Reference< XExtendedDocumentHandler > &r ) +{ + OFileWriter *pw = new OFileWriter("outputDTD.xml"); + AttributeListImpl *pList = new AttributeListImpl; + + Reference< XAttributeList > rList( (XAttributeList *) pList , UNO_QUERY ); + Reference< XOutputStream > ref( ( XOutputStream * ) pw , UNO_QUERY ); + + Reference< XActiveDataSource > source( r , UNO_QUERY ); + + ERROR_ASSERT( ref.is() , "no output stream" ); + ERROR_ASSERT( source.is() , "no active data source" ); + + source->setOutputStream( ref ); + + + r->startDocument(); + r->unknown( OUString( RTL_CONSTASCII_USTRINGPARAM("<!DOCTYPE iCalendar >\n")) ); + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) , rList ); + + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) ); + r->endDocument(); +} + +void OSaxWriterTest::testPerformance(const Reference< XExtendedDocumentHandler > &r ) +{ + OFileWriter *pw = new OFileWriter("testPerformance.xml"); + AttributeListImpl *pList = new AttributeListImpl; + + OUString testParagraph = + OUString( RTL_CONSTASCII_USTRINGPARAM( + "Dies ist ein bloeder Test um zu uberpruefen, ob der SAXWriter " + "wohl Zeilenumbrueche halbwegs richtig macht oder ob er die Zeile " + "bis zum bitteren Ende schreibt." )); + + + Reference< XAttributeList > rList( (XAttributeList *) pList , UNO_QUERY ); + Reference< XOutputStream > ref( ( XOutputStream * ) pw , UNO_QUERY ); + + Reference< XActiveDataSource > source( r , UNO_QUERY ); + + ERROR_ASSERT( ref.is() , "no output stream" ); + ERROR_ASSERT( source.is() , "no active data source" ); + + source->setOutputStream( ref ); + + TimeValue aStartTime, aEndTime; + osl_getSystemTime( &aStartTime ); + + + r->startDocument(); + // just write a bunch of xml tags ! + // for performance testing + sal_Int32 i2; + OUString huhu( RTL_CONSTASCII_USTRINGPARAM("huhu") ); + OUString emptyString; + const int ITERATIONS = 125; + for( i2 = 0 ; i2 < ITERATIONS ; i2 ++ ) + { + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("tag") ) + + OUString::valueOf( i2 ), rList ); + for( sal_Int32 i = 0 ; i < 450 ; i ++ ) + { + r->ignorableWhitespace( emptyString ); + r->startElement( huhu , rList ); + r->characters( testParagraph ); + + r->ignorableWhitespace( emptyString ); + r->endElement( huhu ); + } + } + for( i2 = ITERATIONS-1 ; i2 >= 0 ; i2-- ) + { + r->ignorableWhitespace( emptyString ); + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("tag") ) + OUString::valueOf( i2 ) ); + } + + r->endDocument(); + + osl_getSystemTime( &aEndTime ); + + double fStart = (double)aStartTime.Seconds + ((double)aStartTime.Nanosec / 1000000000.0); + double fEnd = (double)aEndTime.Seconds + ((double)aEndTime.Nanosec / 1000000000.0); + + printf( "Performance writing : %g s\n" , fEnd - fStart ); +} +} diff --git a/sax/test/saxdemo.cxx b/sax/test/saxdemo.cxx new file mode 100644 index 000000000000..6023f09ac948 --- /dev/null +++ b/sax/test/saxdemo.cxx @@ -0,0 +1,651 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +//------------------------------------------------------ +// testcomponent - Loads a service and its testcomponent from dlls performs a test. +// Expands the dll-names depending on the actual environment. +// Example : testcomponent stardiv.uno.io.Pipe stm +// +// Therefor the testcode must exist in teststm and the testservice must be named test.stardiv.uno.io.Pipe +// + +#include <stdio.h> +#include <vector> + +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XActiveDataSource.hpp> + +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/implbase1.hxx> +#include <cppuhelper/implbase3.hxx> + +#include <vos/dynload.hxx> +#include <vos/diagnose.hxx> + +using namespace ::rtl; +using namespace ::std; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::io; + + +/************ + * Sequence of bytes -> InputStream + ************/ +class OInputStream : public WeakImplHelper1 < XInputStream > +{ +public: + OInputStream( const Sequence< sal_Int8 >&seq ) : + m_seq( seq ), + nPos( 0 ) + {} + +public: + virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) + throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) + { + nBytesToRead = (nBytesToRead > m_seq.getLength() - nPos ) ? + m_seq.getLength() - nPos : + nBytesToRead; + aData = Sequence< sal_Int8 > ( &(m_seq.getConstArray()[nPos]) , nBytesToRead ); + nPos += nBytesToRead; + return nBytesToRead; + } + virtual sal_Int32 SAL_CALL readSomeBytes( + ::com::sun::star::uno::Sequence< sal_Int8 >& aData, + sal_Int32 nMaxBytesToRead ) + throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) + { + return readBytes( aData, nMaxBytesToRead ); + } + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) + throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) + { + // not implemented + } + virtual sal_Int32 SAL_CALL available( ) + throw(NotConnectedException, IOException, RuntimeException) + { + return m_seq.getLength() - nPos; + } + virtual void SAL_CALL closeInput( ) + throw(NotConnectedException, IOException, RuntimeException) + { + // not needed + } + sal_Int32 nPos; + Sequence< sal_Int8> m_seq; +}; + +//------------------------------- +// Helper : create an input stream from a file +//------------------------------ +Reference< XInputStream > createStreamFromFile( + const char *pcFile ) +{ + FILE *f = fopen( pcFile , "rb" ); + Reference< XInputStream > r; + + if( f ) { + fseek( f , 0 , SEEK_END ); + int nLength = ftell( f ); + fseek( f , 0 , SEEK_SET ); + + Sequence<sal_Int8> seqIn(nLength); + fread( seqIn.getArray() , nLength , 1 , f ); + + r = Reference< XInputStream > ( new OInputStream( seqIn ) ); + fclose( f ); + } + return r; +} + +//----------------------------------------- +// The document handler, which is needed for the saxparser +// The Documenthandler for reading sax +//----------------------------------------- +class TestDocumentHandler : + public WeakImplHelper3< XExtendedDocumentHandler , XEntityResolver , XErrorHandler > +{ +public: + TestDocumentHandler( ) + { + } + +public: // Error handler + virtual void SAL_CALL error(const Any& aSAXParseException) throw (SAXException, RuntimeException) + { + printf( "Error !\n" ); + throw SAXException( + OUString( RTL_CONSTASCII_USTRINGPARAM("error from error handler")) , + Reference < XInterface >() , + aSAXParseException ); + } + virtual void SAL_CALL fatalError(const Any& aSAXParseException) throw (SAXException, RuntimeException) + { + printf( "Fatal Error !\n" ); + } + virtual void SAL_CALL warning(const Any& aSAXParseException) throw (SAXException, RuntimeException) + { + printf( "Warning !\n" ); + } + + +public: // ExtendedDocumentHandler + + virtual void SAL_CALL startDocument(void) throw (SAXException, RuntimeException) + { + m_iElementCount = 0; + m_iAttributeCount = 0; + m_iWhitespaceCount =0; + m_iCharCount=0; + printf( "document started\n" ); + } + virtual void SAL_CALL endDocument(void) throw (SAXException, RuntimeException) + { + printf( "document finished\n" ); + printf( "(ElementCount %d),(AttributeCount %d),(WhitespaceCount %d),(CharCount %d)\n", + m_iElementCount, m_iAttributeCount, m_iWhitespaceCount , m_iCharCount ); + + } + virtual void SAL_CALL startElement(const OUString& aName, + const Reference< XAttributeList > & xAttribs) + throw (SAXException,RuntimeException) + { + m_iElementCount ++; + m_iAttributeCount += xAttribs->getLength(); + } + + virtual void SAL_CALL endElement(const OUString& aName) throw (SAXException,RuntimeException) + { + // ignored + } + + virtual void SAL_CALL characters(const OUString& aChars) throw (SAXException,RuntimeException) + { + m_iCharCount += aChars.getLength(); + } + virtual void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) throw (SAXException,RuntimeException) + { + m_iWhitespaceCount += aWhitespaces.getLength(); + } + + virtual void SAL_CALL processingInstruction(const OUString& aTarget, const OUString& aData) throw (SAXException,RuntimeException) + { + // ignored + } + + virtual void SAL_CALL setDocumentLocator(const Reference< XLocator> & xLocator) + throw (SAXException,RuntimeException) + { + // ignored + } + + virtual InputSource SAL_CALL resolveEntity( + const OUString& sPublicId, + const OUString& sSystemId) + throw (SAXException,RuntimeException) + { + InputSource source; + source.sSystemId = sSystemId; + source.sPublicId = sPublicId; + + source.aInputStream = createStreamFromFile( + OUStringToOString( sSystemId , RTL_TEXTENCODING_ASCII_US) ); + + return source; + } + + virtual void SAL_CALL startCDATA(void) throw (SAXException,RuntimeException) + { + } + virtual void SAL_CALL endCDATA(void) throw (SAXException,RuntimeException) + { + } + virtual void SAL_CALL comment(const OUString& sComment) throw (SAXException,RuntimeException) + { + } + virtual void SAL_CALL unknown(const OUString& sString) throw (SAXException,RuntimeException) + { + } + + virtual void SAL_CALL allowLineBreak( void) throw (SAXException, RuntimeException ) + { + + } + +public: + int m_iElementCount; + int m_iAttributeCount; + int m_iWhitespaceCount; + int m_iCharCount; +}; + +//-------------------------------------- +// helper implementation for writing +// implements an XAttributeList +//------------------------------------- +struct AttributeListImpl_impl; +class AttributeListImpl : public WeakImplHelper1< XAttributeList > +{ +public: + AttributeListImpl(); + AttributeListImpl( const AttributeListImpl & ); + ~AttributeListImpl(); + +public: + virtual sal_Int16 SAL_CALL getLength(void) throw (RuntimeException); + virtual OUString SAL_CALL getNameByIndex(sal_Int16 i) throw (RuntimeException); + virtual OUString SAL_CALL getTypeByIndex(sal_Int16 i) throw (RuntimeException); + virtual OUString SAL_CALL getTypeByName(const OUString& aName) throw (RuntimeException); + virtual OUString SAL_CALL getValueByIndex(sal_Int16 i) throw (RuntimeException); + virtual OUString SAL_CALL getValueByName(const OUString& aName) throw (RuntimeException); + +public: + void addAttribute( const OUString &sName , + const OUString &sType , + const OUString &sValue ); + void clear(); + +private: + struct AttributeListImpl_impl *m_pImpl; +}; + + +struct TagAttribute +{ + TagAttribute(){} + TagAttribute( const OUString &sName, + const OUString &sType , + const OUString &sValue ) + { + this->sName = sName; + this->sType = sType; + this->sValue = sValue; + } + + OUString sName; + OUString sType; + OUString sValue; +}; + +struct AttributeListImpl_impl +{ + AttributeListImpl_impl() + { + // performance improvement during adding + vecAttribute.reserve(20); + } + vector<struct TagAttribute> vecAttribute; +}; + + + +sal_Int16 AttributeListImpl::getLength(void) throw (RuntimeException) +{ + return m_pImpl->vecAttribute.size(); +} + + +AttributeListImpl::AttributeListImpl( const AttributeListImpl &r ) +{ + m_pImpl = new AttributeListImpl_impl; + *m_pImpl = *(r.m_pImpl); +} + +OUString AttributeListImpl::getNameByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( i < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sName; + } + return OUString(); +} + + +OUString AttributeListImpl::getTypeByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( i < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sType; + } + return OUString(); +} + +OUString AttributeListImpl::getValueByIndex(sal_Int16 i) throw (RuntimeException) +{ + if( i < m_pImpl->vecAttribute.size() ) { + return m_pImpl->vecAttribute[i].sValue; + } + return OUString(); + +} + +OUString AttributeListImpl::getTypeByName( const OUString& sName ) throw (RuntimeException) +{ + vector<struct TagAttribute>::iterator ii = m_pImpl->vecAttribute.begin(); + + for( ; ii != m_pImpl->vecAttribute.end() ; ii ++ ) { + if( (*ii).sName == sName ) { + return (*ii).sType; + } + } + return OUString(); +} + +OUString AttributeListImpl::getValueByName(const OUString& sName) throw (RuntimeException) +{ + vector<struct TagAttribute>::iterator ii = m_pImpl->vecAttribute.begin(); + + for( ; ii != m_pImpl->vecAttribute.end() ; ii ++ ) { + if( (*ii).sName == sName ) { + return (*ii).sValue; + } + } + return OUString(); +} + + + +AttributeListImpl::AttributeListImpl() +{ + m_pImpl = new AttributeListImpl_impl; +} + + + +AttributeListImpl::~AttributeListImpl() +{ + delete m_pImpl; +} + + +void AttributeListImpl::addAttribute( const OUString &sName , + const OUString &sType , + const OUString &sValue ) +{ + m_pImpl->vecAttribute.push_back( TagAttribute( sName , sType , sValue ) ); +} + +void AttributeListImpl::clear() +{ + m_pImpl->vecAttribute.clear(); +} + + +//-------------------------------------- +// helper function for writing +// ensures that linebreaks are inserted +// when writing a long text. +// Note: this implementation may be a bit slow, +// but it shows, how the SAX-Writer handles the allowLineBreak calls. +//-------------------------------------- +void writeParagraphHelper( + const Reference< XExtendedDocumentHandler > &r , + const OUString & s) +{ + int nMax = s.getLength(); + int nStart = 0; + + Sequence<sal_uInt16> seq( s.getLength() ); + memcpy( seq.getArray() , s.getStr() , s.getLength() * sizeof( sal_uInt16 ) ); + + for( int n = 1 ; n < nMax ; n++ ){ + if( 32 == seq.getArray()[n] ) { + r->allowLineBreak(); + r->characters( s.copy( nStart , n - nStart ) ); + nStart = n; + } + } + r->allowLineBreak(); + r->characters( s.copy( nStart , n - nStart ) ); +} + + +//--------------------------------- +// helper implementation for SAX-Writer +// writes data to a file +//-------------------------------- +class OFileWriter : + public WeakImplHelper1< XOutputStream > +{ +public: + OFileWriter( char *pcFile ) { strncpy( m_pcFile , pcFile, 256 - 1 ); m_f = 0; } + + +public: + virtual void SAL_CALL writeBytes(const Sequence< sal_Int8 >& aData) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException); + virtual void SAL_CALL flush(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException); + virtual void SAL_CALL closeOutput(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException); +private: + char m_pcFile[256]; + FILE *m_f; +}; + + +void OFileWriter::writeBytes(const Sequence< sal_Int8 >& aData) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException) +{ + if( ! m_f ) { + m_f = fopen( m_pcFile , "w" ); + } + + fwrite( aData.getConstArray() , 1 , aData.getLength() , m_f ); +} + + +void OFileWriter::flush(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException) +{ + fflush( m_f ); +} + +void OFileWriter::closeOutput(void) + throw (NotConnectedException, BufferSizeExceededException, RuntimeException) +{ + fclose( m_f ); + m_f = 0; +} + + + +// Needed to switch on solaris threads +#ifdef SOLARIS +extern "C" void ChangeGlobalInit(); +#endif +int main (int argc, char **argv) +{ + + if( argc < 3) { + printf( "usage : saxdemo inputfile outputfile\n" ); + exit( 0 ); + } +#ifdef SOLARIS + // switch on threads in solaris + ChangeGlobalInit(); +#endif + + // create service manager + Reference< XMultiServiceFactory > xSMgr = createRegistryServiceFactory( + OUString( RTL_CONSTASCII_USTRINGPARAM( "applicat.rdb" )) ); + + Reference < XImplementationRegistration > xReg; + try + { + // Create registration service + Reference < XInterface > x = xSMgr->createInstance( + OUString::createFromAscii( "com.sun.star.registry.ImplementationRegistration" ) ); + xReg = Reference< XImplementationRegistration > ( x , UNO_QUERY ); + } + catch( Exception & ) { + printf( "Couldn't create ImplementationRegistration service\n" ); + exit(1); + } + + OString sTestName; + try + { + // Load dll for the tested component + OUString aDllName = + OUString::createFromAscii( "sax.uno" SAL_DLLEXTENSION ); + xReg->registerImplementation( + OUString::createFromAscii( "com.sun.star.loader.SharedLibrary" ), + aDllName, + Reference< XSimpleRegistry > () ); + } + catch( Exception &e ) { + printf( "Couldn't reach sax dll\n" ); + printf( "%s\n" , OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US ).getStr() ); + + exit(1); + } + + + //-------------------------------- + // parser demo + // read xml from a file and count elements + //-------------------------------- + Reference< XInterface > x = xSMgr->createInstance( + OUString::createFromAscii( "com.sun.star.xml.sax.Parser" ) ); + if( x.is() ) + { + Reference< XParser > rParser( x , UNO_QUERY ); + + // create and connect the document handler to the parser + TestDocumentHandler *pDocHandler = new TestDocumentHandler( ); + + Reference < XDocumentHandler > rDocHandler( (XDocumentHandler *) pDocHandler ); + Reference< XEntityResolver > rEntityResolver( (XEntityResolver *) pDocHandler ); + + rParser->setDocumentHandler( rDocHandler ); + rParser->setEntityResolver( rEntityResolver ); + + // create the input stream + InputSource source; + source.aInputStream = createStreamFromFile( argv[1] ); + source.sSystemId = OUString::createFromAscii( argv[1] ); + + try + { + // start parsing + rParser->parseStream( source ); + } + + catch( Exception & e ) + { + OString o1 = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8 ); + printf( "Exception during parsing : %s\n" , o1.getStr() ); + } + } + else + { + printf( "couln't create sax-parser component\n" ); + } + + + //---------------------- + // The SAX-Writer demo + //---------------------- + x= xSMgr->createInstance( OUString::createFromAscii( "com.sun.star.xml.sax.Writer" ) ); + if( x.is() ) + { + printf( "start writing to %s\n" , argv[2] ); + + OFileWriter *pw = new OFileWriter( argv[2] ); + Reference< XActiveDataSource > source( x , UNO_QUERY ); + source->setOutputStream( Reference< XOutputStream> ( (XOutputStream*) pw ) ); + + AttributeListImpl *pList = new AttributeListImpl; + Reference< XAttributeList > rList( (XAttributeList *) pList ); + + Reference< XExtendedDocumentHandler > r( x , UNO_QUERY ); + r->startDocument(); + + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM("Arg1" )), + OUString( RTL_CONSTASCII_USTRINGPARAM("CDATA")) , + OUString( RTL_CONSTASCII_USTRINGPARAM("foo\n u")) ); + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM("Arg2")) , + OUString( RTL_CONSTASCII_USTRINGPARAM("CDATA")) , + OUString( RTL_CONSTASCII_USTRINGPARAM("foo2")) ); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("tag1")) , rList ); + // tells the writer to insert a linefeed + r->ignorableWhitespace( OUString() ); + + r->characters( OUString( RTL_CONSTASCII_USTRINGPARAM("huhu")) ); + r->ignorableWhitespace( OUString() ); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("hi")) , rList ); + r->ignorableWhitespace( OUString() ); + + // the enpassant must be converted & -> & + r->characters( OUString( RTL_CONSTASCII_USTRINGPARAM("ü")) ); + r->ignorableWhitespace( OUString() ); + + // '>' must not be converted + r->startCDATA(); + r->characters( OUString( RTL_CONSTASCII_USTRINGPARAM(" > foo < ")) ); + r->endCDATA(); + r->ignorableWhitespace( OUString() ); + + OUString testParagraph = OUString( RTL_CONSTASCII_USTRINGPARAM( + "This is only a test to check, if the writer inserts line feeds " + "if needed or if the writer puts the whole text into one line." )); + writeParagraphHelper( r , testParagraph ); + + r->ignorableWhitespace( OUString() ); + r->comment( OUString( RTL_CONSTASCII_USTRINGPARAM("This is a comment !")) ); + r->ignorableWhitespace( OUString() ); + + r->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM("emptytagtest")) , rList ); + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("emptytagtest")) ); + r->ignorableWhitespace( OUString() ); + + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("hi")) ); + r->ignorableWhitespace( OUString() ); + + r->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM("tag1")) ); + r->endDocument(); + + printf( "finished writing\n" ); + } + else + { + printf( "couln't create sax-writer component\n" ); + } +} diff --git a/sax/test/testcomponent.cxx b/sax/test/testcomponent.cxx new file mode 100644 index 000000000000..f22366092b67 --- /dev/null +++ b/sax/test/testcomponent.cxx @@ -0,0 +1,230 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +//------------------------------------------------------ +// testcomponent - Loads a service and its testcomponent from dlls performs a test. +// Expands the dll-names depending on the actual environment. +// Example : testcomponent stardiv.uno.io.Pipe stm +// +// Therefor the testcode must exist in teststm and the testservice must be named test.stardiv.uno.io.Pipe +// + +#include <stdio.h> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <com/sun/star/test/XSimpleTest.hpp> + +#include <cppuhelper/servicefactory.hxx> + +#include <vos/dynload.hxx> +#include <vos/diagnose.hxx> + +using namespace ::rtl; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::test; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::registry; + +// Needed to switch on solaris threads +#ifdef SOLARIS +extern "C" void ChangeGlobalInit(); +#endif + +int main (int argc, char **argv) +{ + + if( argc < 3) { + printf( "usage : testcomponent service dll [additional dlls]\n" ); + exit( 0 ); + } +#ifdef SOLARIS + // switch on threads in solaris + ChangeGlobalInit(); +#endif + + // create service manager + Reference< XMultiServiceFactory > xSMgr = + createRegistryServiceFactory( OUString( RTL_CONSTASCII_USTRINGPARAM("applicat.rdb")) ); + + Reference < XImplementationRegistration > xReg; + Reference < XSimpleRegistry > xSimpleReg; + + try + { + // Create registration service + Reference < XInterface > x = xSMgr->createInstance( + OUString::createFromAscii( "com.sun.star.registry.ImplementationRegistration" ) ); + xReg = Reference< XImplementationRegistration > ( x , UNO_QUERY ); + } + catch( Exception & ) { + printf( "Couldn't create ImplementationRegistration service\n" ); + exit(1); + } + + sal_Char szBuf[1024]; + OString sTestName; + + try + { + // Load dll for the tested component + for( int n = 2 ; n <argc ; n ++ ) { +#ifdef SAL_W32 + OUString aDllName = OStringToOUString( argv[n] , RTL_TEXTENCODING_ASCII_US ); +#else + OUString aDllName = OUString( RTL_CONSTASCII_USTRINGPARAM("lib")); + aDllName += OStringToOUString( argv[n] , RTL_TEXTENCODING_ASCII_US ); + aDllName += OUString( RTL_CONSTASCII_USTRINGPARAM(".so")); +#endif + xReg->registerImplementation( + OUString::createFromAscii( "com.sun.star.loader.SharedLibrary" ), + aDllName, + xSimpleReg ); + } + } + catch( Exception &e ) { + printf( "Couldn't reach dll %s\n" , szBuf ); + printf( "%s\n" , OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US ).getStr() ); + + exit(1); + } + + + try + { + // Load dll for the test component + sTestName = "test"; + sTestName += argv[2]; + +#ifdef SAL_W32 + OUString aDllName = OStringToOUString( sTestName , RTL_TEXTENCODING_ASCII_US ); +#else + OUString aDllName = OUString( RTL_CONSTASCII_USTRINGPARAM("lib")); + aDllName += OStringToOUString( sTestName , RTL_TEXTENCODING_ASCII_US ); + aDllName += OUString( RTL_CONSTASCII_USTRINGPARAM(".so")); +#endif + + xReg->registerImplementation( + OUString::createFromAscii( "com.sun.star.loader.SharedLibrary" ) , + aDllName, + xSimpleReg ); + } + catch( Exception & e ) + { + printf( "Couldn't reach dll %s\n" , szBuf ); + exit(1); + } + + + // Instantiate test service + sTestName = "test."; + sTestName += argv[1]; + + Reference < XInterface > xIntTest = + xSMgr->createInstance( OStringToOUString( sTestName , RTL_TEXTENCODING_ASCII_US ) ); + Reference< XSimpleTest > xTest( xIntTest , UNO_QUERY ); + + if( ! xTest.is() ) { + printf( "Couldn't instantiate test service \n" ); + exit( 1 ); + } + + + sal_Int32 nHandle = 0; + sal_Int32 nNewHandle; + sal_Int32 nErrorCount = 0; + sal_Int32 nWarningCount = 0; + + // loop until all test are performed + while( nHandle != -1 ) + { + // Instantiate serivce + Reference< XInterface > x = + xSMgr->createInstance( OStringToOUString( argv[1] , RTL_TEXTENCODING_ASCII_US ) ); + if( ! x.is() ) + { + printf( "Couldn't instantiate service !\n" ); + exit( 1 ); + } + + // do the test + try + { + nNewHandle = xTest->test( + OStringToOUString( argv[1] , RTL_TEXTENCODING_ASCII_US ) , x , nHandle ); + } + catch( Exception & e ) { + OString o = OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ); + printf( "testcomponent : uncaught exception %s\n" , o.getStr() ); + exit(1); + } + catch( ... ) + { + printf( "testcomponent : uncaught unknown exception\n" ); + exit(1); + } + + + // print errors and warning + Sequence<OUString> seqErrors = xTest->getErrors(); + Sequence<OUString> seqWarnings = xTest->getWarnings(); + if( seqWarnings.getLength() > nWarningCount ) + { + printf( "Warnings during test %d!\n" , nHandle ); + for( ; nWarningCount < seqWarnings.getLength() ; nWarningCount ++ ) + { + OString o = OUStringToOString( + seqWarnings.getArray()[nWarningCount], RTL_TEXTENCODING_ASCII_US ); + printf( "Warning\n%s\n---------\n" , o.getStr() ); + } + } + + + if( seqErrors.getLength() > nErrorCount ) { + printf( "Errors during test %d!\n" , nHandle ); + for( ; nErrorCount < seqErrors.getLength() ; nErrorCount ++ ) { + OString o = OUStringToOString( + seqErrors.getArray()[nErrorCount], RTL_TEXTENCODING_ASCII_US ); + printf( "%s\n" , o.getStr() ); + } + } + + nHandle = nNewHandle; + } + + if( xTest->testPassed() ) { + printf( "Test passed !\n" ); + } + else { + printf( "Test failed !\n" ); + } + + Reference <XComponent > rComp( xSMgr , UNO_QUERY ); + rComp->dispose(); + return 0; +} diff --git a/sax/util/makefile.mk b/sax/util/makefile.mk new file mode 100644 index 000000000000..5e2f378603e9 --- /dev/null +++ b/sax/util/makefile.mk @@ -0,0 +1,68 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=.. + +PRJNAME=sax +TARGET=sax + +USE_DEFFILE=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# ------------------------------------------------------------------ + +LIB1TARGET= $(SLB)$/$(TARGET).lib +LIB1FILES=\ + $(SLB)$/saxtools.lib + +# sax + +SHL1TARGET= $(TARGET)$(DLLPOSTFIX) +SHL1IMPLIB= i$(TARGET) +SHL1USE_EXPORTS=name + +SHL1LIBS= $(LIB1TARGET) +SHL1STDLIBS= \ + $(VOSLIB) \ + $(CPPULIB) \ + $(CPPUHELPERLIB)\ + $(COMPHELPERLIB)\ + $(RTLLIB) \ + $(SALLIB) \ + $(ONELIB) \ + $(SALHELPERLIB) + +SHL1DEF= $(MISC)$/$(SHL1TARGET).def +DEF1NAME= $(SHL1TARGET) +DEFLIB1NAME=$(TARGET) + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/sax/util/makefile.pmk b/sax/util/makefile.pmk new file mode 100644 index 000000000000..1c044bb10ff0 --- /dev/null +++ b/sax/util/makefile.pmk @@ -0,0 +1,30 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +# Reduction of exported symbols: +CDEFS += -DSAX_DLLIMPLEMENTATION +VISIBILITY_HIDDEN=TRUE |