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