Branch libreoffice-5-0-4
[LibreOffice.git] / oox / source / core / recordparser.cxx
blob708632b237b927de2e9d59788853b67a660a9052
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "oox/core/recordparser.hxx"
22 #include <vector>
23 #include <com/sun/star/lang/DisposedException.hpp>
24 #include <com/sun/star/xml/sax/XLocator.hpp>
25 #include <cppuhelper/implbase1.hxx>
26 #include <osl/diagnose.h>
27 #include "oox/core/fragmenthandler.hxx"
29 namespace oox {
30 namespace core {
32 using namespace ::com::sun::star::io;
33 using namespace ::com::sun::star::lang;
34 using namespace ::com::sun::star::uno;
35 using namespace ::com::sun::star::xml::sax;
37 namespace prv {
39 class Locator : public ::cppu::WeakImplHelper1< XLocator >
41 public:
42 inline explicit Locator( RecordParser* pParser ) : mpParser( pParser ) {}
44 void dispose();
45 void checkDispose() throw( RuntimeException );
47 // com.sun.star.sax.XLocator interface
49 virtual sal_Int32 SAL_CALL getColumnNumber() throw( RuntimeException, std::exception ) SAL_OVERRIDE;
50 virtual sal_Int32 SAL_CALL getLineNumber() throw( RuntimeException, std::exception ) SAL_OVERRIDE;
51 virtual OUString SAL_CALL getPublicId() throw( RuntimeException, std::exception ) SAL_OVERRIDE;
52 virtual OUString SAL_CALL getSystemId() throw( RuntimeException, std::exception ) SAL_OVERRIDE;
54 private:
55 RecordParser* mpParser;
58 void Locator::dispose()
60 mpParser = 0;
63 void Locator::checkDispose() throw( RuntimeException )
65 if( !mpParser )
66 throw DisposedException();
69 sal_Int32 SAL_CALL Locator::getColumnNumber() throw( RuntimeException, std::exception )
71 return -1;
74 sal_Int32 SAL_CALL Locator::getLineNumber() throw( RuntimeException, std::exception )
76 return -1;
79 OUString SAL_CALL Locator::getPublicId() throw( RuntimeException, std::exception )
81 checkDispose();
82 return mpParser->getInputSource().maPublicId;
85 OUString SAL_CALL Locator::getSystemId() throw( RuntimeException, std::exception )
87 checkDispose();
88 return mpParser->getInputSource().maSystemId;
91 class ContextStack
93 public:
94 explicit ContextStack( FragmentHandlerRef xHandler );
96 inline bool empty() const { return maStack.empty(); }
98 sal_Int32 getCurrentRecId() const;
99 bool hasCurrentEndRecId() const;
100 ContextHandlerRef getCurrentContext() const;
102 void pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext );
103 void popContext();
105 private:
106 typedef ::std::pair< RecordInfo, ContextHandlerRef > ContextInfo;
107 typedef ::std::vector< ContextInfo > ContextInfoVec;
109 FragmentHandlerRef mxHandler;
110 ContextInfoVec maStack;
113 ContextStack::ContextStack( FragmentHandlerRef xHandler ) :
114 mxHandler( xHandler )
118 sal_Int32 ContextStack::getCurrentRecId() const
120 return maStack.empty() ? -1 : maStack.back().first.mnStartRecId;
123 bool ContextStack::hasCurrentEndRecId() const
125 return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0);
128 ContextHandlerRef ContextStack::getCurrentContext() const
130 if( !maStack.empty() )
131 return maStack.back().second;
132 return mxHandler.get();
135 void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext )
137 OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(),
138 "ContextStack::pushContext - nested incomplete context record identifiers" );
139 maStack.push_back( ContextInfo( rRecInfo, rxContext ) );
142 void ContextStack::popContext()
144 OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" );
145 if( !maStack.empty() )
147 ContextInfo& rContextInfo = maStack.back();
148 if( rContextInfo.second.is() )
149 rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId );
150 maStack.pop_back();
154 } // namespace prv
156 namespace {
158 /** Reads a byte from the passed stream, returns true on success. */
159 inline bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm )
161 return rStrm.readMemory( &ornByte, 1 ) == 1;
164 /** Reads a compressed signed 32-bit integer from the passed stream. */
165 bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm )
167 ornValue = 0;
168 sal_uInt8 nByte;
169 if( !lclReadByte( nByte, rStrm ) ) return false;
170 ornValue = nByte & 0x7F;
171 if( (nByte & 0x80) == 0 ) return true;
172 if( !lclReadByte( nByte, rStrm ) ) return false;
173 ornValue |= sal_Int32( nByte & 0x7F ) << 7;
174 if( (nByte & 0x80) == 0 ) return true;
175 if( !lclReadByte( nByte, rStrm ) ) return false;
176 ornValue |= sal_Int32( nByte & 0x7F ) << 14;
177 if( (nByte & 0x80) == 0 ) return true;
178 if( !lclReadByte( nByte, rStrm ) ) return false;
179 ornValue |= sal_Int32( nByte & 0x7F ) << 21;
180 return true;
183 bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm )
185 return
186 lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) &&
187 lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0);
190 bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm )
192 sal_Int32 nRecSize = 0;
193 bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm );
194 if( bValid )
196 orData.realloc( nRecSize );
197 bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize);
199 return bValid;
202 } // namespace
204 RecordParser::RecordParser()
206 mxLocator.set( new prv::Locator( this ) );
209 RecordParser::~RecordParser()
211 if( mxLocator.is() )
212 mxLocator->dispose();
215 void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler )
217 mxHandler = rxHandler;
219 // build record infos
220 maStartMap.clear();
221 maEndMap.clear();
222 const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : 0;
223 OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" );
224 for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs )
226 maStartMap[ pRecs->mnStartRecId ] = *pRecs;
227 if( pRecs->mnEndRecId >= 0 )
228 maEndMap[ pRecs->mnEndRecId ] = *pRecs;
232 void RecordParser::parseStream( const RecordInputSource& rInputSource ) throw( SAXException, IOException, RuntimeException )
234 maSource = rInputSource;
236 if( !maSource.mxInStream || maSource.mxInStream->isEof() )
237 throw IOException();
238 if( !mxHandler.is() )
239 throw SAXException();
241 // start the document
242 Reference< XLocator > xLocator( mxLocator.get() );
243 mxHandler->setDocumentLocator( xLocator );
244 mxHandler->startDocument();
246 // parse the stream
247 mxStack.reset( new prv::ContextStack( mxHandler ) );
248 sal_Int32 nRecId = 0;
249 StreamDataSequence aRecData;
250 while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) )
252 // create record stream object from imported record data
253 SequenceInputStream aRecStrm( aRecData );
254 // try to leave a context, there may be other incomplete contexts on the stack
255 if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) )
257 // finalize contexts without record identifier for context end
258 while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() )
259 mxStack->popContext();
260 // finalize the current context and pop context info from stack
261 OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" );
262 (void)pEndRecInfo; // suppress compiler warning for unused variable
263 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
264 if( xCurrContext.is() )
266 // context end record may contain some data, handle it as simple record
267 aRecStrm.seekToStart();
268 xCurrContext->startRecord( nRecId, aRecStrm );
269 xCurrContext->endRecord( nRecId );
271 mxStack->popContext();
273 else
275 // end context with incomplete record id, if the same id comes again
276 if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() )
277 mxStack->popContext();
278 // try to start a new context
279 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
280 if( xCurrContext.is() )
282 aRecStrm.seekToStart();
283 xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm );
285 // track all context identifiers on the stack (do not push simple records)
286 const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId );
287 if( pStartRecInfo )
288 mxStack->pushContext( *pStartRecInfo, xCurrContext );
289 // import the record
290 if( xCurrContext.is() )
292 // import the record
293 aRecStrm.seekToStart();
294 xCurrContext->startRecord( nRecId, aRecStrm );
295 // end simple records (context records are finished in ContextStack::popContext)
296 if( !pStartRecInfo )
297 xCurrContext->endRecord( nRecId );
301 // close remaining contexts (missing context end records or stream error)
302 while( !mxStack->empty() )
303 mxStack->popContext();
304 mxStack.reset();
306 // finish document
307 mxHandler->endDocument();
309 maSource = RecordInputSource();
312 const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const
314 RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId );
315 return (aIt == maStartMap.end()) ? 0 : &aIt->second;
318 const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const
320 RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId );
321 return (aIt == maEndMap.end()) ? 0 : &aIt->second;
324 } // namespace core
325 } // namespace oox
327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */