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