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