summaryrefslogtreecommitdiff
path: root/sc/source/filter/excel
diff options
context:
space:
mode:
authorKohei Yoshida <kyoshida@novell.com>2011-04-13 22:35:09 -0400
committerKohei Yoshida <kyoshida@novell.com>2011-04-13 22:53:04 -0400
commit164317eda5dacfc8585fafc5f9fba8845bba5313 (patch)
tree95cfe6b2346a7fcab93c07871fba5ec5180bb879 /sc/source/filter/excel
parent18d6092b76696148e0f435af98c83a9e63aaa466 (diff)
Convert OLE links from XLS doc into external ranges on import.
Linked cell ranges that are OLE links to other Excel documents were previously ignored on import. Let's import them but convert them into external references which are more robust than OLE links.
Diffstat (limited to 'sc/source/filter/excel')
-rw-r--r--sc/source/filter/excel/excform8.cxx81
-rw-r--r--sc/source/filter/excel/read.cxx4
-rw-r--r--sc/source/filter/excel/xilink.cxx230
3 files changed, 288 insertions, 27 deletions
diff --git a/sc/source/filter/excel/excform8.cxx b/sc/source/filter/excel/excform8.cxx
index aebb75418f36..3cfc20d5aeba 100644
--- a/sc/source/filter/excel/excform8.cxx
+++ b/sc/source/filter/excel/excform8.cxx
@@ -42,9 +42,51 @@
#include "externalrefmgr.hxx"
#include <vector>
+#include <cstring>
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
using ::std::vector;
+namespace {
+
+/**
+ * Extract a file path from OLE link path. An OLE link path is expected to
+ * be in the following format:
+ *
+ * Excel.Sheet.8 \3 [file path]
+ */
+bool extractFilePath(const OUString& rUrl, OUString& rPath)
+{
+ const char* prefix = "Excel.Sheet.8\3";
+ size_t nPrefixLen = ::std::strlen(prefix);
+
+ sal_Int32 n = rUrl.getLength();
+ if (n <= static_cast<sal_Int32>(nPrefixLen))
+ // needs to have the specified prefix.
+ return false;
+
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rUrl.getStr();
+ for (size_t i = 0; i < static_cast<size_t>(n); ++i, ++p)
+ {
+ if (i < nPrefixLen)
+ {
+ sal_Unicode pc = static_cast<sal_Unicode>(*prefix++);
+ if (pc != *p)
+ return false;
+
+ continue;
+ }
+ aBuf.append(*p);
+ }
+
+ rPath = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
ExcelToSc8::ExternalTabInfo::ExternalTabInfo() :
mnFileId(0), mbExternal(false)
{
@@ -92,6 +134,20 @@ bool ExcelToSc8::Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB&
return GetExternalFileIdFromXti(nIxti, rExtInfo.mnFileId);
}
+bool ExcelToSc8::HandleOleLink(sal_uInt16 nXtiIndex, const XclImpExtName& rExtName, ExternalTabInfo& rExtInfo)
+{
+ const String* pUrl = rLinkMan.GetSupbookUrl(nXtiIndex);
+ if (!pUrl)
+ return false;
+
+ OUString aPath;
+ if (!extractFilePath(*pUrl, aPath))
+ // file path extraction failed.
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(aPath, GetDocShell());
+ return rExtName.CreateOleData(GetDoc(), aFileUrl, rExtInfo.mnFileId, rExtInfo.maTabName, rExtInfo.maRange);
+}
// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
// otherwise it will seek to the first byte past additional content after <nFormulaLen>
@@ -682,8 +738,29 @@ ConvErr ExcelToSc8::Convert( const ScTokenArray*& rpTokArray, XclImpStream& aIn,
aStack << aPool.Store( ocEuroConvert, String() );
}
break;
-
- default: // OLE link
+ case xlExtOLE:
+ {
+ ExternalTabInfo aExtInfo;
+ if (HandleOleLink(nXtiIndex, *pExtName, aExtInfo))
+ {
+ if (aExtInfo.maRange.aStart == aExtInfo.maRange.aEnd)
+ {
+ // single cell
+ aSRD.InitAddress(aExtInfo.maRange.aStart);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ // range
+ aCRD.InitRange(aExtInfo.maRange);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ }
+ else
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ }
+ break;
+ default:
{
aPool << ocBad;
aPool >> aStack;
diff --git a/sc/source/filter/excel/read.cxx b/sc/source/filter/excel/read.cxx
index 70feaa4e4e22..96e68a9df01f 100644
--- a/sc/source/filter/excel/read.cxx
+++ b/sc/source/filter/excel/read.cxx
@@ -1125,8 +1125,8 @@ FltError ImportExcel8::Read( void )
case 0x9D: AutoFilterInfo(); break;// AUTOFILTERINFO
case 0x9E: AutoFilter(); break; // AUTOFILTER
case 0x0208: Row34(); break; // ROW [ 34 ]
- case 0x0021:
- case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case EXC_ID2_ARRAY:
+ case EXC_ID3_ARRAY: Array34(); break; // ARRAY [ 34 ]
case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345 ]
case 0x04BC: Shrfmla(); break; // SHRFMLA [ 5 ]
case 0x0867: SheetProtection(); break; // SHEETPROTECTION
diff --git a/sc/source/filter/excel/xilink.cxx b/sc/source/filter/excel/xilink.cxx
index 634a723238a6..75b0e1b01468 100644
--- a/sc/source/filter/excel/xilink.cxx
+++ b/sc/source/filter/excel/xilink.cxx
@@ -44,6 +44,9 @@
#include <boost/ptr_container/ptr_vector.hpp>
using ::std::vector;
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
+
// ============================================================================
// *** Helper classes ***
@@ -284,7 +287,61 @@ sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMa
// External names =============================================================
-XclImpExtName::XclImpExtName( const XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv )
+XclImpExtName::MOper::MOper(XclImpStream& rStrm) :
+ mxCached(new ScMatrix(0,0))
+{
+ SCSIZE nLastCol = rStrm.ReaduInt8();
+ SCSIZE nLastRow = rStrm.ReaduInt16();
+ mxCached->Resize(nLastCol+1, nLastRow+1);
+ for (SCSIZE nRow = 0; nRow <= nLastRow; ++nRow)
+ {
+ for (SCSIZE nCol = 0; nCol <= nLastCol; ++nCol)
+ {
+ sal_uInt8 nOp;
+ rStrm >> nOp;
+ switch (nOp)
+ {
+ case 0x01:
+ {
+ double fVal = rStrm.ReadDouble();
+ mxCached->PutDouble(fVal, nCol, nRow);
+ }
+ break;
+ case 0x02:
+ {
+ OUString aStr = rStrm.ReadUniString();
+ mxCached->PutString(aStr, nCol, nRow);
+ }
+ break;
+ case 0x04:
+ {
+ bool bVal = rStrm.ReaduInt8();
+ mxCached->PutBoolean(bVal, nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ case 0x10:
+ {
+ sal_uInt8 nErr = rStrm.ReaduInt8();
+ // TODO: Map the error code from xls to calc.
+ mxCached->PutError(nErr, nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ default:
+ rStrm.Ignore(8);
+ }
+ }
+ }
+}
+
+const ScMatrix& XclImpExtName::MOper::GetCache() const
+{
+ return *mxCached;
+}
+
+XclImpExtName::XclImpExtName( const XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv ) :
+ mpMOper(NULL)
{
sal_uInt16 nFlags;
sal_uInt8 nLen;
@@ -312,36 +369,45 @@ XclImpExtName::XclImpExtName( const XclImpSupbook& rSupbook, XclImpStream& rStrm
meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE );
}
- if( (meType == xlExtDDE) && (rStrm.GetRecLeft() > 1) )
- mxDdeMatrix.reset( new XclImpCachedMatrix( rStrm ) );
-
- if (meType == xlExtName)
+ switch (meType)
{
- // TODO: For now, only global external names are supported. In future
- // we should extend this to supporting per-sheet external names.
- if (mnStorageId == 0)
- {
- if (pFormulaConv)
+ case xlExtDDE:
+ if (rStrm.GetRecLeft() > 1)
+ mxDdeMatrix.reset(new XclImpCachedMatrix(rStrm));
+ break;
+ case xlExtName:
+ // TODO: For now, only global external names are supported. In future
+ // we should extend this to supporting per-sheet external names.
+ if (mnStorageId == 0)
{
- const ScTokenArray* pArray = NULL;
- sal_uInt16 nFmlaLen;
- rStrm >> nFmlaLen;
- vector<String> aTabNames;
- sal_uInt16 nCount = rSupbook.GetTabCount();
- aTabNames.reserve(nCount);
- for (sal_uInt16 i = 0; i < nCount; ++i)
- aTabNames.push_back(rSupbook.GetTabName(i));
-
- pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames);
- if (pArray)
- mxArray.reset(pArray->Clone());
+ if (pFormulaConv)
+ {
+ const ScTokenArray* pArray = NULL;
+ sal_uInt16 nFmlaLen;
+ rStrm >> nFmlaLen;
+ vector<String> aTabNames;
+ sal_uInt16 nCount = rSupbook.GetTabCount();
+ aTabNames.reserve(nCount);
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ aTabNames.push_back(rSupbook.GetTabName(i));
+
+ pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames);
+ if (pArray)
+ mxArray.reset(pArray->Clone());
+ }
}
- }
+ break;
+ case xlExtOLE:
+ mpMOper = new MOper(rStrm);
+ break;
+ default:
+ ;
}
}
XclImpExtName::~XclImpExtName()
{
+ delete mpMOper;
}
void XclImpExtName::CreateDdeData( ScDocument& rDoc, const String& rApplic, const String& rTopic ) const
@@ -361,6 +427,124 @@ void XclImpExtName::CreateExtNameData( ScDocument& rDoc, sal_uInt16 nFileId ) co
pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray);
}
+namespace {
+
+/**
+ * Decompose the name into sheet name and range name. An OLE link name is
+ * always formatted like this [ !Sheet1!R1C1:R5C2 ] and it always uses R1C1
+ * notation.
+ */
+bool extractSheetAndRange(const OUString& rName, OUString& rSheet, OUString& rRange)
+{
+ sal_Int32 n = rName.getLength();
+ const sal_Unicode* p = rName.getStr();
+ OUStringBuffer aBuf;
+ bool bInSheet = true;
+ for (sal_Int32 i = 0; i < n; ++i, ++p)
+ {
+ if (i == 0)
+ {
+ // first character must be '!'.
+ if (*p != '!')
+ return false;
+ continue;
+ }
+
+ if (*p == '!')
+ {
+ // sheet name to range separator.
+ if (!bInSheet)
+ return false;
+ rSheet = aBuf.makeStringAndClear();
+ bInSheet = false;
+ continue;
+ }
+
+ aBuf.append(*p);
+ }
+
+ rRange = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+bool XclImpExtName::CreateOleData(ScDocument& rDoc, const OUString& rUrl,
+ sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const
+{
+ if (!mpMOper)
+ return false;
+
+ OUString aSheet, aRangeStr;
+ if (!extractSheetAndRange(maName, aSheet, aRangeStr))
+ return false;
+
+ ScRange aRange;
+ sal_uInt16 nRes = aRange.ParseAny(aRangeStr, &rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+ if ((nRes & SCA_VALID) != SCA_VALID)
+ return false;
+
+ if (aRange.aStart.Tab() != aRange.aEnd.Tab())
+ // We don't support multi-sheet range for this.
+ return false;
+
+ const ScMatrix& rCache = mpMOper->GetCache();
+ SCSIZE nC, nR;
+ rCache.GetDimensions(nC, nR);
+ if (!nC || !nR)
+ // cache matrix is empty.
+ return false;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(rUrl);
+ ScExternalRefCache::TableTypeRef xTab = pRefMgr->getCacheTable(nFileId, aSheet, true, NULL);
+ if (!xTab)
+ // cache table creation failed.
+ return false;
+
+ xTab->setWholeTableCached();
+ for (SCSIZE i = 0; i < nR; ++i)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ SCCOL nCol = aRange.aStart.Col() + j;
+ SCROW nRow = aRange.aStart.Row() + i;
+
+ ScMatrixValue aVal = rCache.Get(j, i);
+ switch (aVal.nType)
+ {
+ case SC_MATVAL_BOOLEAN:
+ {
+ bool b = aVal.GetBoolean();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case SC_MATVAL_VALUE:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(aVal.fVal));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case SC_MATVAL_STRING:
+ {
+ const String& rStr = aVal.GetString();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(rStr));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ rFileId = nFileId;
+ rTabName = aSheet;
+ rRange = aRange;
+ return true;
+}
+
bool XclImpExtName::HasFormulaTokens() const
{
return (mxArray.get() != NULL);