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"
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
;
51 // ============================================================================
55 class Locator
: public ::cppu::WeakImplHelper1
< XLocator
>
58 inline explicit Locator( RecordParser
* pParser
) : mpParser( pParser
) {}
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
);
71 RecordParser
* mpParser
;
74 // ----------------------------------------------------------------------------
76 void Locator::dispose()
81 void Locator::checkDispose() throw( RuntimeException
)
84 throw DisposedException();
87 sal_Int32 SAL_CALL
Locator::getColumnNumber() throw( RuntimeException
)
92 sal_Int32 SAL_CALL
Locator::getLineNumber() throw( RuntimeException
)
97 OUString SAL_CALL
Locator::getPublicId() throw( RuntimeException
)
100 return mpParser
->getInputSource().maPublicId
;
103 OUString SAL_CALL
Locator::getSystemId() throw( RuntimeException
)
106 return mpParser
->getInputSource().maSystemId
;
109 // ============================================================================
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
);
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
);
178 // ============================================================================
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
)
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;
207 bool lclReadRecordHeader( sal_Int32
& ornRecId
, sal_Int32
& ornRecSize
, BinaryInputStream
& rStrm
)
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
);
220 orData
.realloc( nRecSize
);
221 bValid
= (nRecSize
== 0) || (rStrm
.readData( orData
, nRecSize
) == nRecSize
);
228 // ============================================================================
230 RecordParser::RecordParser()
232 mxLocator
.set( new prv::Locator( this ) );
235 RecordParser::~RecordParser()
238 mxLocator
->dispose();
241 void RecordParser::setFragmentHandler( const ::rtl::Reference
< FragmentHandler
>& rxHandler
)
243 mxHandler
= rxHandler
;
245 // build record infos
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() )
264 if( !mxHandler
.is() )
265 throw SAXException();
267 // start the document
268 Reference
< XLocator
> xLocator( mxLocator
.get() );
269 mxHandler
->setDocumentLocator( xLocator
);
270 mxHandler
->startDocument();
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
295 xCurrContext
->startRecord( nRecId
, aRecStrm
);
296 xCurrContext
->endRecord( nRecId
);
298 mxStack
->popContext();
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() )
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
);
315 mxStack
->pushContext( *pStartRecInfo
, xCurrContext
);
317 if( xCurrContext
.is() )
321 xCurrContext
->startRecord( nRecId
, aRecStrm
);
322 // end simple records (context records are finished in ContextStack::popContext)
324 xCurrContext
->endRecord( nRecId
);
328 // close remaining contexts (missing context end records or stream error)
329 while( !mxStack
->empty() )
330 mxStack
->popContext();
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 // ============================================================================