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: 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 69 void Locator::dispose() 70 { 71 mpParser = 0; 72 } 73 74 void Locator::checkDispose() throw( RuntimeException ) 75 { 76 if( !mpParser ) 77 throw DisposedException(); 78 } 79 80 sal_Int32 SAL_CALL Locator::getColumnNumber() throw( RuntimeException ) 81 { 82 return -1; 83 } 84 85 sal_Int32 SAL_CALL Locator::getLineNumber() throw( RuntimeException ) 86 { 87 return -1; 88 } 89 90 OUString SAL_CALL Locator::getPublicId() throw( RuntimeException ) 91 { 92 checkDispose(); 93 return mpParser->getInputSource().maPublicId; 94 } 95 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 109 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 128 ContextStack::ContextStack( FragmentHandlerRef xHandler ) : 129 mxHandler( xHandler ) 130 { 131 } 132 133 sal_Int32 ContextStack::getCurrentRecId() const 134 { 135 return maStack.empty() ? -1 : maStack.back().first.mnStartRecId; 136 } 137 138 bool ContextStack::hasCurrentEndRecId() const 139 { 140 return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0); 141 } 142 143 ContextHandlerRef ContextStack::getCurrentContext() const 144 { 145 if( !maStack.empty() ) 146 return maStack.back().second; 147 return mxHandler.get(); 148 } 149 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 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. */ 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. */ 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 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 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 223 RecordParser::RecordParser() 224 { 225 mxLocator.set( new prv::Locator( this ) ); 226 } 227 228 RecordParser::~RecordParser() 229 { 230 if( mxLocator.is() ) 231 mxLocator->dispose(); 232 } 233 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 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 331 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 337 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