LCOV - code coverage report
Current view: top level - sc/source/filter/excel - xilink.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 191 400 47.8 %
Date: 2015-06-13 12:38:46 Functions: 54 75 72.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "xilink.hxx"
      21             : #include "document.hxx"
      22             : #include "formulacell.hxx"
      23             : #include "scextopt.hxx"
      24             : #include "tablink.hxx"
      25             : #include "xistream.hxx"
      26             : #include "xihelper.hxx"
      27             : #include "xiname.hxx"
      28             : #include "excform.hxx"
      29             : #include "tokenarray.hxx"
      30             : #include "externalrefmgr.hxx"
      31             : #include "scmatrix.hxx"
      32             : #include <svl/sharedstringpool.hxx>
      33             : 
      34             : #include <vector>
      35             : #include <boost/ptr_container/ptr_vector.hpp>
      36             : 
      37             : using ::std::vector;
      38             : 
      39             : // *** Helper classes ***
      40             : 
      41             : // Cached external cells ======================================================
      42             : 
      43             : /**
      44             :  * Contains the address and value of an external referenced cell.
      45             :  * Note that this is non-copyable, so cannot be used in most stl/boost containers.
      46             :  */
      47           4 : class XclImpCrn : public XclImpCachedValue
      48             : {
      49             : public:
      50             :     /** Reads a cached value and stores it with its cell address. */
      51             :     explicit            XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
      52             : 
      53           2 :     const XclAddress&   GetAddress() const { return maXclPos;}
      54             : 
      55             : private:
      56             :     XclAddress          maXclPos;       /// Excel position of the cached cell.
      57             : };
      58             : 
      59             : // Sheet in an external document ==============================================
      60             : 
      61             : /** Contains the name and sheet index of one sheet in an external document. */
      62             : class XclImpSupbookTab
      63             : {
      64             : public:
      65             :     /** Stores the sheet name and marks the sheet index as invalid.
      66             :         The sheet index is set while creating the Calc sheet with CreateTable(). */
      67             :     explicit            XclImpSupbookTab( const OUString& rTabName );
      68             :                         ~XclImpSupbookTab();
      69             : 
      70           4 :     inline const OUString& GetTabName() const { return maTabName; }
      71             : 
      72             :     /** Reads a CRN record (external referenced cell) at the specified address. */
      73             :     void                ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
      74             : 
      75             :     void                LoadCachedValues(ScExternalRefCache::TableTypeRef pCacheTable);
      76             : 
      77             : private:
      78             :     typedef boost::shared_ptr< XclImpCrn > XclImpCrnRef;
      79             :     typedef std::vector< XclImpCrnRef > XclImpCrnList;
      80             : 
      81             :     XclImpCrnList       maCrnList;      /// List of CRN records (cached cell values).
      82             :     OUString            maTabName;      /// Name of the external sheet.
      83             : };
      84             : 
      85             : // External document (SUPBOOK) ================================================
      86             : 
      87             : /** This class represents an external linked document (record SUPBOOK).
      88             :     @descr  Contains a list of all referenced sheets in the document. */
      89          98 : class XclImpSupbook : protected XclImpRoot
      90             : {
      91             : public:
      92             :     /** Reads the SUPBOOK record from stream. */
      93             :     explicit            XclImpSupbook( XclImpStream& rStrm );
      94             : 
      95             :     /** Reads an XCT record (count of following CRNs and current sheet). */
      96             :     void                ReadXct( XclImpStream& rStrm );
      97             :     /** Reads a CRN record (external referenced cell). */
      98             :     void                ReadCrn( XclImpStream& rStrm );
      99             :     /** Reads an EXTERNNAME record. */
     100             :     void                ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv = NULL );
     101             : 
     102             :     /** Returns the SUPBOOK record type. */
     103        1165 :     inline XclSupbookType GetType() const { return meType; }
     104             : 
     105             :     /** Returns the URL of the external document. */
     106           2 :     inline const OUString& GetXclUrl() const { return maXclUrl; }
     107             : 
     108             :     /** Returns the external name specified by an index from the Excel document (one-based). */
     109             :     const XclImpExtName* GetExternName( sal_uInt16 nXclIndex ) const;
     110             :     /** Tries to decode the URL to OLE or DDE link components.
     111             :         @descr  For DDE links: Decodes to application name and topic.
     112             :         For OLE object links: Decodes to class name and document URL.
     113             :         @return  true = decoding was successful, returned strings are valid (not empty). */
     114             :     bool                GetLinkData( OUString& rApplic, OUString& rDoc ) const;
     115             :     /** Returns the specified macro name (1-based) or an empty string on error. */
     116             :     const OUString&     GetMacroName( sal_uInt16 nXclNameIdx ) const;
     117             : 
     118             :     const OUString&     GetTabName( sal_uInt16 nXtiTab ) const;
     119             : 
     120             :     sal_uInt16          GetTabCount() const;
     121             : 
     122             :     void                LoadCachedValues();
     123             : 
     124             :     svl::SharedStringPool& GetSharedStringPool();
     125             : 
     126             : private:
     127             :     typedef boost::ptr_vector< XclImpSupbookTab >  XclImpSupbookTabList;
     128             :     typedef boost::ptr_vector< XclImpExtName >     XclImpExtNameList;
     129             : 
     130             :     XclImpSupbookTabList maSupbTabList;     /// All sheet names of the document.
     131             :     XclImpExtNameList   maExtNameList;      /// All external names of the document.
     132             :     OUString            maXclUrl;           /// URL of the external document (Excel mode).
     133             :     XclSupbookType      meType;             /// Type of the supbook record.
     134             :     sal_uInt16          mnSBTab;            /// Current Excel sheet index from SUPBOOK for XCT/CRN records.
     135             : };
     136             : 
     137             : // Import link manager ========================================================
     138             : 
     139             : /** Contains the SUPBOOK index and sheet indexes of an external link.
     140             :     @descr  It is possible to enter a formula like =SUM(Sheet1:Sheet3!A1),
     141             :     therefore here occurs a sheet range. */
     142             : struct XclImpXti
     143             : {
     144             :     sal_uInt16          mnSupbook;      /// Index to SUPBOOK record.
     145             :     sal_uInt16          mnSBTabFirst;   /// Index to the first sheet of the range in the SUPBOOK.
     146             :     sal_uInt16          mnSBTabLast;    /// Index to the last sheet of the range in the SUPBOOK.
     147          80 :     inline explicit     XclImpXti() : mnSupbook( SAL_MAX_UINT16 ), mnSBTabFirst( SAL_MAX_UINT16 ), mnSBTabLast( SAL_MAX_UINT16 ) {}
     148             : };
     149             : 
     150          80 : inline XclImpStream& operator>>( XclImpStream& rStrm, XclImpXti& rXti )
     151             : {
     152          80 :     rXti.mnSupbook = rStrm.ReaduInt16();
     153          80 :     rXti.mnSBTabFirst = rStrm.ReaduInt16();
     154          80 :     rXti.mnSBTabLast = rStrm.ReaduInt16();
     155          80 :     return rStrm;
     156             : }
     157             : 
     158             : /** Implementation of the link manager. */
     159         162 : class XclImpLinkManagerImpl : protected XclImpRoot
     160             : {
     161             : public:
     162             :     explicit            XclImpLinkManagerImpl( const XclImpRoot& rRoot );
     163             : 
     164             :     /** Reads the EXTERNSHEET record. */
     165             :     void                ReadExternsheet( XclImpStream& rStrm );
     166             :     /** Reads a SUPBOOK record. */
     167             :     void                ReadSupbook( XclImpStream& rStrm );
     168             :     /** Reads an XCT record and appends it to the current SUPBOOK. */
     169             :     void                ReadXct( XclImpStream& rStrm );
     170             :     /** Reads a CRN record and appends it to the current SUPBOOK. */
     171             :     void                ReadCrn( XclImpStream& rStrm );
     172             :     /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */
     173             :     void                ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv = NULL );
     174             : 
     175             :     /** Returns true, if the specified XTI entry contains an internal reference. */
     176             :     bool                IsSelfRef( sal_uInt16 nXtiIndex ) const;
     177             :     /** Returns the Calc sheet index range of the specified XTI entry.
     178             :         @return  true = XTI data found, returned sheet index range is valid. */
     179             :     bool                GetScTabRange(
     180             :                             SCTAB& rnFirstScTab, SCTAB& rnLastScTab,
     181             :                             sal_uInt16 nXtiIndex ) const;
     182             :     /** Returns the specified external name or 0 on error. */
     183             :     const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const;
     184             : 
     185             :     /** Returns the absolute file URL of a supporting workbook specified by
     186             :         the index. */
     187             :     const OUString*       GetSupbookUrl( sal_uInt16 nXtiIndex ) const;
     188             : 
     189             :     const OUString&       GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const;
     190             : 
     191             :     /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components.
     192             :         @descr  For DDE links: Decodes to application name and topic.
     193             :         For OLE object links: Decodes to class name and document URL.
     194             :         @return  true = decoding was successful, returned strings are valid (not empty). */
     195             :     bool                GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const;
     196             :     /** Returns the specified macro name or an empty string on error. */
     197             :     const OUString&     GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const;
     198             : 
     199             : private:
     200             :     /** Returns the specified XTI (link entry from BIFF8 EXTERNSHEET record). */
     201             :     const XclImpXti*    GetXti( sal_uInt16 nXtiIndex ) const;
     202             :     /** Returns the specified SUPBOOK (external document). */
     203             :     const XclImpSupbook* GetSupbook( sal_uInt16 nXtiIndex ) const;
     204             : 
     205             :     void                LoadCachedValues();
     206             : 
     207             : private:
     208             :     typedef ::std::vector< XclImpXti >  XclImpXtiVector;
     209             :     typedef boost::ptr_vector< XclImpSupbook > XclImpSupbookList;
     210             : 
     211             :     XclImpXtiVector     maXtiList;          /// List of all XTI structures.
     212             :     XclImpSupbookList   maSupbookList;      /// List of external documents.
     213             : };
     214             : 
     215             : // *** Implementation ***
     216             : 
     217             : // Excel sheet indexes ========================================================
     218             : 
     219             : // original Excel sheet names -------------------------------------------------
     220             : 
     221         219 : void XclImpTabInfo::AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab )
     222             : {
     223         219 :     maTabNames[ rXclTabName ] = nScTab;
     224         219 : }
     225             : 
     226           0 : void XclImpTabInfo::InsertScTab( SCTAB nScTab )
     227             : {
     228           0 :     for( XclTabNameMap::iterator aIt = maTabNames.begin(), aEnd = maTabNames.end(); aIt != aEnd; ++aIt )
     229           0 :         if( aIt->second >= nScTab )
     230           0 :             ++aIt->second;
     231           0 : }
     232             : 
     233           2 : SCTAB XclImpTabInfo::GetScTabFromXclName( const OUString& rXclTabName ) const
     234             : {
     235           2 :     XclTabNameMap::const_iterator aIt = maTabNames.find( rXclTabName );
     236           2 :     return (aIt != maTabNames.end()) ? aIt->second : SCTAB_INVALID;
     237             : }
     238             : 
     239             : // record creation order - TABID record ---------------------------------------
     240             : 
     241          78 : void XclImpTabInfo::ReadTabid( XclImpStream& rStrm )
     242             : {
     243             :     OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
     244          78 :     if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
     245             :     {
     246          78 :         rStrm.EnableDecryption();
     247          78 :         sal_Size nReadCount = rStrm.GetRecLeft() / 2;
     248             :         OSL_ENSURE( nReadCount <= 0xFFFF, "XclImpTabInfo::ReadTabid - record too long" );
     249          78 :         maTabIdVec.clear();
     250          78 :         maTabIdVec.reserve( nReadCount );
     251         296 :         for( sal_Size nIndex = 0; rStrm.IsValid() && (nIndex < nReadCount); ++nIndex )
     252             :             // zero index is not allowed in BIFF8, but it seems that it occurs in real life
     253         218 :             maTabIdVec.push_back( rStrm.ReaduInt16() );
     254             :     }
     255          78 : }
     256             : 
     257          26 : sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const
     258             : {
     259          26 :     sal_uInt16 nReturn = 0;
     260          26 :     for( ScfUInt16Vec::const_iterator aIt = maTabIdVec.begin(), aEnd = maTabIdVec.end(); aIt != aEnd; ++aIt )
     261             :     {
     262          26 :         sal_uInt16 nValue = *aIt;
     263          26 :         if( nValue == nCreatedId )
     264          26 :             return nReturn;
     265           0 :         if( nValue <= nMaxTabId )
     266           0 :             ++nReturn;
     267             :     }
     268           0 :     return 0;
     269             : }
     270             : 
     271             : // External names =============================================================
     272             : 
     273           0 : XclImpExtName::MOper::MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm) :
     274           0 :     mxCached(new ScMatrix(0,0))
     275             : {
     276           0 :     SCSIZE nLastCol = rStrm.ReaduInt8();
     277           0 :     SCSIZE nLastRow = rStrm.ReaduInt16();
     278             : 
     279             :     //assuming worse case scenario of nOp + one byte unistring len
     280           0 :     const size_t nMinRecordSize = 2;
     281           0 :     const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * (nLastCol+1));
     282           0 :     if (nLastRow >= nMaxRows)
     283             :     {
     284             :         SAL_WARN("sc", "Parsing error: " << nMaxRows <<
     285             :                  " max possible rows, but " << nLastRow << " index claimed, truncating");
     286           0 :         nLastRow = nMaxRows-1;
     287             :     }
     288             : 
     289           0 :     mxCached->Resize(nLastCol+1, nLastRow+1);
     290           0 :     for (SCSIZE nRow = 0; nRow <= nLastRow; ++nRow)
     291             :     {
     292           0 :         for (SCSIZE nCol = 0; nCol <= nLastCol; ++nCol)
     293             :         {
     294             :             sal_uInt8 nOp;
     295           0 :             nOp = rStrm.ReaduInt8();
     296           0 :             switch (nOp)
     297             :             {
     298             :                 case 0x01:
     299             :                 {
     300           0 :                     double fVal = rStrm.ReadDouble();
     301           0 :                     mxCached->PutDouble(fVal, nCol, nRow);
     302             :                 }
     303           0 :                 break;
     304             :                 case 0x02:
     305             :                 {
     306           0 :                     OUString aStr = rStrm.ReadUniString();
     307           0 :                     mxCached->PutString(rPool.intern(aStr), nCol, nRow);
     308             :                 }
     309           0 :                 break;
     310             :                 case 0x04:
     311             :                 {
     312           0 :                     bool bVal = rStrm.ReaduInt8();
     313           0 :                     mxCached->PutBoolean(bVal, nCol, nRow);
     314           0 :                     rStrm.Ignore(7);
     315             :                 }
     316           0 :                 break;
     317             :                 case 0x10:
     318             :                 {
     319           0 :                     sal_uInt8 nErr = rStrm.ReaduInt8();
     320             :                     // TODO: Map the error code from xls to calc.
     321           0 :                     mxCached->PutError(nErr, nCol, nRow);
     322           0 :                     rStrm.Ignore(7);
     323             :                 }
     324           0 :                 break;
     325             :                 default:
     326           0 :                     rStrm.Ignore(8);
     327             :             }
     328             :         }
     329             :     }
     330           0 : }
     331             : 
     332           0 : const ScMatrix& XclImpExtName::MOper::GetCache() const
     333             : {
     334           0 :     return *mxCached;
     335             : }
     336             : 
     337           0 : XclImpExtName::XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv )
     338             :     : mpMOper(NULL)
     339           0 :     , mnStorageId(0)
     340             : {
     341           0 :     sal_uInt16 nFlags(0);
     342           0 :     sal_uInt8 nLen(0);
     343             : 
     344           0 :     nFlags = rStrm.ReaduInt16();
     345           0 :     mnStorageId = rStrm.ReaduInt32();
     346           0 :     nLen = rStrm.ReaduInt8();
     347           0 :     maName = rStrm.ReadUniString( nLen );
     348           0 :     if( ::get_flag( nFlags, EXC_EXTN_BUILTIN ) || !::get_flag( nFlags, EXC_EXTN_OLE_OR_DDE ) )
     349             :     {
     350           0 :         if( eSubType == EXC_SBTYPE_ADDIN )
     351             :         {
     352           0 :             meType = xlExtAddIn;
     353           0 :             maName = XclImpRoot::GetScAddInName( maName );
     354             :         }
     355           0 :         else if ( (eSubType == EXC_SBTYPE_EUROTOOL) &&
     356           0 :                 maName.equalsIgnoreAsciiCase( "EUROCONVERT" ) )
     357           0 :             meType = xlExtEuroConvert;
     358             :         else
     359             :         {
     360           0 :             meType = xlExtName;
     361           0 :             maName = ScfTools::ConvertToScDefinedName( maName );
     362             :         }
     363             :     }
     364             :     else
     365             :     {
     366           0 :         meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE );
     367             :     }
     368             : 
     369           0 :     switch (meType)
     370             :     {
     371             :         case xlExtDDE:
     372           0 :             if (rStrm.GetRecLeft() > 1)
     373           0 :                 mxDdeMatrix.reset(new XclImpCachedMatrix(rStrm));
     374           0 :         break;
     375             :         case xlExtName:
     376             :             // TODO: For now, only global external names are supported.  In future
     377             :             // we should extend this to supporting per-sheet external names.
     378           0 :             if (mnStorageId == 0)
     379             :             {
     380           0 :                 if (pFormulaConv)
     381             :                 {
     382           0 :                     const ScTokenArray* pArray = NULL;
     383             :                     sal_uInt16 nFmlaLen;
     384           0 :                     nFmlaLen = rStrm.ReaduInt16();
     385           0 :                     vector<OUString> aTabNames;
     386           0 :                     sal_uInt16 nCount = rSupbook.GetTabCount();
     387           0 :                     aTabNames.reserve(nCount);
     388           0 :                     for (sal_uInt16 i = 0; i < nCount; ++i)
     389           0 :                         aTabNames.push_back(rSupbook.GetTabName(i));
     390             : 
     391           0 :                     pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames);
     392           0 :                     if (pArray)
     393           0 :                         mxArray.reset(pArray->Clone());
     394             :                 }
     395             :             }
     396           0 :         break;
     397             :         case xlExtOLE:
     398           0 :             mpMOper = new MOper(rSupbook.GetSharedStringPool(), rStrm);
     399           0 :         break;
     400             :         default:
     401             :             ;
     402             :     }
     403           0 : }
     404             : 
     405           0 : XclImpExtName::~XclImpExtName()
     406             : {
     407           0 :     delete mpMOper;
     408           0 : }
     409             : 
     410           0 : void XclImpExtName::CreateDdeData( ScDocument& rDoc, const OUString& rApplic, const OUString& rTopic ) const
     411             : {
     412           0 :     ScMatrixRef xResults;
     413           0 :     if( mxDdeMatrix.get() )
     414           0 :         xResults = mxDdeMatrix->CreateScMatrix(rDoc.GetSharedStringPool());
     415           0 :     rDoc.CreateDdeLink( rApplic, rTopic, maName, SC_DDE_DEFAULT, xResults );
     416           0 : }
     417             : 
     418           0 : void XclImpExtName::CreateExtNameData( ScDocument& rDoc, sal_uInt16 nFileId ) const
     419             : {
     420           0 :     if (!mxArray.get())
     421           0 :         return;
     422             : 
     423           0 :     ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
     424           0 :     pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray);
     425             : }
     426             : 
     427             : namespace {
     428             : 
     429             : /**
     430             :  * Decompose the name into sheet name and range name.  An OLE link name is
     431             :  * always formatted like this [ !Sheet1!R1C1:R5C2 ] and it always uses R1C1
     432             :  * notation.
     433             :  */
     434           0 : bool extractSheetAndRange(const OUString& rName, OUString& rSheet, OUString& rRange)
     435             : {
     436           0 :     sal_Int32 n = rName.getLength();
     437           0 :     const sal_Unicode* p = rName.getStr();
     438           0 :     OUStringBuffer aBuf;
     439           0 :     bool bInSheet = true;
     440           0 :     for (sal_Int32 i = 0; i < n; ++i, ++p)
     441             :     {
     442           0 :         if (i == 0)
     443             :         {
     444             :             // first character must be '!'.
     445           0 :             if (*p != '!')
     446           0 :                 return false;
     447           0 :             continue;
     448             :         }
     449             : 
     450           0 :         if (*p == '!')
     451             :         {
     452             :             // sheet name to range separator.
     453           0 :             if (!bInSheet)
     454           0 :                 return false;
     455           0 :             rSheet = aBuf.makeStringAndClear();
     456           0 :             bInSheet = false;
     457           0 :             continue;
     458             :         }
     459             : 
     460           0 :         aBuf.append(*p);
     461             :     }
     462             : 
     463           0 :     rRange = aBuf.makeStringAndClear();
     464           0 :     return true;
     465             : }
     466             : 
     467             : }
     468             : 
     469           0 : bool XclImpExtName::CreateOleData(ScDocument& rDoc, const OUString& rUrl,
     470             :                                   sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const
     471             : {
     472           0 :     if (!mpMOper)
     473           0 :         return false;
     474             : 
     475           0 :     OUString aSheet, aRangeStr;
     476           0 :     if (!extractSheetAndRange(maName, aSheet, aRangeStr))
     477           0 :         return false;
     478             : 
     479           0 :     ScRange aRange;
     480           0 :     sal_uInt16 nRes = aRange.ParseAny(aRangeStr, &rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
     481           0 :     if ((nRes & SCA_VALID) != SCA_VALID)
     482           0 :         return false;
     483             : 
     484           0 :     if (aRange.aStart.Tab() != aRange.aEnd.Tab())
     485             :         // We don't support multi-sheet range for this.
     486           0 :         return false;
     487             : 
     488           0 :     const ScMatrix& rCache = mpMOper->GetCache();
     489             :     SCSIZE nC, nR;
     490           0 :     rCache.GetDimensions(nC, nR);
     491           0 :     if (!nC || !nR)
     492             :         // cache matrix is empty.
     493           0 :         return false;
     494             : 
     495           0 :     ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
     496           0 :     sal_uInt16 nFileId = pRefMgr->getExternalFileId(rUrl);
     497           0 :     ScExternalRefCache::TableTypeRef xTab = pRefMgr->getCacheTable(nFileId, aSheet, true, NULL);
     498           0 :     if (!xTab)
     499             :         // cache table creation failed.
     500           0 :         return false;
     501             : 
     502           0 :     xTab->setWholeTableCached();
     503           0 :     for (SCSIZE i = 0; i < nR; ++i)
     504             :     {
     505           0 :         for (SCSIZE j = 0; j < nC; ++j)
     506             :         {
     507           0 :             SCCOL nCol = aRange.aStart.Col() + j;
     508           0 :             SCROW nRow = aRange.aStart.Row() + i;
     509             : 
     510           0 :             ScMatrixValue aVal = rCache.Get(j, i);
     511           0 :             switch (aVal.nType)
     512             :             {
     513             :                 case SC_MATVAL_BOOLEAN:
     514             :                 {
     515           0 :                     bool b = aVal.GetBoolean();
     516           0 :                     ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
     517           0 :                     xTab->setCell(nCol, nRow, pToken, 0, false);
     518             :                 }
     519           0 :                 break;
     520             :                 case SC_MATVAL_VALUE:
     521             :                 {
     522           0 :                     ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(aVal.fVal));
     523           0 :                     xTab->setCell(nCol, nRow, pToken, 0, false);
     524             :                 }
     525           0 :                 break;
     526             :                 case SC_MATVAL_STRING:
     527             :                 {
     528           0 :                     const svl::SharedString aStr( aVal.GetString());
     529           0 :                     ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(aStr));
     530           0 :                     xTab->setCell(nCol, nRow, pToken, 0, false);
     531             :                 }
     532           0 :                 break;
     533             :                 default:
     534             :                     ;
     535             :             }
     536           0 :         }
     537             :     }
     538             : 
     539           0 :     rFileId = nFileId;
     540           0 :     rTabName = aSheet;
     541           0 :     rRange = aRange;
     542           0 :     return true;
     543             : }
     544             : 
     545           0 : bool XclImpExtName::HasFormulaTokens() const
     546             : {
     547           0 :     return (mxArray.get() != NULL);
     548             : }
     549             : 
     550             : // Cached external cells ======================================================
     551             : 
     552           2 : XclImpCrn::XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) :
     553             :     XclImpCachedValue( rStrm ),
     554           2 :     maXclPos( rXclPos )
     555             : {
     556           2 : }
     557             : 
     558             : // Sheet in an external document ==============================================
     559             : 
     560           2 : XclImpSupbookTab::XclImpSupbookTab( const OUString& rTabName ) :
     561           2 :     maTabName( rTabName )
     562             : {
     563           2 : }
     564             : 
     565           2 : XclImpSupbookTab::~XclImpSupbookTab()
     566             : {
     567           2 : }
     568             : 
     569           2 : void XclImpSupbookTab::ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos )
     570             : {
     571           2 :     XclImpCrnRef crnRef( new XclImpCrn(rStrm, rXclPos) );
     572           2 :     maCrnList.push_back( crnRef );
     573           2 : }
     574             : 
     575           2 : void XclImpSupbookTab::LoadCachedValues(ScExternalRefCache::TableTypeRef pCacheTable)
     576             : {
     577           2 :     if (maCrnList.empty())
     578           2 :         return;
     579             : 
     580           4 :     for (XclImpCrnList::iterator itCrnRef = maCrnList.begin(); itCrnRef != maCrnList.end(); ++itCrnRef)
     581             :     {
     582           2 :         const XclImpCrn* const pCrn = itCrnRef->get();
     583           2 :         const XclAddress& rAddr = pCrn->GetAddress();
     584           2 :         switch (pCrn->GetType())
     585             :         {
     586             :             case EXC_CACHEDVAL_BOOL:
     587             :             {
     588           0 :                 bool b = pCrn->GetBool();
     589           0 :                 ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
     590           0 :                 pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
     591             :             }
     592           0 :             break;
     593             :             case EXC_CACHEDVAL_DOUBLE:
     594             :             {
     595           0 :                 double f = pCrn->GetValue();
     596           0 :                 ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(f));
     597           0 :                 pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
     598             :             }
     599           0 :             break;
     600             :             case EXC_CACHEDVAL_ERROR:
     601             :             {
     602           0 :                 double fError = XclTools::ErrorToDouble( pCrn->GetXclError() );
     603           0 :                 ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(fError));
     604           0 :                 pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
     605             :             }
     606           0 :             break;
     607             :             case EXC_CACHEDVAL_STRING:
     608             :             {
     609           2 :                 const OUString& rStr = pCrn->GetString();
     610           2 :                 ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(rStr));
     611           2 :                 pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
     612             :             }
     613           2 :             break;
     614             :             default:
     615             :                 ;
     616             :         }
     617             :     }
     618             : }
     619             : 
     620             : // External document (SUPBOOK) ================================================
     621             : 
     622          49 : XclImpSupbook::XclImpSupbook( XclImpStream& rStrm ) :
     623          49 :     XclImpRoot( rStrm.GetRoot() ),
     624             :     meType( EXC_SBTYPE_UNKNOWN ),
     625          49 :     mnSBTab( EXC_TAB_DELETED )
     626             : {
     627             :     sal_uInt16 nSBTabCnt;
     628          49 :     nSBTabCnt = rStrm.ReaduInt16();
     629             : 
     630          49 :     if( rStrm.GetRecLeft() == 2 )
     631             :     {
     632          47 :         switch( rStrm.ReaduInt16() )
     633             :         {
     634          47 :             case EXC_SUPB_SELF:     meType = EXC_SBTYPE_SELF;   break;
     635           0 :             case EXC_SUPB_ADDIN:    meType = EXC_SBTYPE_ADDIN;  break;
     636             :             default:    OSL_FAIL( "XclImpSupbook::XclImpSupbook - unknown special SUPBOOK type" );
     637             :         }
     638          96 :         return;
     639             :     }
     640             : 
     641           2 :     OUString aEncUrl( rStrm.ReadUniString() );
     642           2 :     bool bSelf = false;
     643           2 :     XclImpUrlHelper::DecodeUrl( maXclUrl, bSelf, GetRoot(), aEncUrl );
     644             : 
     645           2 :     if( maXclUrl.equalsIgnoreAsciiCase( "\010EUROTOOL.XLA" ) )
     646             :     {
     647           0 :         meType = EXC_SBTYPE_EUROTOOL;
     648           0 :         maSupbTabList.push_back( new XclImpSupbookTab( maXclUrl ) );
     649             :     }
     650           2 :     else if( nSBTabCnt )
     651             :     {
     652           2 :         meType = EXC_SBTYPE_EXTERN;
     653             : 
     654             :         //assuming all empty strings with just len header of 0
     655           2 :         const size_t nMinRecordSize = sizeof(sal_Int16);
     656           2 :         const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
     657           2 :         if (nSBTabCnt > nMaxRecords)
     658             :         {
     659             :             SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
     660             :                      " max possible entries, but " << nSBTabCnt << " claimed, truncating");
     661           0 :             nSBTabCnt = nMaxRecords;
     662             :         }
     663             : 
     664           4 :         for( sal_uInt16 nSBTab = 0; nSBTab < nSBTabCnt; ++nSBTab )
     665             :         {
     666           2 :             OUString aTabName( rStrm.ReadUniString() );
     667           2 :             maSupbTabList.push_back( new XclImpSupbookTab( aTabName ) );
     668           2 :         }
     669             :     }
     670             :     else
     671             :     {
     672           0 :         meType = EXC_SBTYPE_SPECIAL;
     673             :         // create dummy list entry
     674           0 :         maSupbTabList.push_back( new XclImpSupbookTab( maXclUrl ) );
     675           2 :     }
     676             : }
     677             : 
     678           2 : void XclImpSupbook::ReadXct( XclImpStream& rStrm )
     679             : {
     680           2 :     rStrm.Ignore( 2 );
     681           2 :     mnSBTab = rStrm.ReaduInt16();
     682           2 : }
     683             : 
     684           2 : void XclImpSupbook::ReadCrn( XclImpStream& rStrm )
     685             : {
     686           2 :     if (mnSBTab >= maSupbTabList.size())
     687           2 :         return;
     688           2 :     XclImpSupbookTab& rSbTab = maSupbTabList[mnSBTab];
     689             :     sal_uInt8 nXclColLast, nXclColFirst;
     690             :     sal_uInt16 nXclRow;
     691           2 :     nXclColLast = rStrm.ReaduInt8();
     692           2 :     nXclColFirst = rStrm.ReaduInt8();
     693           2 :     nXclRow = rStrm.ReaduInt16();
     694             : 
     695           4 :     for( sal_uInt8 nXclCol = nXclColFirst; (nXclCol <= nXclColLast) && (rStrm.GetRecLeft() > 1); ++nXclCol )
     696           2 :         rSbTab.ReadCrn( rStrm, XclAddress( nXclCol, nXclRow ) );
     697             : }
     698             : 
     699           0 : void XclImpSupbook::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
     700             : {
     701           0 :     maExtNameList.push_back( new XclImpExtName( *this, rStrm, meType, pFormulaConv ) );
     702           0 : }
     703             : 
     704           0 : const XclImpExtName* XclImpSupbook::GetExternName( sal_uInt16 nXclIndex ) const
     705             : {
     706             :     OSL_ENSURE( nXclIndex > 0, "XclImpSupbook::GetExternName - index must be >0" );
     707           0 :     if (meType == EXC_SBTYPE_SELF || nXclIndex > maExtNameList.size())
     708           0 :         return NULL;
     709           0 :     return &maExtNameList[nXclIndex-1];
     710             : }
     711             : 
     712           0 : bool XclImpSupbook::GetLinkData( OUString& rApplic, OUString& rTopic ) const
     713             : {
     714           0 :     return (meType == EXC_SBTYPE_SPECIAL) && XclImpUrlHelper::DecodeLink( rApplic, rTopic, maXclUrl );
     715             : }
     716             : 
     717           1 : const OUString& XclImpSupbook::GetMacroName( sal_uInt16 nXclNameIdx ) const
     718             : {
     719             :     OSL_ENSURE( nXclNameIdx > 0, "XclImpSupbook::GetMacroName - index must be >0" );
     720           1 :     const XclImpName* pName = (meType == EXC_SBTYPE_SELF) ? GetNameManager().GetName( nXclNameIdx ) : 0;
     721           1 :     return (pName && pName->IsVBName()) ? pName->GetScName() : EMPTY_OUSTRING;
     722             : }
     723             : 
     724           2 : const OUString& XclImpSupbook::GetTabName( sal_uInt16 nXtiTab ) const
     725             : {
     726           2 :     if (nXtiTab >= maSupbTabList.size())
     727           0 :         return EMPTY_OUSTRING;
     728           2 :     return maSupbTabList[nXtiTab].GetTabName();
     729             : }
     730             : 
     731           0 : sal_uInt16 XclImpSupbook::GetTabCount() const
     732             : {
     733           0 :     return ulimit_cast<sal_uInt16>(maSupbTabList.size());
     734             : }
     735             : 
     736          49 : void XclImpSupbook::LoadCachedValues()
     737             : {
     738          49 :     if (meType != EXC_SBTYPE_EXTERN || GetExtDocOptions().GetDocSettings().mnLinkCnt > 0 || !GetDocShell())
     739          96 :         return;
     740             : 
     741           2 :     OUString aAbsUrl( ScGlobal::GetAbsDocName(maXclUrl, GetDocShell()) );
     742             : 
     743           2 :     ScExternalRefManager* pRefMgr = GetRoot().GetDoc().GetExternalRefManager();
     744           2 :     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aAbsUrl);
     745             : 
     746           4 :     for (XclImpSupbookTabList::iterator itTab = maSupbTabList.begin(); itTab != maSupbTabList.end(); ++itTab)
     747             :     {
     748           2 :         const OUString& rTabName = itTab->GetTabName();
     749           2 :         ScExternalRefCache::TableTypeRef pCacheTable = pRefMgr->getCacheTable(nFileId, rTabName, true);
     750           2 :         itTab->LoadCachedValues(pCacheTable);
     751           2 :         pCacheTable->setWholeTableCached();
     752           4 :     }
     753             : }
     754             : 
     755           0 : svl::SharedStringPool& XclImpSupbook::GetSharedStringPool()
     756             : {
     757           0 :     return GetDoc().GetSharedStringPool();
     758             : }
     759             : 
     760             : // Import link manager ========================================================
     761             : 
     762          81 : XclImpLinkManagerImpl::XclImpLinkManagerImpl( const XclImpRoot& rRoot ) :
     763          81 :     XclImpRoot( rRoot )
     764             : {
     765          81 : }
     766             : 
     767          47 : void XclImpLinkManagerImpl::ReadExternsheet( XclImpStream& rStrm )
     768             : {
     769             :     sal_uInt16 nXtiCount;
     770          47 :     nXtiCount = rStrm.ReaduInt16();
     771             :     OSL_ENSURE( static_cast< sal_Size >( nXtiCount * 6 ) == rStrm.GetRecLeft(), "XclImpLinkManagerImpl::ReadExternsheet - invalid count" );
     772          47 :     nXtiCount = static_cast< sal_uInt16 >( ::std::min< sal_Size >( nXtiCount, rStrm.GetRecLeft() / 6 ) );
     773             : 
     774             :     /*  #i104057# A weird external XLS generator writes multiple EXTERNSHEET
     775             :         records instead of only one as expected. Surprisingly, Excel seems to
     776             :         insert the entries of the second record before the entries of the first
     777             :         record. */
     778          47 :     XclImpXtiVector aNewEntries( nXtiCount );
     779         127 :     for( XclImpXtiVector::iterator aIt = aNewEntries.begin(), aEnd = aNewEntries.end(); rStrm.IsValid() && (aIt != aEnd); ++aIt )
     780          80 :         rStrm >> *aIt;
     781          47 :     maXtiList.insert( maXtiList.begin(), aNewEntries.begin(), aNewEntries.end() );
     782             : 
     783          47 :     LoadCachedValues();
     784          47 : }
     785             : 
     786          49 : void XclImpLinkManagerImpl::ReadSupbook( XclImpStream& rStrm )
     787             : {
     788          49 :     maSupbookList.push_back( new XclImpSupbook( rStrm ) );
     789          49 : }
     790             : 
     791           2 : void XclImpLinkManagerImpl::ReadXct( XclImpStream& rStrm )
     792             : {
     793           2 :     if( !maSupbookList.empty() )
     794           2 :         maSupbookList.back().ReadXct( rStrm );
     795           2 : }
     796             : 
     797           2 : void XclImpLinkManagerImpl::ReadCrn( XclImpStream& rStrm )
     798             : {
     799           2 :     if( !maSupbookList.empty() )
     800           2 :         maSupbookList.back().ReadCrn( rStrm );
     801           2 : }
     802             : 
     803           0 : void XclImpLinkManagerImpl::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
     804             : {
     805           0 :     if( !maSupbookList.empty() )
     806           0 :         maSupbookList.back().ReadExternname( rStrm, pFormulaConv );
     807           0 : }
     808             : 
     809        1165 : bool XclImpLinkManagerImpl::IsSelfRef( sal_uInt16 nXtiIndex ) const
     810             : {
     811        1165 :     const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
     812        1165 :     return pSupbook && (pSupbook->GetType() == EXC_SBTYPE_SELF);
     813             : }
     814             : 
     815        1173 : bool XclImpLinkManagerImpl::GetScTabRange(
     816             :         SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
     817             : {
     818        1173 :     if( const XclImpXti* pXti = GetXti( nXtiIndex ) )
     819             :     {
     820        1173 :         if (!maSupbookList.empty() && (pXti->mnSupbook < maSupbookList.size()) )
     821             :         {
     822        1173 :             rnFirstScTab = pXti->mnSBTabFirst;
     823        1173 :             rnLastScTab  = pXti->mnSBTabLast;
     824        1173 :             return true;
     825             :         }
     826             :     }
     827           0 :     return false;
     828             : }
     829             : 
     830           0 : const XclImpExtName* XclImpLinkManagerImpl::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
     831             : {
     832           0 :     const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
     833           0 :     return pSupbook ? pSupbook->GetExternName( nExtName ) : 0;
     834             : }
     835             : 
     836           2 : const OUString* XclImpLinkManagerImpl::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
     837             : {
     838           2 :     const XclImpSupbook* p = GetSupbook( nXtiIndex );
     839           2 :     if (!p)
     840           0 :         return NULL;
     841           2 :     return &p->GetXclUrl();
     842             : }
     843             : 
     844           2 : const OUString& XclImpLinkManagerImpl::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
     845             : {
     846           2 :     const XclImpSupbook* p = GetSupbook(nXti);
     847           2 :     return p ? p->GetTabName(nXtiTab) : EMPTY_OUSTRING;
     848             : }
     849             : 
     850           0 : bool XclImpLinkManagerImpl::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
     851             : {
     852           0 :     const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
     853           0 :     return pSupbook && pSupbook->GetLinkData( rApplic, rTopic );
     854             : }
     855             : 
     856           1 : const OUString& XclImpLinkManagerImpl::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
     857             : {
     858           1 :     const XclImpSupbook* pSupbook = GetSupbook( nExtSheet );
     859           1 :     return pSupbook ? pSupbook->GetMacroName( nExtName ) : EMPTY_OUSTRING;
     860             : }
     861             : 
     862        2343 : const XclImpXti* XclImpLinkManagerImpl::GetXti( sal_uInt16 nXtiIndex ) const
     863             : {
     864        2343 :     return (nXtiIndex < maXtiList.size()) ? &maXtiList[ nXtiIndex ] : 0;
     865             : }
     866             : 
     867        1170 : const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( sal_uInt16 nXtiIndex ) const
     868             : {
     869        1170 :     if ( maSupbookList.empty() )
     870           0 :         return NULL;
     871        1170 :     const XclImpXti* pXti = GetXti( nXtiIndex );
     872        1170 :     if (!pXti || pXti->mnSupbook >= maSupbookList.size())
     873           0 :         return NULL;
     874        1170 :     return &(maSupbookList.at( pXti->mnSupbook ));
     875             : }
     876             : 
     877          47 : void XclImpLinkManagerImpl::LoadCachedValues()
     878             : {
     879             :     // Read all CRN records which can be accessed via XclImpSupbook, and store
     880             :     // the cached values to the external reference manager.
     881          96 :     for (XclImpSupbookList::iterator itSupbook = maSupbookList.begin(); itSupbook != maSupbookList.end(); ++itSupbook)
     882          49 :         itSupbook->LoadCachedValues();
     883          47 : }
     884             : 
     885          81 : XclImpLinkManager::XclImpLinkManager( const XclImpRoot& rRoot ) :
     886             :     XclImpRoot( rRoot ),
     887          81 :     mxImpl( new XclImpLinkManagerImpl( rRoot ) )
     888             : {
     889          81 : }
     890             : 
     891         162 : XclImpLinkManager::~XclImpLinkManager()
     892             : {
     893         162 : }
     894             : 
     895          47 : void XclImpLinkManager::ReadExternsheet( XclImpStream& rStrm )
     896             : {
     897          47 :     mxImpl->ReadExternsheet( rStrm );
     898          47 : }
     899             : 
     900          49 : void XclImpLinkManager::ReadSupbook( XclImpStream& rStrm )
     901             : {
     902          49 :     mxImpl->ReadSupbook( rStrm );
     903          49 : }
     904             : 
     905           2 : void XclImpLinkManager::ReadXct( XclImpStream& rStrm )
     906             : {
     907           2 :     mxImpl->ReadXct( rStrm );
     908           2 : }
     909             : 
     910           2 : void XclImpLinkManager::ReadCrn( XclImpStream& rStrm )
     911             : {
     912           2 :     mxImpl->ReadCrn( rStrm );
     913           2 : }
     914             : 
     915           0 : void XclImpLinkManager::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
     916             : {
     917           0 :     mxImpl->ReadExternname( rStrm, pFormulaConv );
     918           0 : }
     919             : 
     920        1165 : bool XclImpLinkManager::IsSelfRef( sal_uInt16 nXtiIndex ) const
     921             : {
     922        1165 :     return mxImpl->IsSelfRef( nXtiIndex );
     923             : }
     924             : 
     925        1173 : bool XclImpLinkManager::GetScTabRange(
     926             :         SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
     927             : {
     928        1173 :     return mxImpl->GetScTabRange( rnFirstScTab, rnLastScTab, nXtiIndex );
     929             : }
     930             : 
     931           0 : const XclImpExtName* XclImpLinkManager::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
     932             : {
     933           0 :     return mxImpl->GetExternName( nXtiIndex, nExtName );
     934             : }
     935             : 
     936           2 : const OUString* XclImpLinkManager::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
     937             : {
     938           2 :     return mxImpl->GetSupbookUrl(nXtiIndex);
     939             : }
     940             : 
     941           2 : const OUString& XclImpLinkManager::GetSupbookTabName( sal_uInt16 nXti,  sal_uInt16 nXtiTab ) const
     942             : {
     943           2 :     return mxImpl->GetSupbookTabName(nXti, nXtiTab);
     944             : }
     945             : 
     946           0 : bool XclImpLinkManager::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
     947             : {
     948           0 :     return mxImpl->GetLinkData( rApplic, rTopic, nXtiIndex );
     949             : }
     950             : 
     951           1 : const OUString& XclImpLinkManager::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
     952             : {
     953           1 :     return mxImpl->GetMacroName( nExtSheet, nExtName );
     954          30 : }
     955             : 
     956             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11