From 9f15c9ec6fa129bf17da4cb86209a1ff2c013af7 Mon Sep 17 00:00:00 2001
From: Kohei Yoshida <kohei.yoshida@collabora.com>
Date: Wed, 16 Jul 2014 19:59:35 -0400
Subject: [PATCH 040/137] bnc#885548: Initial work on importing revisions from
xlsx.
Change-Id: Ie0528490d024093cbabf38541fe70be96a9caa2e
(cherry picked from commit b92fdaa1a40bd54d279b4ac2faf19bf15468cc2f)
(cherry picked from commit 6c24bf4cab7e2a9514cf99160a31728835198741)
Signed-off-by: Andras Timar <andras.timar@collabora.com>
---
sc/Library_scfilt.mk | 1 +
sc/inc/chgtrack.hxx | 2 +-
sc/source/filter/inc/revisionfragment.hxx | 79 +++++
sc/source/filter/oox/revisionfragment.cxx | 462 ++++++++++++++++++++++++++++++
sc/source/filter/oox/workbookfragment.cxx | 11 +-
5 files changed, 553 insertions(+), 2 deletions(-)
create mode 100644 sc/source/filter/inc/revisionfragment.hxx
create mode 100644 sc/source/filter/oox/revisionfragment.cxx
diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk
index eb0d5d2..c034e61 100644
--- a/sc/Library_scfilt.mk
+++ b/sc/Library_scfilt.mk
@@ -198,6 +198,7 @@ $(eval $(call gb_Library_add_exception_objects,scfilt,\
sc/source/filter/oox/pivottablefragment \
sc/source/filter/oox/querytablebuffer \
sc/source/filter/oox/querytablefragment \
+ sc/source/filter/oox/revisionfragment \
sc/source/filter/oox/richstringcontext \
sc/source/filter/oox/richstring \
sc/source/filter/oox/scenariobuffer \
diff --git a/sc/inc/chgtrack.hxx b/sc/inc/chgtrack.hxx
index 5fdb50b..1f6ddcd 100644
--- a/sc/inc/chgtrack.hxx
+++ b/sc/inc/chgtrack.hxx
@@ -1101,7 +1101,7 @@ public:
sal_uLong nOldFormat, ScDocument* pRefDoc = NULL );
// after new value was set in the document,
// old value from pOldCell, format from Doc
- void AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell );
+ SC_DLLPUBLIC void AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell );
// after new values were set in the document,
// old values from RefDoc/UndoDoc.
// All contents with a cell in RefDoc
diff --git a/sc/source/filter/inc/revisionfragment.hxx b/sc/source/filter/inc/revisionfragment.hxx
new file mode 100644
index 0000000..473549e
--- /dev/null
+++ b/sc/source/filter/inc/revisionfragment.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SC_OOX_XLS_REVISIONFRAGMENT_HXX
+#define INCLUDED_SC_OOX_XLS_REVISIONFRAGMENT_HXX
+
+#include <excelhandlers.hxx>
+
+class ScChangeTrack;
+
+namespace oox { namespace xls {
+
+class RevisionHeadersFragment : public WorkbookFragmentBase
+{
+ struct Impl;
+ Impl* mpImpl;
+
+public:
+ explicit RevisionHeadersFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath );
+
+ virtual ~RevisionHeadersFragment();
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs ) SAL_OVERRIDE;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE;
+ virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE;
+ virtual void onEndElement() SAL_OVERRIDE;
+
+ virtual void finalizeImport() SAL_OVERRIDE;
+
+private:
+ void importHeaders( const AttributeList& rAttribs );
+ void importHeader( const AttributeList& rAttribs );
+};
+
+class RevisionLogFragment : public WorkbookFragmentBase
+{
+ struct Impl;
+ Impl* mpImpl;
+
+public:
+ explicit RevisionLogFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack );
+
+ virtual ~RevisionLogFragment();
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs ) SAL_OVERRIDE;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE;
+ virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE;
+ virtual void onEndElement() SAL_OVERRIDE;
+
+ virtual void finalizeImport() SAL_OVERRIDE;
+
+private:
+ void importCommon( const AttributeList& rAttribs );
+ void importRcc( const AttributeList& rAttribs );
+ void importRrc( const AttributeList& rAttribs );
+
+ void pushRevision();
+};
+
+}}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/filter/oox/revisionfragment.cxx b/sc/source/filter/oox/revisionfragment.cxx
new file mode 100644
index 0000000..dd8cc4b
--- /dev/null
+++ b/sc/source/filter/oox/revisionfragment.cxx
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <revisionfragment.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/core/fastparser.hxx>
+#include <o3tl/heap_ptr.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <editeng/editobj.hxx>
+
+#include <chgtrack.hxx>
+#include <document.hxx>
+#include <compiler.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <chgviset.hxx>
+#include <richstringcontext.hxx>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <com/sun/star/util/DateTime.hpp>
+
+using namespace com::sun::star;
+
+namespace oox { namespace xls {
+
+namespace {
+
+enum RevisionType
+{
+ REV_UNKNOWN = 0,
+ REV_CELLCHANGE,
+ REV_INSERTROW,
+ REV_DELETEROW,
+ REV_INSERTCOL,
+ REV_DELETECOL
+};
+
+/**
+ * For nc (new cell) or oc (old cell) elements under rcc (cell content
+ * revision).
+ */
+class RCCCellValueContext : public WorkbookContextBase
+{
+ sal_Int32 mnSheetIndex;
+ ScAddress& mrPos;
+ ScCellValue& mrCellValue;
+ sal_Int32 mnType;
+
+ RichStringRef mxRichString;
+
+public:
+ RCCCellValueContext(
+ RevisionLogFragment& rParent, sal_Int32 nSheetIndex, ScAddress& rPos, ScCellValue& rCellValue ) :
+ WorkbookContextBase(rParent),
+ mnSheetIndex(nSheetIndex),
+ mrPos(rPos),
+ mrCellValue(rCellValue),
+ mnType(-1) {}
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) SAL_OVERRIDE
+ {
+ if (nElement == XLS_TOKEN(is))
+ {
+ mxRichString.reset(new RichString(*this));
+ return new RichStringContext(*this, mxRichString);
+ }
+
+ return this;
+ }
+
+ virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(nc):
+ case XLS_TOKEN(oc):
+ importCell(rAttribs);
+ break;
+ default:
+ ;
+ }
+ }
+
+ virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(v):
+ {
+ if (mnType == XML_n || mnType == XML_b)
+ mrCellValue.set(rChars.toDouble());
+ }
+ break;
+ case XLS_TOKEN(t):
+ {
+ if (mnType == XML_inlineStr)
+ {
+ ScDocument& rDoc = getScDocument();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ mrCellValue.set(rPool.intern(rChars));
+ }
+ }
+ break;
+ case XLS_TOKEN(f):
+ {
+ // formula string
+ ScDocument& rDoc = getScDocument();
+ ScCompiler aComp(&rDoc, mrPos);
+ aComp.SetGrammar(formula::FormulaGrammar::GRAM_OOXML);
+ ScTokenArray* pArray = aComp.CompileString(rChars);
+ if (!pArray)
+ break;
+
+ mrCellValue.set(new ScFormulaCell(&rDoc, mrPos, pArray));
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ virtual void onEndElement() SAL_OVERRIDE
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(nc):
+ case XLS_TOKEN(oc):
+ {
+ if (mrCellValue.isEmpty() && mxRichString)
+ {
+ // The value is a rich text string.
+ ScDocument& rDoc = getScDocument();
+ EditTextObject* pTextObj = mxRichString->convert(rDoc.GetEditEngine(), NULL);
+ if (pTextObj)
+ {
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ pTextObj->NormalizeString(rPool);
+ mrCellValue.set(pTextObj);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+private:
+ void importCell( const AttributeList& rAttribs )
+ {
+ mnType = rAttribs.getToken(XML_t, XML_n);
+ OUString aRefStr = rAttribs.getString(XML_r, OUString());
+ if (!aRefStr.isEmpty())
+ {
+ mrPos.Parse(aRefStr, NULL, formula::FormulaGrammar::CONV_XL_OOX);
+ if (mnSheetIndex != -1)
+ mrPos.SetTab(mnSheetIndex-1);
+ }
+ }
+};
+
+struct RevisionMetadata
+{
+ OUString maUserName;
+ DateTime maDateTime;
+
+ RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
+ RevisionMetadata( const RevisionMetadata& r ) :
+ maUserName(r.maUserName), maDateTime(r.maDateTime) {}
+};
+
+}
+
+typedef std::map<OUString, RevisionMetadata> RevDataType;
+
+struct RevisionHeadersFragment::Impl
+{
+ RevDataType maRevData;
+
+ Impl() {}
+};
+
+RevisionHeadersFragment::RevisionHeadersFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase(rHelper, rFragmentPath),
+ mpImpl(new Impl) {}
+
+RevisionHeadersFragment::~RevisionHeadersFragment()
+{
+ delete mpImpl;
+}
+
+oox::core::ContextHandlerRef RevisionHeadersFragment::onCreateContext(
+ sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+ return this;
+}
+
+void RevisionHeadersFragment::onStartElement( const AttributeList& rAttribs )
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(headers):
+ importHeaders(rAttribs);
+ break;
+ case XLS_TOKEN(header):
+ importHeader(rAttribs);
+ break;
+ case XLS_TOKEN(sheetIdMap):
+ break;
+ case XLS_TOKEN(sheetId):
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionHeadersFragment::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(headers):
+ break;
+ case XLS_TOKEN(header):
+ break;
+ case XLS_TOKEN(sheetIdMap):
+ break;
+ case XLS_TOKEN(sheetId):
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionHeadersFragment::finalizeImport()
+{
+ ScDocument& rDoc = getScDocument();
+ o3tl::heap_ptr<ScChangeTrack> pCT(new ScChangeTrack(&rDoc));
+ pCT->SetUseFixDateTime(true);
+
+ const oox::core::Relations& rRels = getRelations();
+ RevDataType::const_iterator it = mpImpl->maRevData.begin(), itEnd = mpImpl->maRevData.end();
+ for (; it != itEnd; ++it)
+ {
+ OUString aPath = rRels.getFragmentPathFromRelId(it->first);
+ if (aPath.isEmpty())
+ continue;
+
+ // Parse each reivison log fragment.
+ const RevisionMetadata& rData = it->second;
+ pCT->SetUser(rData.maUserName);
+ pCT->SetFixDateTimeLocal(rData.maDateTime);
+ boost::scoped_ptr<oox::core::FastParser> xParser(getOoxFilter().createParser());
+ rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
+ importOoxFragment(xFragment, *xParser);
+ }
+
+ rDoc.SetChangeTrack(pCT.release());
+
+ // Turn on visibility of tracked changes.
+ ScChangeViewSettings aSettings;
+ aSettings.SetShowChanges(true);
+ rDoc.SetChangeViewSettings(aSettings);
+}
+
+void RevisionHeadersFragment::importHeaders( const AttributeList& /*rAttribs*/ )
+{
+ // Nothing for now.
+}
+
+void RevisionHeadersFragment::importHeader( const AttributeList& rAttribs )
+{
+ OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
+ if (aRId.isEmpty())
+ // All bets are off if we don't have a relation ID.
+ return;
+
+ RevisionMetadata aMetadata;
+ OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
+ if (!aDateTimeStr.isEmpty())
+ {
+ util::DateTime aDateTime;
+ sax::Converter::parseDateTime(aDateTime, 0, aDateTimeStr);
+ Date aDate(aDateTime.Day, aDateTime.Month, aDateTime.Year);
+ Time aTime(aDateTime.Hours, aDateTime.Minutes, aDateTime.Seconds, aDateTime.NanoSeconds);
+ aMetadata.maDateTime.SetDate(aDate.GetDate());
+ aMetadata.maDateTime.SetTime(aTime.GetTime());
+ }
+
+ aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
+
+ mpImpl->maRevData.insert(RevDataType::value_type(aRId, aMetadata));
+}
+
+struct RevisionLogFragment::Impl
+{
+ ScChangeTrack& mrChangeTrack;
+
+ sal_Int32 mnRevIndex;
+ sal_Int32 mnSheetIndex;
+
+ RevisionType meType;
+
+ // rcc
+ ScAddress maOldCellPos;
+ ScAddress maNewCellPos;
+ ScCellValue maOldCellValue;
+ ScCellValue maNewCellValue;
+
+ // rrc
+ ScRange maRange;
+
+ bool mbEndOfList;
+
+ Impl( ScChangeTrack& rChangeTrack ) :
+ mrChangeTrack(rChangeTrack),
+ mnRevIndex(-1),
+ mnSheetIndex(-1),
+ meType(REV_UNKNOWN),
+ mbEndOfList(false) {}
+};
+
+RevisionLogFragment::RevisionLogFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
+ WorkbookFragmentBase(rHelper, rFragmentPath),
+ mpImpl(new Impl(rChangeTrack)) {}
+
+RevisionLogFragment::~RevisionLogFragment()
+{
+ delete mpImpl;
+}
+
+oox::core::ContextHandlerRef RevisionLogFragment::onCreateContext(
+ sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch (nElement)
+ {
+ case XLS_TOKEN(nc):
+ return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
+ case XLS_TOKEN(oc):
+ return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
+ default:
+ ;
+ }
+ return this;
+}
+
+void RevisionLogFragment::onStartElement( const AttributeList& rAttribs )
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(rcc):
+ mpImpl->maNewCellPos.SetInvalid();
+ mpImpl->maOldCellPos.SetInvalid();
+ mpImpl->maNewCellValue.clear();
+ mpImpl->maOldCellValue.clear();
+ importRcc(rAttribs);
+ break;
+ case XLS_TOKEN(rrc):
+ importRrc(rAttribs);
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionLogFragment::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(rcc):
+ case XLS_TOKEN(rrc):
+ pushRevision();
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionLogFragment::finalizeImport() {}
+
+void RevisionLogFragment::importCommon( const AttributeList& rAttribs )
+{
+ mpImpl->mnRevIndex = rAttribs.getInteger(XML_rId, -1);
+ mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
+}
+
+void RevisionLogFragment::importRcc( const AttributeList& rAttribs )
+{
+ importCommon(rAttribs);
+
+ mpImpl->meType = REV_CELLCHANGE;
+}
+
+void RevisionLogFragment::importRrc( const AttributeList& rAttribs )
+{
+ importCommon(rAttribs);
+
+ if (mpImpl->mnSheetIndex == -1)
+ // invalid sheet index, or sheet index not given.
+ return;
+
+ mpImpl->meType = REV_UNKNOWN;
+ sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
+ if (nAction == -1)
+ return;
+
+ OUString aRefStr = rAttribs.getString(XML_ref, OUString());
+ mpImpl->maRange.Parse(aRefStr, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
+ if (!mpImpl->maRange.IsValid())
+ return;
+
+ switch (nAction)
+ {
+ case XML_insertRow:
+ mpImpl->meType = REV_INSERTROW;
+ mpImpl->maRange.aEnd.SetCol(MAXCOL);
+ mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
+ mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
+ break;
+ default:
+ // Unknown action type. Ignore it.
+ return;
+ }
+
+ mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
+}
+
+void RevisionLogFragment::pushRevision()
+{
+ switch (mpImpl->meType)
+ {
+ case REV_CELLCHANGE:
+ mpImpl->mrChangeTrack.AppendContentOnTheFly(
+ mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
+ break;
+ case REV_INSERTROW:
+ mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
+ break;
+ default:
+ ;
+ }
+}
+
+}}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbookfragment.cxx b/sc/source/filter/oox/workbookfragment.cxx
index bf42f46..283aa74 100644
--- a/sc/source/filter/oox/workbookfragment.cxx
+++ b/sc/source/filter/oox/workbookfragment.cxx
@@ -36,6 +36,7 @@
#include "pivotcachebuffer.hxx"
#include "sharedstringsbuffer.hxx"
#include "sharedstringsfragment.hxx"
+#include "revisionfragment.hxx"
#include "stylesfragment.hxx"
#include "tablebuffer.hxx"
#include "themebuffer.hxx"
@@ -300,7 +301,7 @@ public:
}
};
-static void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
+void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
{
sal_Int32 nThreads = std::min( rSheets.size(), (size_t) 4 /* FIXME: ncpus/2 */ );
@@ -499,6 +500,14 @@ void WorkbookFragment::finalizeImport()
// final conversions, e.g. calculation settings and view settings
finalizeWorkbookImport();
+
+ OUString aRevHeadersPath = getFragmentPathFromFirstType(CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
+ if (!aRevHeadersPath.isEmpty())
+ {
+ boost::scoped_ptr<oox::core::FastParser> xParser(getOoxFilter().createParser());
+ rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionHeadersFragment(*this, aRevHeadersPath));
+ importOoxFragment(xFragment, *xParser);
+ }
}
// private --------------------------------------------------------------------
--
1.9.3