diff options
Diffstat (limited to 'oox/source/core/recordparser.cxx')
-rw-r--r-- | oox/source/core/recordparser.cxx | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/oox/source/core/recordparser.cxx b/oox/source/core/recordparser.cxx new file mode 100644 index 000000000000..b1749ae7be07 --- /dev/null +++ b/oox/source/core/recordparser.cxx @@ -0,0 +1,352 @@ +/************************************************************************* + * + * 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 "oox/core/recordparser.hxx" +#include <vector> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/xml/sax/XLocator.hpp> +#include <cppuhelper/implbase1.hxx> +#include "oox/helper/recordinputstream.hxx" +#include "oox/core/fragmenthandler.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::lang::DisposedException; +using ::com::sun::star::io::XInputStream; +using ::com::sun::star::io::IOException; +using ::com::sun::star::xml::sax::SAXException; +using ::com::sun::star::xml::sax::XLocator; + +namespace oox { +namespace core { + +// ============================================================================ + +namespace prv { + +class Locator : public ::cppu::WeakImplHelper1< XLocator > +{ +public: + inline explicit Locator( RecordParser* pParser ) : mpParser( pParser ) {} + + void dispose(); + void checkDispose() throw( RuntimeException ); + + // com.sun.star.sax.XLocator interface + + virtual sal_Int32 SAL_CALL getColumnNumber() throw( RuntimeException ); + virtual sal_Int32 SAL_CALL getLineNumber() throw( RuntimeException ); + virtual OUString SAL_CALL getPublicId() throw( RuntimeException ); + virtual OUString SAL_CALL getSystemId() throw( RuntimeException ); + +private: + RecordParser* mpParser; +}; + +// ---------------------------------------------------------------------------- + +void Locator::dispose() +{ + mpParser = 0; +} + +void Locator::checkDispose() throw( RuntimeException ) +{ + if( !mpParser ) + throw DisposedException(); +} + +sal_Int32 SAL_CALL Locator::getColumnNumber() throw( RuntimeException ) +{ + return -1; +} + +sal_Int32 SAL_CALL Locator::getLineNumber() throw( RuntimeException ) +{ + return -1; +} + +OUString SAL_CALL Locator::getPublicId() throw( RuntimeException ) +{ + checkDispose(); + return mpParser->getInputSource().maPublicId; +} + +OUString SAL_CALL Locator::getSystemId() throw( RuntimeException ) +{ + checkDispose(); + return mpParser->getInputSource().maSystemId; +} + +// ============================================================================ + +class ContextStack +{ +public: + explicit ContextStack( FragmentHandlerRef xHandler ); + + inline bool empty() const { return maStack.empty(); } + + sal_Int32 getCurrentRecId() const; + bool hasCurrentEndRecId() const; + ContextHandlerRef getCurrentContext() const; + + void pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext ); + void popContext(); + +private: + typedef ::std::pair< RecordInfo, ContextHandlerRef > ContextInfo; + typedef ::std::vector< ContextInfo > ContextInfoVec; + + FragmentHandlerRef mxHandler; + ContextInfoVec maStack; +}; + +// ---------------------------------------------------------------------------- + +ContextStack::ContextStack( FragmentHandlerRef xHandler ) : + mxHandler( xHandler ) +{ +} + +sal_Int32 ContextStack::getCurrentRecId() const +{ + return maStack.empty() ? -1 : maStack.back().first.mnStartRecId; +} + +bool ContextStack::hasCurrentEndRecId() const +{ + return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0); +} + +ContextHandlerRef ContextStack::getCurrentContext() const +{ + if( !maStack.empty() ) + return maStack.back().second; + return mxHandler.get(); +} + +void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext ) +{ + OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(), + "ContextStack::pushContext - nested incomplete context record identifiers" ); + maStack.push_back( ContextInfo( rRecInfo, rxContext ) ); +} + +void ContextStack::popContext() +{ + OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" ); + if( !maStack.empty() ) + { + ContextInfo& rContextInfo = maStack.back(); + if( rContextInfo.second.is() ) + rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId ); + maStack.pop_back(); + } +} + +} // namespace prv + +// ============================================================================ + +namespace { + +/** Reads a byte from the passed stream, returns true on success. */ +inline bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm ) +{ + return rStrm.readMemory( &ornByte, 1 ) == 1; +} + +/** Reads a compressed signed 32-bit integer from the passed stream. */ +bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm ) +{ + ornValue = 0; + sal_uInt8 nByte; + if( !lclReadByte( nByte, rStrm ) ) return false; + ornValue = nByte & 0x7F; + if( (nByte & 0x80) == 0 ) return true; + if( !lclReadByte( nByte, rStrm ) ) return false; + ornValue |= sal_Int32( nByte & 0x7F ) << 7; + if( (nByte & 0x80) == 0 ) return true; + if( !lclReadByte( nByte, rStrm ) ) return false; + ornValue |= sal_Int32( nByte & 0x7F ) << 14; + if( (nByte & 0x80) == 0 ) return true; + if( !lclReadByte( nByte, rStrm ) ) return false; + ornValue |= sal_Int32( nByte & 0x7F ) << 21; + return true; +} + +bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm ) +{ + return + lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) && + lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0); +} + +bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm ) +{ + sal_Int32 nRecSize = 0; + bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm ); + if( bValid ) + { + orData.realloc( nRecSize ); + bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize); + } + return bValid; +} + +} // namespace + +// ============================================================================ + +RecordParser::RecordParser() +{ + mxLocator.set( new prv::Locator( this ) ); +} + +RecordParser::~RecordParser() +{ + if( mxLocator.is() ) + mxLocator->dispose(); +} + +void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler ) +{ + mxHandler = rxHandler; + + // build record infos + maStartMap.clear(); + maEndMap.clear(); + const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : 0; + OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" ); + for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs ) + { + maStartMap[ pRecs->mnStartRecId ] = *pRecs; + if( pRecs->mnEndRecId >= 0 ) + maEndMap[ pRecs->mnEndRecId ] = *pRecs; + } +} + +void RecordParser::parseStream( const RecordInputSource& rInputSource ) throw( SAXException, IOException, RuntimeException ) +{ + maSource = rInputSource; + + if( !maSource.mxInStream || maSource.mxInStream->isEof() ) + throw IOException(); + if( !mxHandler.is() ) + throw SAXException(); + + // start the document + Reference< XLocator > xLocator( mxLocator.get() ); + mxHandler->setDocumentLocator( xLocator ); + mxHandler->startDocument(); + + // parse the stream + mxStack.reset( new prv::ContextStack( mxHandler ) ); + sal_Int32 nRecId = 0; + StreamDataSequence aRecData; + while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) ) + { + // create record stream object from imported record data + RecordInputStream aRecStrm( aRecData ); + // try to leave a context, there may be other incomplete contexts on the stack + if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) ) + { + (void)pEndRecInfo; // shut warning up in non-debug + // finalize contexts without record identifier for context end + while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() ) + mxStack->popContext(); + // finalize the current context and pop context info from stack + OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" ); + (void)pEndRecInfo; // suppress compiler warning for unused variable + ContextHandlerRef xCurrContext = mxStack->getCurrentContext(); + if( xCurrContext.is() ) + { + // context end record may contain some data, handle it as simple record + aRecStrm.seek( 0 ); + xCurrContext->startRecord( nRecId, aRecStrm ); + xCurrContext->endRecord( nRecId ); + } + mxStack->popContext(); + } + else + { + // end context with incomplete record id, if the same id comes again + if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() ) + mxStack->popContext(); + // try to start a new context + ContextHandlerRef xCurrContext = mxStack->getCurrentContext(); + if( xCurrContext.is() ) + { + aRecStrm.seek( 0 ); + xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm ); + } + // track all context identifiers on the stack (do not push simple records) + const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId ); + if( pStartRecInfo ) + mxStack->pushContext( *pStartRecInfo, xCurrContext ); + // import the record + if( xCurrContext.is() ) + { + // import the record + aRecStrm.seek( 0 ); + xCurrContext->startRecord( nRecId, aRecStrm ); + // end simple records (context records are finished in ContextStack::popContext) + if( !pStartRecInfo ) + xCurrContext->endRecord( nRecId ); + } + } + } + // close remaining contexts (missing context end records or stream error) + while( !mxStack->empty() ) + mxStack->popContext(); + mxStack.reset(); + + // finish document + mxHandler->endDocument(); + + maSource = RecordInputSource(); +} + +const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const +{ + RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId ); + return (aIt == maStartMap.end()) ? 0 : &aIt->second; +} + +const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const +{ + RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId ); + return (aIt == maEndMap.end()) ? 0 : &aIt->second; +} + +// ============================================================================ + +} // namespace core +} // namespace oox + |