From 9f15c9ec6fa129bf17da4cb86209a1ff2c013af7 Mon Sep 17 00:00:00 2001 From: Kohei Yoshida 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 --- 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 + +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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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 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 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 xParser(getOoxFilter().createParser()); + rtl::Reference 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 xParser(getOoxFilter().createParser()); + rtl::Reference xFragment(new RevisionHeadersFragment(*this, aRevHeadersPath)); + importOoxFragment(xFragment, *xParser); + } } // private -------------------------------------------------------------------- -- 1.9.3