LibreOffice Module oox (master) 1
recordparser.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21
22#include <utility>
23#include <vector>
24#include <com/sun/star/io/IOException.hpp>
25#include <com/sun/star/lang/DisposedException.hpp>
26#include <com/sun/star/xml/sax/SAXException.hpp>
27#include <com/sun/star/xml/sax/XLocator.hpp>
29#include <osl/diagnose.h>
31
32namespace oox::core {
33
34using namespace ::com::sun::star::io;
35using namespace ::com::sun::star::lang;
36using namespace ::com::sun::star::uno;
37using namespace ::com::sun::star::xml::sax;
38
39namespace prv {
40
41class Locator : public ::cppu::WeakImplHelper< XLocator >
42{
43public:
44 explicit Locator( RecordParser* pParser ) : mpParser( pParser ) {}
45
46 void dispose();
48 void checkDispose();
49
50 // com.sun.star.sax.XLocator interface
51
52 virtual sal_Int32 SAL_CALL getColumnNumber() override;
53 virtual sal_Int32 SAL_CALL getLineNumber() override;
54 virtual OUString SAL_CALL getPublicId() override;
55 virtual OUString SAL_CALL getSystemId() override;
56
57private:
59};
60
62{
63 mpParser = nullptr;
64}
65
67{
68 if( !mpParser )
69 throw DisposedException();
70}
71
72sal_Int32 SAL_CALL Locator::getColumnNumber()
73{
74 return -1;
75}
76
77sal_Int32 SAL_CALL Locator::getLineNumber()
78{
79 return -1;
80}
81
82OUString SAL_CALL Locator::getPublicId()
83{
85 return OUString();
86}
87
88OUString SAL_CALL Locator::getSystemId()
89{
92}
93
95{
96public:
97 explicit ContextStack( FragmentHandlerRef xHandler );
98
99 bool empty() const { return maStack.empty(); }
100
101 sal_Int32 getCurrentRecId() const;
102 bool hasCurrentEndRecId() const;
104
105 void pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext );
106 void popContext();
107
108private:
109 typedef ::std::pair< RecordInfo, ContextHandlerRef > ContextInfo;
110 typedef ::std::vector< ContextInfo > ContextInfoVec;
111
114};
115
117 mxHandler(std::move( xHandler ))
118{
119}
120
122{
123 return maStack.empty() ? -1 : maStack.back().first.mnStartRecId;
124}
125
127{
128 return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0);
129}
130
132{
133 if( !maStack.empty() )
134 return maStack.back().second;
135 return mxHandler;
136}
137
138void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext )
139{
140 OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(),
141 "ContextStack::pushContext - nested incomplete context record identifiers" );
142 maStack.emplace_back( rRecInfo, rxContext );
143}
144
146{
147 OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" );
148 if( !maStack.empty() )
149 {
150 ContextInfo& rContextInfo = maStack.back();
151 if( rContextInfo.second.is() )
152 rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId );
153 maStack.pop_back();
154 }
155}
156
157} // namespace oox::core::prv
158
159namespace {
160
162bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm )
163{
164 return rStrm.readMemory( &ornByte, 1 ) == 1;
165}
166
168bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm )
169{
170 ornValue = 0;
171 sal_uInt8 nByte;
172 if( !lclReadByte( nByte, rStrm ) ) return false;
173 ornValue = nByte & 0x7F;
174 if( (nByte & 0x80) == 0 ) return true;
175 if( !lclReadByte( nByte, rStrm ) ) return false;
176 ornValue |= sal_Int32( nByte & 0x7F ) << 7;
177 if( (nByte & 0x80) == 0 ) return true;
178 if( !lclReadByte( nByte, rStrm ) ) return false;
179 ornValue |= sal_Int32( nByte & 0x7F ) << 14;
180 if( (nByte & 0x80) == 0 ) return true;
181 if( !lclReadByte( nByte, rStrm ) ) return false;
182 ornValue |= sal_Int32( nByte & 0x7F ) << 21;
183 return true;
184}
185
186bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm )
187{
188 return
189 lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) &&
190 lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0);
191}
192
193bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm )
194{
195 sal_Int32 nRecSize = 0;
196 bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm );
197 if( bValid )
198 {
199 orData.realloc( nRecSize );
200 bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize);
201 }
202 return bValid;
203}
204
205} // namespace
206
208{
209 mxLocator.set( new prv::Locator( this ) );
210}
211
213{
214 if( mxLocator.is() )
215 mxLocator->dispose();
216}
217
218void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler )
219{
220 mxHandler = rxHandler;
221
222 // build record infos
223 maStartMap.clear();
224 maEndMap.clear();
225 const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : nullptr;
226 OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" );
227 for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs )
228 {
229 maStartMap[ pRecs->mnStartRecId ] = *pRecs;
230 if( pRecs->mnEndRecId >= 0 )
231 maEndMap[ pRecs->mnEndRecId ] = *pRecs;
232 }
233}
234
236{
237 maSource = rInputSource;
238
239 if( !maSource.mxInStream || maSource.mxInStream->isEof() )
240 throw IOException();
241 if( !mxHandler.is() )
242 throw SAXException();
243
244 // start the document
245 mxHandler->setDocumentLocator( mxLocator );
246 mxHandler->startDocument();
247
248 // parse the stream
249 mxStack.reset( new prv::ContextStack( mxHandler ) );
250 sal_Int32 nRecId = 0;
251 StreamDataSequence aRecData;
252 while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) )
253 {
254 // create record stream object from imported record data
255 SequenceInputStream aRecStrm( aRecData );
256 // try to leave a context, there may be other incomplete contexts on the stack
257 if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) )
258 {
259 // finalize contexts without record identifier for context end
260 while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() )
261 mxStack->popContext();
262 // finalize the current context and pop context info from stack
263 OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" );
264 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
265 if( xCurrContext.is() )
266 {
267 // context end record may contain some data, handle it as simple record
268 aRecStrm.seekToStart();
269 xCurrContext->startRecord( nRecId, aRecStrm );
270 xCurrContext->endRecord( nRecId );
271 }
272 mxStack->popContext();
273 }
274 else
275 {
276 // end context with incomplete record id, if the same id comes again
277 if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() )
278 mxStack->popContext();
279 // try to start a new context
280 ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
281 if( xCurrContext.is() )
282 {
283 aRecStrm.seekToStart();
284 xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm );
285 }
286 // track all context identifiers on the stack (do not push simple records)
287 const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId );
288 if( pStartRecInfo )
289 mxStack->pushContext( *pStartRecInfo, xCurrContext );
290 // import the record
291 if( xCurrContext.is() )
292 {
293 // import the record
294 aRecStrm.seekToStart();
295 xCurrContext->startRecord( nRecId, aRecStrm );
296 // end simple records (context records are finished in ContextStack::popContext)
297 if( !pStartRecInfo )
298 xCurrContext->endRecord( nRecId );
299 }
300 }
301 }
302 // close remaining contexts (missing context end records or stream error)
303 while( !mxStack->empty() )
304 mxStack->popContext();
305 mxStack.reset();
306
307 // finish document
308 mxHandler->endDocument();
309
311}
312
313const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const
314{
315 RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId );
316 return (aIt == maStartMap.end()) ? nullptr : &aIt->second;
317}
318
319const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const
320{
321 RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId );
322 return (aIt == maEndMap.end()) ? nullptr : &aIt->second;
323}
324
325} // namespace oox::core
326
327/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Interface for binary input stream classes.
void seekToStart()
Seeks the stream to the beginning, if stream is seekable.
Wraps a StreamDataSequence and provides convenient access functions.
::rtl::Reference< FragmentHandler > mxHandler
void setFragmentHandler(const ::rtl::Reference< FragmentHandler > &rxHandler)
const RecordInfo * getEndRecordInfo(sal_Int32 nRecId) const
Returns a RecordInfo struct that contains the passed record identifier as context end identifier.
const RecordInfo * getStartRecordInfo(sal_Int32 nRecId) const
Returns a RecordInfo struct that contains the passed record identifier as context start identifier.
::std::unique_ptr< prv::ContextStack > mxStack
RecordInfoMap maStartMap
RecordInputSource maSource
::rtl::Reference< prv::Locator > mxLocator
const RecordInputSource & getInputSource() const
void parseStream(const RecordInputSource &rInputSource)
void pushContext(const RecordInfo &rRec, const ContextHandlerRef &rxContext)
::std::pair< RecordInfo, ContextHandlerRef > ContextInfo
FragmentHandlerRef mxHandler
::std::vector< ContextInfo > ContextInfoVec
sal_Int32 getCurrentRecId() const
ContextStack(FragmentHandlerRef xHandler)
ContextHandlerRef getCurrentContext() const
virtual sal_Int32 SAL_CALL getLineNumber() override
virtual OUString SAL_CALL getSystemId() override
RecordParser * mpParser
virtual sal_Int32 SAL_CALL getColumnNumber() override
virtual OUString SAL_CALL getPublicId() override
Locator(RecordParser *pParser)
void SvStream & rStrm
css::uno::Sequence< sal_Int8 > StreamDataSequence
Describes record identifiers used to create contexts in a binary stream.
sal_Int32 mnEndRecId
Record identifier for context end, -1 = no record.
sal_Int32 mnStartRecId
Record identifier for context start.
BinaryInputStreamRef mxInStream
unsigned char sal_uInt8
rtl::Reference< FragmentHandler > mxHandler