update dev300-m58
[ooovba.git] / oox / source / core / recordparser.cxx
blob2d24ed40cc91dc9335830e822dbe52d6cd3248db
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: recordparser.cxx,v $
10 * $Revision: 1.4.20.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "oox/core/recordparser.hxx"
32 #include <vector>
33 #include <com/sun/star/lang/DisposedException.hpp>
34 #include <com/sun/star/xml/sax/XLocator.hpp>
35 #include <cppuhelper/implbase1.hxx>
36 #include "oox/helper/recordinputstream.hxx"
37 #include "oox/core/fragmenthandler.hxx"
39 using ::rtl::OUString;
40 using ::com::sun::star::uno::Reference;
41 using ::com::sun::star::uno::RuntimeException;
42 using ::com::sun::star::lang::DisposedException;
43 using ::com::sun::star::io::XInputStream;
44 using ::com::sun::star::io::IOException;
45 using ::com::sun::star::xml::sax::SAXException;
46 using ::com::sun::star::xml::sax::XLocator;
48 namespace oox {
49 namespace core {
51 // ============================================================================
53 namespace prv {
55 class Locator : public ::cppu::WeakImplHelper1< XLocator >
57 public:
58 inline explicit Locator( RecordParser* pParser ) : mpParser( pParser ) {}
60 void dispose();
61 void checkDispose() throw( RuntimeException );
63 // com.sun.star.sax.XLocator interface
65 virtual sal_Int32 SAL_CALL getColumnNumber() throw( RuntimeException );
66 virtual sal_Int32 SAL_CALL getLineNumber() throw( RuntimeException );
67 virtual OUString SAL_CALL getPublicId() throw( RuntimeException );
68 virtual OUString SAL_CALL getSystemId() throw( RuntimeException );
70 private:
71 RecordParser* mpParser;
74 // ----------------------------------------------------------------------------
76 void Locator::dispose()
78 mpParser = 0;
81 void Locator::checkDispose() throw( RuntimeException )
83 if( !mpParser )
84 throw DisposedException();
87 sal_Int32 SAL_CALL Locator::getColumnNumber() throw( RuntimeException )
89 return -1;
92 sal_Int32 SAL_CALL Locator::getLineNumber() throw( RuntimeException )
94 return -1;
97 OUString SAL_CALL Locator::getPublicId() throw( RuntimeException )
99 checkDispose();
100 return mpParser->getInputSource().maPublicId;
103 OUString SAL_CALL Locator::getSystemId() throw( RuntimeException )
105 checkDispose();
106 return mpParser->getInputSource().maSystemId;
109 // ============================================================================
111 class ContextStack
113 public:
114 explicit ContextStack( FragmentHandlerRef xHandler );
116 inline bool empty() const { return maStack.empty(); }
118 sal_Int32 getCurrentRecId() const;
119 bool hasCurrentEndRecId() const;
120 ContextHandlerRef getCurrentContext() const;
122 void pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext );
123 void popContext();
125 private:
126 typedef ::std::pair< RecordInfo, ContextHandlerRef > ContextInfo;
127 typedef ::std::vector< ContextInfo > ContextInfoVec;
129 FragmentHandlerRef mxHandler;
130 ContextInfoVec maStack;
133 // ----------------------------------------------------------------------------
135 ContextStack::ContextStack( FragmentHandlerRef xHandler ) :
136 mxHandler( xHandler )
140 sal_Int32 ContextStack::getCurrentRecId() const
142 return maStack.empty() ? -1 : maStack.back().first.mnStartRecId;
145 bool ContextStack::hasCurrentEndRecId() const
147 return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0);
150 ContextHandlerRef ContextStack::getCurrentContext() const
152 if( !maStack.empty() )
153 return maStack.back().second;
154 return mxHandler.get();
157 void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext )
159 OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(),
160 "ContextStack::pushContext - nested incomplete context record identifiers" );
161 maStack.push_back( ContextInfo( rRecInfo, rxContext ) );
164 void ContextStack::popContext()
166 OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" );
167 if( !maStack.empty() )
169 ContextInfo& rContextInfo = maStack.back();
170 if( rContextInfo.second.is() )
171 rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId );
172 maStack.pop_back();
176 } // namespace prv
178 // ============================================================================
180 namespace {
182 /** Reads a byte from the passed stream, returns true on success. */
183 inline bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm )
185 return rStrm.readMemory( &ornByte, 1 ) == 1;
188 /** Reads a compressed signed 32-bit integer from the passed stream. */
189 bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm )
191 ornValue = 0;
192 sal_uInt8 nByte;
193 if( !lclReadByte( nByte, rStrm ) ) return false;
194 ornValue = nByte & 0x7F;
195 if( (nByte & 0x80) == 0 ) return true;
196 if( !lclReadByte( nByte, rStrm ) ) return false;
197 ornValue |= sal_Int32( nByte & 0x7F ) << 7;
198 if( (nByte & 0x80) == 0 ) return true;
199 if( !lclReadByte( nByte, rStrm ) ) return false;
200 ornValue |= sal_Int32( nByte & 0x7F ) << 14;
201 if( (nByte & 0x80) == 0 ) return true;
202 if( !lclReadByte( nByte, rStrm ) ) return false;
203 ornValue |= sal_Int32( nByte & 0x7F ) << 21;
204 return true;
207 bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm )
209 return
210 lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) &&
211 lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0);
214 bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm )
216 sal_Int32 nRecSize = 0;
217 bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm );
218 if( bValid )
220 orData.realloc( nRecSize );
221 bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize);
223 return bValid;
226 } // namespace
228 // ============================================================================
230 RecordParser::RecordParser()
232 mxLocator.set( new prv::Locator( this ) );
235 RecordParser::~RecordParser()
237 if( mxLocator.is() )
238 mxLocator->dispose();
241 void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler )
243 mxHandler = rxHandler;
245 // build record infos
246 maStartMap.clear();
247 maEndMap.clear();
248 const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : 0;
249 OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" );
250 for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs )
252 maStartMap[ pRecs->mnStartRecId ] = *pRecs;
253 if( pRecs->mnEndRecId >= 0 )
254 maEndMap[ pRecs->mnEndRecId ] = *pRecs;
258 void RecordParser::parseStream( const RecordInputSource& rInputSource ) throw( SAXException, IOException, RuntimeException )
260 maSource = rInputSource;
262 if( !maSource.mxInStream || maSource.mxInStream->isEof() )
263 throw IOException();
264 if( !mxHandler.is() )
265 throw SAXException();
267 // start the document
268 Reference< XLocator > xLocator( mxLocator.get() );
269 mxHandler->setDocumentLocator( xLocator );
270 mxHandler->startDocument();
272 // parse the stream
273 mxStack.reset( new prv::ContextStack( mxHandler ) );
274 sal_Int32 nRecId = 0;
275 StreamDataSequence aRecData;
276 while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) )
278 // create record stream object from imported record data
279 RecordInputStream aRecStrm( aRecData );
280 // try to leave a context, there may be other incomplete contexts on the stack
281 if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) )
283 (void)pEndRecInfo; // shut warning up in non-debug
284 // finalize contexts without record identifier for context end
285 while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() )
286 mxStack->popContext();
287 // finalize the current context and pop context info from stack
288 OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" );
289 (void)pEndRecInfo; // suppress compiler warning for unused variable
290 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
291 if( xCurrContext.is() )
293 // context end record may contain some data, handle it as simple record
294 aRecStrm.seek( 0 );
295 xCurrContext->startRecord( nRecId, aRecStrm );
296 xCurrContext->endRecord( nRecId );
298 mxStack->popContext();
300 else
302 // end context with incomplete record id, if the same id comes again
303 if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() )
304 mxStack->popContext();
305 // try to start a new context
306 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
307 if( xCurrContext.is() )
309 aRecStrm.seek( 0 );
310 xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm );
312 // track all context identifiers on the stack (do not push simple records)
313 const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId );
314 if( pStartRecInfo )
315 mxStack->pushContext( *pStartRecInfo, xCurrContext );
316 // import the record
317 if( xCurrContext.is() )
319 // import the record
320 aRecStrm.seek( 0 );
321 xCurrContext->startRecord( nRecId, aRecStrm );
322 // end simple records (context records are finished in ContextStack::popContext)
323 if( !pStartRecInfo )
324 xCurrContext->endRecord( nRecId );
328 // close remaining contexts (missing context end records or stream error)
329 while( !mxStack->empty() )
330 mxStack->popContext();
331 mxStack.reset();
333 // finish document
334 mxHandler->endDocument();
336 maSource = RecordInputSource();
339 const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const
341 RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId );
342 return (aIt == maStartMap.end()) ? 0 : &aIt->second;
345 const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const
347 RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId );
348 return (aIt == maEndMap.end()) ? 0 : &aIt->second;
351 // ============================================================================
353 } // namespace core
354 } // namespace oox