Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / oox / revisionfragment.cxx
blob071b8f4717e4404235bcc633c9773c2b7cad8937
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <sal/config.h>
12 #include <memory>
14 #include <revisionfragment.hxx>
15 #include <oox/core/relations.hxx>
16 #include <oox/core/xmlfilterbase.hxx>
17 #include <oox/core/fastparser.hxx>
18 #include <oox/helper/attributelist.hxx>
19 #include <oox/token/namespaces.hxx>
20 #include <oox/token/tokens.hxx>
21 #include <svl/sharedstringpool.hxx>
22 #include <sax/tools/converter.hxx>
23 #include <editeng/editobj.hxx>
25 #include <chgtrack.hxx>
26 #include <document.hxx>
27 #include <compiler.hxx>
28 #include <editutil.hxx>
29 #include <formulacell.hxx>
30 #include <chgviset.hxx>
31 #include <richstringcontext.hxx>
32 #include <tokenarray.hxx>
34 #include <com/sun/star/util/DateTime.hpp>
36 using namespace com::sun::star;
38 namespace oox { namespace xls {
40 namespace {
42 enum RevisionType
44 REV_UNKNOWN = 0,
45 REV_CELLCHANGE,
46 REV_INSERTROW
49 /**
50 * For nc (new cell) or oc (old cell) elements under rcc (cell content
51 * revision).
53 class RCCCellValueContext : public WorkbookContextBase
55 sal_Int32 const mnSheetIndex;
56 ScAddress& mrPos;
57 ScCellValue& mrCellValue;
58 sal_Int32 mnType;
60 RichStringRef mxRichString;
62 public:
63 RCCCellValueContext(
64 RevisionLogFragment& rParent, sal_Int32 nSheetIndex, ScAddress& rPos, ScCellValue& rCellValue ) :
65 WorkbookContextBase(rParent),
66 mnSheetIndex(nSheetIndex),
67 mrPos(rPos),
68 mrCellValue(rCellValue),
69 mnType(-1) {}
71 protected:
72 virtual oox::core::ContextHandlerRef onCreateContext(
73 sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) override
75 if (nElement == XLS_TOKEN(is))
77 mxRichString.reset(new RichString(*this));
78 return new RichStringContext(*this, mxRichString);
81 return this;
84 virtual void onStartElement( const AttributeList& rAttribs ) override
86 switch (getCurrentElement())
88 case XLS_TOKEN(nc):
89 case XLS_TOKEN(oc):
90 importCell(rAttribs);
91 break;
92 default:
97 virtual void onCharacters( const OUString& rChars ) override
99 switch (getCurrentElement())
101 case XLS_TOKEN(v):
103 if (mnType == XML_n || mnType == XML_b)
104 mrCellValue.set(rChars.toDouble());
106 break;
107 case XLS_TOKEN(t):
109 if (mnType == XML_inlineStr)
111 ScDocument& rDoc = getScDocument();
112 svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
113 mrCellValue.set(rPool.intern(rChars));
116 break;
117 case XLS_TOKEN(f):
119 // formula string
120 ScDocument& rDoc = getScDocument();
121 ScCompiler aComp(&rDoc, mrPos, formula::FormulaGrammar::GRAM_OOXML);
122 std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(rChars);
123 if (!pArray)
124 break;
126 mrCellValue.set(new ScFormulaCell(&rDoc, mrPos, std::move(pArray)));
128 break;
129 default:
134 virtual void onEndElement() override
136 switch (getCurrentElement())
138 case XLS_TOKEN(nc):
139 case XLS_TOKEN(oc):
141 if (mrCellValue.isEmpty() && mxRichString)
143 // The value is a rich text string.
144 ScDocument& rDoc = getScDocument();
145 std::unique_ptr<EditTextObject> pTextObj = mxRichString->convert(rDoc.GetEditEngine(), nullptr);
146 if (pTextObj)
148 svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
149 pTextObj->NormalizeString(rPool);
150 mrCellValue.set(pTextObj.release());
154 break;
155 default:
160 private:
161 void importCell( const AttributeList& rAttribs )
163 mnType = rAttribs.getToken(XML_t, XML_n);
164 OUString aRefStr = rAttribs.getString(XML_r, OUString());
165 if (!aRefStr.isEmpty())
167 mrPos.Parse(aRefStr, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
168 if (mnSheetIndex != -1)
169 mrPos.SetTab(mnSheetIndex-1);
174 struct RevisionMetadata
176 OUString maUserName;
177 DateTime maDateTime;
179 RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
180 RevisionMetadata( const RevisionMetadata& r ) :
181 maUserName(r.maUserName), maDateTime(r.maDateTime) {}
186 typedef std::map<OUString, RevisionMetadata> RevDataType;
188 struct RevisionHeadersFragment::Impl
190 RevDataType maRevData;
192 Impl() {}
195 RevisionHeadersFragment::RevisionHeadersFragment(
196 const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
197 WorkbookFragmentBase(rHelper, rFragmentPath),
198 mpImpl(new Impl) {}
200 RevisionHeadersFragment::~RevisionHeadersFragment()
204 oox::core::ContextHandlerRef RevisionHeadersFragment::onCreateContext(
205 sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
207 return this;
210 void RevisionHeadersFragment::onStartElement( const AttributeList& rAttribs )
212 switch (getCurrentElement())
214 case XLS_TOKEN(headers):
215 break;
216 case XLS_TOKEN(header):
217 importHeader(rAttribs);
218 break;
219 case XLS_TOKEN(sheetIdMap):
220 break;
221 case XLS_TOKEN(sheetId):
222 break;
223 default:
228 void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
230 void RevisionHeadersFragment::onEndElement()
232 switch (getCurrentElement())
234 case XLS_TOKEN(headers):
235 break;
236 case XLS_TOKEN(header):
237 break;
238 case XLS_TOKEN(sheetIdMap):
239 break;
240 case XLS_TOKEN(sheetId):
241 break;
242 default:
247 void RevisionHeadersFragment::finalizeImport()
249 ScDocument& rDoc = getScDocument();
250 std::unique_ptr<ScChangeTrack> pCT(new ScChangeTrack(&rDoc));
251 OUString aSelfUser = pCT->GetUser(); // owner of this document.
252 pCT->SetUseFixDateTime(true);
254 const oox::core::Relations& rRels = getRelations();
255 for (const auto& [rRelId, rData] : mpImpl->maRevData)
257 OUString aPath = rRels.getFragmentPathFromRelId(rRelId);
258 if (aPath.isEmpty())
259 continue;
261 // Parse each revision log fragment.
262 pCT->SetUser(rData.maUserName);
263 pCT->SetFixDateTimeLocal(rData.maDateTime);
264 std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
265 rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
266 importOoxFragment(xFragment, *xParser);
269 pCT->SetUser(aSelfUser); // set the default user to the document owner.
270 pCT->SetUseFixDateTime(false);
271 rDoc.SetChangeTrack(std::move(pCT));
273 // Turn on visibility of tracked changes.
274 ScChangeViewSettings aSettings;
275 aSettings.SetShowChanges(true);
276 rDoc.SetChangeViewSettings(aSettings);
279 void RevisionHeadersFragment::importHeader( const AttributeList& rAttribs )
281 OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
282 if (aRId.isEmpty())
283 // All bets are off if we don't have a relation ID.
284 return;
286 RevisionMetadata aMetadata;
287 OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
288 if (!aDateTimeStr.isEmpty())
290 util::DateTime aDateTime;
291 sax::Converter::parseDateTime(aDateTime, aDateTimeStr);
292 Date aDate(aDateTime);
293 tools::Time aTime(aDateTime);
294 aMetadata.maDateTime.SetDate(aDate.GetDate());
295 aMetadata.maDateTime.SetTime(aTime.GetTime());
298 aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
300 mpImpl->maRevData.emplace(aRId, aMetadata);
303 struct RevisionLogFragment::Impl
305 ScChangeTrack& mrChangeTrack;
307 sal_Int32 mnSheetIndex;
309 RevisionType meType;
311 // rcc
312 ScAddress maOldCellPos;
313 ScAddress maNewCellPos;
314 ScCellValue maOldCellValue;
315 ScCellValue maNewCellValue;
317 // rrc
318 ScRange maRange;
320 bool mbEndOfList;
322 explicit Impl( ScChangeTrack& rChangeTrack ) :
323 mrChangeTrack(rChangeTrack),
324 mnSheetIndex(-1),
325 meType(REV_UNKNOWN),
326 mbEndOfList(false) {}
329 RevisionLogFragment::RevisionLogFragment(
330 const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
331 WorkbookFragmentBase(rHelper, rFragmentPath),
332 mpImpl(new Impl(rChangeTrack)) {}
334 RevisionLogFragment::~RevisionLogFragment()
338 oox::core::ContextHandlerRef RevisionLogFragment::onCreateContext(
339 sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
341 switch (nElement)
343 case XLS_TOKEN(nc):
344 return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
345 case XLS_TOKEN(oc):
346 return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
347 default:
350 return this;
353 void RevisionLogFragment::onStartElement( const AttributeList& rAttribs )
355 switch (getCurrentElement())
357 case XLS_TOKEN(rcc):
358 mpImpl->maNewCellPos.SetInvalid();
359 mpImpl->maOldCellPos.SetInvalid();
360 mpImpl->maNewCellValue.clear();
361 mpImpl->maOldCellValue.clear();
362 importRcc(rAttribs);
363 break;
364 case XLS_TOKEN(rrc):
365 importRrc(rAttribs);
366 break;
367 default:
372 void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
374 void RevisionLogFragment::onEndElement()
376 switch (getCurrentElement())
378 case XLS_TOKEN(rcc):
379 case XLS_TOKEN(rrc):
380 pushRevision();
381 break;
382 default:
387 void RevisionLogFragment::finalizeImport() {}
389 void RevisionLogFragment::importCommon( const AttributeList& rAttribs )
391 mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
394 void RevisionLogFragment::importRcc( const AttributeList& rAttribs )
396 importCommon(rAttribs);
398 mpImpl->meType = REV_CELLCHANGE;
401 void RevisionLogFragment::importRrc( const AttributeList& rAttribs )
403 importCommon(rAttribs);
405 if (mpImpl->mnSheetIndex == -1)
406 // invalid sheet index, or sheet index not given.
407 return;
409 mpImpl->meType = REV_UNKNOWN;
410 sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
411 if (nAction == -1)
412 return;
414 OUString aRefStr = rAttribs.getString(XML_ref, OUString());
415 mpImpl->maRange.Parse(aRefStr, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
416 if (!mpImpl->maRange.IsValid())
417 return;
419 switch (nAction)
421 case XML_insertRow:
422 mpImpl->meType = REV_INSERTROW;
423 mpImpl->maRange.aEnd.SetCol(getScDocument().MaxCol());
424 mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
425 mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
426 break;
427 default:
428 // Unknown action type. Ignore it.
429 return;
432 mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
435 void RevisionLogFragment::pushRevision()
437 switch (mpImpl->meType)
439 case REV_CELLCHANGE:
440 mpImpl->mrChangeTrack.AppendContentOnTheFly(
441 mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
442 break;
443 case REV_INSERTROW:
444 mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
445 break;
446 default:
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */