bump product version to 4.2.0.1
[LibreOffice.git] / oox / source / core / recordparser.cxx
blob0f302b840bec1fdc8f22cd20e83c3740ca98a548
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 "oox/core/fragmenthandler.hxx"
28 namespace oox {
29 namespace core {
31 // ============================================================================
33 using namespace ::com::sun::star::io;
34 using namespace ::com::sun::star::lang;
35 using namespace ::com::sun::star::uno;
36 using namespace ::com::sun::star::xml::sax;
39 // ============================================================================
41 namespace prv {
43 class Locator : public ::cppu::WeakImplHelper1< XLocator >
45 public:
46 inline explicit Locator( RecordParser* pParser ) : mpParser( pParser ) {}
48 void dispose();
49 void checkDispose() throw( RuntimeException );
51 // com.sun.star.sax.XLocator interface
53 virtual sal_Int32 SAL_CALL getColumnNumber() throw( RuntimeException );
54 virtual sal_Int32 SAL_CALL getLineNumber() throw( RuntimeException );
55 virtual OUString SAL_CALL getPublicId() throw( RuntimeException );
56 virtual OUString SAL_CALL getSystemId() throw( RuntimeException );
58 private:
59 RecordParser* mpParser;
62 // ----------------------------------------------------------------------------
64 void Locator::dispose()
66 mpParser = 0;
69 void Locator::checkDispose() throw( RuntimeException )
71 if( !mpParser )
72 throw DisposedException();
75 sal_Int32 SAL_CALL Locator::getColumnNumber() throw( RuntimeException )
77 return -1;
80 sal_Int32 SAL_CALL Locator::getLineNumber() throw( RuntimeException )
82 return -1;
85 OUString SAL_CALL Locator::getPublicId() throw( RuntimeException )
87 checkDispose();
88 return mpParser->getInputSource().maPublicId;
91 OUString SAL_CALL Locator::getSystemId() throw( RuntimeException )
93 checkDispose();
94 return mpParser->getInputSource().maSystemId;
97 // ============================================================================
99 class ContextStack
101 public:
102 explicit ContextStack( FragmentHandlerRef xHandler );
104 inline bool empty() const { return maStack.empty(); }
106 sal_Int32 getCurrentRecId() const;
107 bool hasCurrentEndRecId() const;
108 ContextHandlerRef getCurrentContext() const;
110 void pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext );
111 void popContext();
113 private:
114 typedef ::std::pair< RecordInfo, ContextHandlerRef > ContextInfo;
115 typedef ::std::vector< ContextInfo > ContextInfoVec;
117 FragmentHandlerRef mxHandler;
118 ContextInfoVec maStack;
121 // ----------------------------------------------------------------------------
123 ContextStack::ContextStack( FragmentHandlerRef xHandler ) :
124 mxHandler( xHandler )
128 sal_Int32 ContextStack::getCurrentRecId() const
130 return maStack.empty() ? -1 : maStack.back().first.mnStartRecId;
133 bool ContextStack::hasCurrentEndRecId() const
135 return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0);
138 ContextHandlerRef ContextStack::getCurrentContext() const
140 if( !maStack.empty() )
141 return maStack.back().second;
142 return mxHandler.get();
145 void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext )
147 OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(),
148 "ContextStack::pushContext - nested incomplete context record identifiers" );
149 maStack.push_back( ContextInfo( rRecInfo, rxContext ) );
152 void ContextStack::popContext()
154 OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" );
155 if( !maStack.empty() )
157 ContextInfo& rContextInfo = maStack.back();
158 if( rContextInfo.second.is() )
159 rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId );
160 maStack.pop_back();
164 } // namespace prv
166 // ============================================================================
168 namespace {
170 /** Reads a byte from the passed stream, returns true on success. */
171 inline bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm )
173 return rStrm.readMemory( &ornByte, 1 ) == 1;
176 /** Reads a compressed signed 32-bit integer from the passed stream. */
177 bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm )
179 ornValue = 0;
180 sal_uInt8 nByte;
181 if( !lclReadByte( nByte, rStrm ) ) return false;
182 ornValue = nByte & 0x7F;
183 if( (nByte & 0x80) == 0 ) return true;
184 if( !lclReadByte( nByte, rStrm ) ) return false;
185 ornValue |= sal_Int32( nByte & 0x7F ) << 7;
186 if( (nByte & 0x80) == 0 ) return true;
187 if( !lclReadByte( nByte, rStrm ) ) return false;
188 ornValue |= sal_Int32( nByte & 0x7F ) << 14;
189 if( (nByte & 0x80) == 0 ) return true;
190 if( !lclReadByte( nByte, rStrm ) ) return false;
191 ornValue |= sal_Int32( nByte & 0x7F ) << 21;
192 return true;
195 bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm )
197 return
198 lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) &&
199 lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0);
202 bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm )
204 sal_Int32 nRecSize = 0;
205 bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm );
206 if( bValid )
208 orData.realloc( nRecSize );
209 bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize);
211 return bValid;
214 } // namespace
216 // ============================================================================
218 RecordParser::RecordParser()
220 mxLocator.set( new prv::Locator( this ) );
223 RecordParser::~RecordParser()
225 if( mxLocator.is() )
226 mxLocator->dispose();
229 void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler )
231 mxHandler = rxHandler;
233 // build record infos
234 maStartMap.clear();
235 maEndMap.clear();
236 const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : 0;
237 OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" );
238 for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs )
240 maStartMap[ pRecs->mnStartRecId ] = *pRecs;
241 if( pRecs->mnEndRecId >= 0 )
242 maEndMap[ pRecs->mnEndRecId ] = *pRecs;
246 void RecordParser::parseStream( const RecordInputSource& rInputSource ) throw( SAXException, IOException, RuntimeException )
248 maSource = rInputSource;
250 if( !maSource.mxInStream || maSource.mxInStream->isEof() )
251 throw IOException();
252 if( !mxHandler.is() )
253 throw SAXException();
255 // start the document
256 Reference< XLocator > xLocator( mxLocator.get() );
257 mxHandler->setDocumentLocator( xLocator );
258 mxHandler->startDocument();
260 // parse the stream
261 mxStack.reset( new prv::ContextStack( mxHandler ) );
262 sal_Int32 nRecId = 0;
263 StreamDataSequence aRecData;
264 while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) )
266 // create record stream object from imported record data
267 SequenceInputStream aRecStrm( aRecData );
268 // try to leave a context, there may be other incomplete contexts on the stack
269 if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) )
271 // finalize contexts without record identifier for context end
272 while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() )
273 mxStack->popContext();
274 // finalize the current context and pop context info from stack
275 OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" );
276 (void)pEndRecInfo; // suppress compiler warning for unused variable
277 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
278 if( xCurrContext.is() )
280 // context end record may contain some data, handle it as simple record
281 aRecStrm.seekToStart();
282 xCurrContext->startRecord( nRecId, aRecStrm );
283 xCurrContext->endRecord( nRecId );
285 mxStack->popContext();
287 else
289 // end context with incomplete record id, if the same id comes again
290 if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() )
291 mxStack->popContext();
292 // try to start a new context
293 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
294 if( xCurrContext.is() )
296 aRecStrm.seekToStart();
297 xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm );
299 // track all context identifiers on the stack (do not push simple records)
300 const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId );
301 if( pStartRecInfo )
302 mxStack->pushContext( *pStartRecInfo, xCurrContext );
303 // import the record
304 if( xCurrContext.is() )
306 // import the record
307 aRecStrm.seekToStart();
308 xCurrContext->startRecord( nRecId, aRecStrm );
309 // end simple records (context records are finished in ContextStack::popContext)
310 if( !pStartRecInfo )
311 xCurrContext->endRecord( nRecId );
315 // close remaining contexts (missing context end records or stream error)
316 while( !mxStack->empty() )
317 mxStack->popContext();
318 mxStack.reset();
320 // finish document
321 mxHandler->endDocument();
323 maSource = RecordInputSource();
326 const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const
328 RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId );
329 return (aIt == maStartMap.end()) ? 0 : &aIt->second;
332 const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const
334 RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId );
335 return (aIt == maEndMap.end()) ? 0 : &aIt->second;
338 // ============================================================================
340 } // namespace core
341 } // namespace oox
343 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */