xref: /trunk/main/oox/source/core/recordparser.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "oox/core/recordparser.hxx"
29 
30 #include <vector>
31 #include <com/sun/star/lang/DisposedException.hpp>
32 #include <com/sun/star/xml/sax/XLocator.hpp>
33 #include <cppuhelper/implbase1.hxx>
34 #include "oox/core/fragmenthandler.hxx"
35 
36 namespace oox {
37 namespace core {
38 
39 // ============================================================================
40 
41 using namespace ::com::sun::star::io;
42 using namespace ::com::sun::star::lang;
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::xml::sax;
45 
46 using ::rtl::OUString;
47 
48 // ============================================================================
49 
50 namespace prv {
51 
52 class Locator : public ::cppu::WeakImplHelper1< XLocator >
53 {
54 public:
55     inline explicit         Locator( RecordParser* pParser ) : mpParser( pParser ) {}
56 
57     void                    dispose();
58     void                    checkDispose() throw( RuntimeException );
59 
60     // com.sun.star.sax.XLocator interface
61 
62     virtual sal_Int32 SAL_CALL getColumnNumber() throw( RuntimeException );
63     virtual sal_Int32 SAL_CALL getLineNumber() throw( RuntimeException );
64     virtual OUString SAL_CALL getPublicId() throw( RuntimeException );
65     virtual OUString SAL_CALL getSystemId() throw( RuntimeException );
66 
67 private:
68     RecordParser*           mpParser;
69 };
70 
71 // ----------------------------------------------------------------------------
72 
73 void Locator::dispose()
74 {
75     mpParser = 0;
76 }
77 
78 void Locator::checkDispose() throw( RuntimeException )
79 {
80     if( !mpParser )
81         throw DisposedException();
82 }
83 
84 sal_Int32 SAL_CALL Locator::getColumnNumber() throw( RuntimeException )
85 {
86     return -1;
87 }
88 
89 sal_Int32 SAL_CALL Locator::getLineNumber() throw( RuntimeException )
90 {
91     return -1;
92 }
93 
94 OUString SAL_CALL Locator::getPublicId() throw( RuntimeException )
95 {
96     checkDispose();
97     return mpParser->getInputSource().maPublicId;
98 }
99 
100 OUString SAL_CALL Locator::getSystemId() throw( RuntimeException )
101 {
102     checkDispose();
103     return mpParser->getInputSource().maSystemId;
104 }
105 
106 // ============================================================================
107 
108 class ContextStack
109 {
110 public:
111     explicit            ContextStack( FragmentHandlerRef xHandler );
112 
113     inline bool         empty() const { return maStack.empty(); }
114 
115     sal_Int32           getCurrentRecId() const;
116     bool                hasCurrentEndRecId() const;
117     ContextHandlerRef   getCurrentContext() const;
118 
119     void                pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext );
120     void                popContext();
121 
122 private:
123     typedef ::std::pair< RecordInfo, ContextHandlerRef >    ContextInfo;
124     typedef ::std::vector< ContextInfo >                    ContextInfoVec;
125 
126     FragmentHandlerRef  mxHandler;
127     ContextInfoVec      maStack;
128 };
129 
130 // ----------------------------------------------------------------------------
131 
132 ContextStack::ContextStack( FragmentHandlerRef xHandler ) :
133     mxHandler( xHandler )
134 {
135 }
136 
137 sal_Int32 ContextStack::getCurrentRecId() const
138 {
139     return maStack.empty() ? -1 : maStack.back().first.mnStartRecId;
140 }
141 
142 bool ContextStack::hasCurrentEndRecId() const
143 {
144     return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0);
145 }
146 
147 ContextHandlerRef ContextStack::getCurrentContext() const
148 {
149     if( !maStack.empty() )
150         return maStack.back().second;
151     return mxHandler.get();
152 }
153 
154 void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext )
155 {
156     OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(),
157         "ContextStack::pushContext - nested incomplete context record identifiers" );
158     maStack.push_back( ContextInfo( rRecInfo, rxContext ) );
159 }
160 
161 void ContextStack::popContext()
162 {
163     OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" );
164     if( !maStack.empty() )
165     {
166         ContextInfo& rContextInfo = maStack.back();
167         if( rContextInfo.second.is() )
168             rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId );
169         maStack.pop_back();
170     }
171 }
172 
173 } // namespace prv
174 
175 // ============================================================================
176 
177 namespace {
178 
179 /** Reads a byte from the passed stream, returns true on success. */
180 inline bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm )
181 {
182     return rStrm.readMemory( &ornByte, 1 ) == 1;
183 }
184 
185 /** Reads a compressed signed 32-bit integer from the passed stream. */
186 bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm )
187 {
188     ornValue = 0;
189     sal_uInt8 nByte;
190     if( !lclReadByte( nByte, rStrm ) ) return false;
191     ornValue = nByte & 0x7F;
192     if( (nByte & 0x80) == 0 ) return true;
193     if( !lclReadByte( nByte, rStrm ) ) return false;
194     ornValue |= sal_Int32( nByte & 0x7F ) << 7;
195     if( (nByte & 0x80) == 0 ) return true;
196     if( !lclReadByte( nByte, rStrm ) ) return false;
197     ornValue |= sal_Int32( nByte & 0x7F ) << 14;
198     if( (nByte & 0x80) == 0 ) return true;
199     if( !lclReadByte( nByte, rStrm ) ) return false;
200     ornValue |= sal_Int32( nByte & 0x7F ) << 21;
201     return true;
202 }
203 
204 bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm )
205 {
206     return
207         lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) &&
208         lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0);
209 }
210 
211 bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm )
212 {
213     sal_Int32 nRecSize = 0;
214     bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm );
215     if( bValid )
216     {
217         orData.realloc( nRecSize );
218         bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize);
219     }
220     return bValid;
221 }
222 
223 } // namespace
224 
225 // ============================================================================
226 
227 RecordParser::RecordParser()
228 {
229     mxLocator.set( new prv::Locator( this ) );
230 }
231 
232 RecordParser::~RecordParser()
233 {
234     if( mxLocator.is() )
235         mxLocator->dispose();
236 }
237 
238 void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler )
239 {
240     mxHandler = rxHandler;
241 
242     // build record infos
243     maStartMap.clear();
244     maEndMap.clear();
245     const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : 0;
246     OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" );
247     for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs )
248     {
249         maStartMap[ pRecs->mnStartRecId ] = *pRecs;
250         if( pRecs->mnEndRecId >= 0 )
251             maEndMap[ pRecs->mnEndRecId ] = *pRecs;
252     }
253 }
254 
255 void RecordParser::parseStream( const RecordInputSource& rInputSource ) throw( SAXException, IOException, RuntimeException )
256 {
257     maSource = rInputSource;
258 
259     if( !maSource.mxInStream || maSource.mxInStream->isEof() )
260         throw IOException();
261     if( !mxHandler.is() )
262         throw SAXException();
263 
264     // start the document
265     Reference< XLocator > xLocator( mxLocator.get() );
266     mxHandler->setDocumentLocator( xLocator );
267     mxHandler->startDocument();
268 
269     // parse the stream
270     mxStack.reset( new prv::ContextStack( mxHandler ) );
271     sal_Int32 nRecId = 0;
272     StreamDataSequence aRecData;
273     while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) )
274     {
275         // create record stream object from imported record data
276         SequenceInputStream aRecStrm( aRecData );
277         // try to leave a context, there may be other incomplete contexts on the stack
278         if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) )
279         {
280             // finalize contexts without record identifier for context end
281             while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() )
282                 mxStack->popContext();
283             // finalize the current context and pop context info from stack
284             OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" );
285             (void)pEndRecInfo;  // suppress compiler warning for unused variable
286             ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
287             if( xCurrContext.is() )
288             {
289                 // context end record may contain some data, handle it as simple record
290                 aRecStrm.seekToStart();
291                 xCurrContext->startRecord( nRecId, aRecStrm );
292                 xCurrContext->endRecord( nRecId );
293             }
294             mxStack->popContext();
295         }
296         else
297         {
298             // end context with incomplete record id, if the same id comes again
299             if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() )
300                 mxStack->popContext();
301             // try to start a new context
302             ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
303             if( xCurrContext.is() )
304             {
305                 aRecStrm.seekToStart();
306                 xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm );
307             }
308             // track all context identifiers on the stack (do not push simple records)
309             const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId );
310             if( pStartRecInfo )
311                 mxStack->pushContext( *pStartRecInfo, xCurrContext );
312             // import the record
313             if( xCurrContext.is() )
314             {
315                 // import the record
316                 aRecStrm.seekToStart();
317                 xCurrContext->startRecord( nRecId, aRecStrm );
318                 // end simple records (context records are finished in ContextStack::popContext)
319                 if( !pStartRecInfo )
320                     xCurrContext->endRecord( nRecId );
321             }
322         }
323     }
324     // close remaining contexts (missing context end records or stream error)
325     while( !mxStack->empty() )
326         mxStack->popContext();
327     mxStack.reset();
328 
329     // finish document
330     mxHandler->endDocument();
331 
332     maSource = RecordInputSource();
333 }
334 
335 const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const
336 {
337     RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId );
338     return (aIt == maStartMap.end()) ? 0 : &aIt->second;
339 }
340 
341 const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const
342 {
343     RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId );
344     return (aIt == maEndMap.end()) ? 0 : &aIt->second;
345 }
346 
347 // ============================================================================
348 
349 } // namespace core
350 } // namespace oox
351