1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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"
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
;
39 class Locator
: public ::cppu::WeakImplHelper1
< XLocator
>
42 inline explicit Locator( RecordParser
* pParser
) : mpParser( pParser
) {}
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
;
55 RecordParser
* mpParser
;
58 void Locator::dispose()
63 void Locator::checkDispose() throw( RuntimeException
)
66 throw DisposedException();
69 sal_Int32 SAL_CALL
Locator::getColumnNumber() throw( RuntimeException
, std::exception
)
74 sal_Int32 SAL_CALL
Locator::getLineNumber() throw( RuntimeException
, std::exception
)
79 OUString SAL_CALL
Locator::getPublicId() throw( RuntimeException
, std::exception
)
82 return mpParser
->getInputSource().maPublicId
;
85 OUString SAL_CALL
Locator::getSystemId() throw( RuntimeException
, std::exception
)
88 return mpParser
->getInputSource().maSystemId
;
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
);
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
);
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
)
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;
183 bool lclReadRecordHeader( sal_Int32
& ornRecId
, sal_Int32
& ornRecSize
, BinaryInputStream
& rStrm
)
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
);
196 orData
.realloc( nRecSize
);
197 bValid
= (nRecSize
== 0) || (rStrm
.readData( orData
, nRecSize
) == nRecSize
);
204 RecordParser::RecordParser()
206 mxLocator
.set( new prv::Locator( this ) );
209 RecordParser::~RecordParser()
212 mxLocator
->dispose();
215 void RecordParser::setFragmentHandler( const ::rtl::Reference
< FragmentHandler
>& rxHandler
)
217 mxHandler
= rxHandler
;
219 // build record infos
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() )
238 if( !mxHandler
.is() )
239 throw SAXException();
241 // start the document
242 Reference
< XLocator
> xLocator( mxLocator
.get() );
243 mxHandler
->setDocumentLocator( xLocator
);
244 mxHandler
->startDocument();
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();
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
);
288 mxStack
->pushContext( *pStartRecInfo
, xCurrContext
);
290 if( xCurrContext
.is() )
293 aRecStrm
.seekToStart();
294 xCurrContext
->startRecord( nRecId
, aRecStrm
);
295 // end simple records (context records are finished in ContextStack::popContext)
297 xCurrContext
->endRecord( nRecId
);
301 // close remaining contexts (missing context end records or stream error)
302 while( !mxStack
->empty() )
303 mxStack
->popContext();
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
;
327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */