LibreOffice Module sc (master) 1
revisionfragment.cxx
Go to the documentation of this file.
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
10#include <sal/config.h>
11
12#include <memory>
13
14#include <revisionfragment.hxx>
19#include <oox/token/namespaces.hxx>
20#include <oox/token/tokens.hxx>
23#include <editeng/editobj.hxx>
24
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>
33
34#include <com/sun/star/util/DateTime.hpp>
35
36using namespace com::sun::star;
37
38namespace oox::xls {
39
40namespace {
41
42enum RevisionType
43{
44 REV_UNKNOWN = 0,
45 REV_CELLCHANGE,
46 REV_INSERTROW
47};
48
53class RCCCellValueContext : public WorkbookContextBase
54{
55 sal_Int32 mnSheetIndex;
58 sal_Int32 mnType;
59
61
62public:
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) {}
70
71protected:
72 virtual oox::core::ContextHandlerRef onCreateContext(
73 sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) override
74 {
75 if (nElement == XLS_TOKEN(is))
76 {
77 mxRichString = std::make_shared<RichString>();
78 return new RichStringContext(*this, mxRichString);
79 }
80
81 return this;
82 }
83
84 virtual void onStartElement( const AttributeList& rAttribs ) override
85 {
86 switch (getCurrentElement())
87 {
88 case XLS_TOKEN(nc):
89 case XLS_TOKEN(oc):
90 importCell(rAttribs);
91 break;
92 default:
93 ;
94 }
95 }
96
97 virtual void onCharacters( const OUString& rChars ) override
98 {
99 switch (getCurrentElement())
100 {
101 case XLS_TOKEN(v):
102 {
103 if (mnType == XML_n || mnType == XML_b)
104 mrCellValue.set(rChars.toDouble());
105 }
106 break;
107 case XLS_TOKEN(t):
108 {
109 if (mnType == XML_inlineStr)
110 {
111 ScDocument& rDoc = getScDocument();
113 mrCellValue.set(rPool.intern(rChars));
114 }
115 }
116 break;
117 case XLS_TOKEN(f):
118 {
119 // formula string
120 ScDocument& rDoc = getScDocument();
122 std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(rChars);
123 if (!pArray)
124 break;
125
126 mrCellValue.set(new ScFormulaCell(rDoc, mrPos, std::move(pArray)));
127 }
128 break;
129 default:
130 ;
131 }
132 }
133
134 virtual void onEndElement() override
135 {
136 switch (getCurrentElement())
137 {
138 case XLS_TOKEN(nc):
139 case XLS_TOKEN(oc):
140 {
142 {
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)
147 {
149 pTextObj->NormalizeString(rPool);
150 mrCellValue.set(std::move(pTextObj));
151 }
152 }
153 }
154 break;
155 default:
156 ;
157 }
158 }
159
160private:
161 void importCell( const AttributeList& rAttribs )
162 {
163 mnType = rAttribs.getToken(XML_t, XML_n);
164 OUString aRefStr = rAttribs.getString(XML_r, OUString());
165 if (!aRefStr.isEmpty())
166 {
167 mrPos.Parse(aRefStr, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
168 if (mnSheetIndex != -1)
170 }
171 }
172};
173
174struct RevisionMetadata
175{
176 OUString maUserName;
178
179 RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
180 RevisionMetadata( const RevisionMetadata& r ) :
182};
183
184}
185
187{
188 std::map<OUString, RevisionMetadata> maRevData;
189
190 Impl() {}
191};
192
194 const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
195 WorkbookFragmentBase(rHelper, rFragmentPath),
196 mpImpl(new Impl) {}
197
199{
200}
201
203 sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
204{
205 return this;
206}
207
209{
210 switch (getCurrentElement())
211 {
212 case XLS_TOKEN(headers):
213 break;
214 case XLS_TOKEN(header):
215 importHeader(rAttribs);
216 break;
217 case XLS_TOKEN(sheetIdMap):
218 break;
219 case XLS_TOKEN(sheetId):
220 break;
221 default:
222 ;
223 }
224}
225
226void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
227
229{
230 switch (getCurrentElement())
231 {
232 case XLS_TOKEN(headers):
233 break;
234 case XLS_TOKEN(header):
235 break;
236 case XLS_TOKEN(sheetIdMap):
237 break;
238 case XLS_TOKEN(sheetId):
239 break;
240 default:
241 ;
242 }
243}
244
246{
247 ScDocument& rDoc = getScDocument();
248 std::unique_ptr<ScChangeTrack> pCT(new ScChangeTrack(rDoc));
249 OUString aSelfUser = pCT->GetUser(); // owner of this document.
250 pCT->SetUseFixDateTime(true);
251
252 const oox::core::Relations& rRels = getRelations();
253 for (const auto& [rRelId, rData] : mpImpl->maRevData)
254 {
255 OUString aPath = rRels.getFragmentPathFromRelId(rRelId);
256 if (aPath.isEmpty())
257 continue;
258
259 // Parse each revision log fragment.
260 pCT->SetUser(rData.maUserName);
261 pCT->SetFixDateTimeLocal(rData.maDateTime);
262 std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
263 rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
264 importOoxFragment(xFragment, *xParser);
265 }
266
267 pCT->SetUser(aSelfUser); // set the default user to the document owner.
268 pCT->SetUseFixDateTime(false);
269 rDoc.SetChangeTrack(std::move(pCT));
270
271 // Turn on visibility of tracked changes.
272 ScChangeViewSettings aSettings;
273 aSettings.SetShowChanges(true);
274 rDoc.SetChangeViewSettings(aSettings);
275}
276
278{
279 OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
280 if (aRId.isEmpty())
281 // All bets are off if we don't have a relation ID.
282 return;
283
284 RevisionMetadata aMetadata;
285 OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
286 if (!aDateTimeStr.isEmpty())
287 {
288 util::DateTime aDateTime;
289 if (sax::Converter::parseDateTime(aDateTime, aDateTimeStr))
290 aMetadata.maDateTime = aDateTime;
291 else
292 SAL_WARN("sc.filter", "RevisionHeadersFragment: broken DateTime '" << aDateTimeStr << "'");
293 }
294
295 aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
296
297 mpImpl->maRevData.emplace(aRId, aMetadata);
298}
299
301{
303
304 sal_Int32 mnSheetIndex;
305
306 RevisionType meType;
307
308 // rcc
313
314 // rrc
316
318
319 explicit Impl( ScChangeTrack& rChangeTrack ) :
320 mrChangeTrack(rChangeTrack),
321 mnSheetIndex(-1),
322 meType(REV_UNKNOWN),
323 mbEndOfList(false) {}
324};
325
327 const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
328 WorkbookFragmentBase(rHelper, rFragmentPath),
329 mpImpl(new Impl(rChangeTrack)) {}
330
332{
333}
334
336 sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
337{
338 switch (nElement)
339 {
340 case XLS_TOKEN(nc):
341 return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
342 case XLS_TOKEN(oc):
343 return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
344 default:
345 ;
346 }
347 return this;
348}
349
351{
352 switch (getCurrentElement())
353 {
354 case XLS_TOKEN(rcc):
355 mpImpl->maNewCellPos.SetInvalid();
356 mpImpl->maOldCellPos.SetInvalid();
357 mpImpl->maNewCellValue.clear();
358 mpImpl->maOldCellValue.clear();
359 importRcc(rAttribs);
360 break;
361 case XLS_TOKEN(rrc):
362 importRrc(rAttribs);
363 break;
364 default:
365 ;
366 }
367}
368
369void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
370
372{
373 switch (getCurrentElement())
374 {
375 case XLS_TOKEN(rcc):
376 case XLS_TOKEN(rrc):
377 pushRevision();
378 break;
379 default:
380 ;
381 }
382}
383
385
387{
388 mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
389}
390
392{
393 importCommon(rAttribs);
394
395 mpImpl->meType = REV_CELLCHANGE;
396}
397
399{
400 importCommon(rAttribs);
401
402 if (mpImpl->mnSheetIndex == -1)
403 // invalid sheet index, or sheet index not given.
404 return;
405
406 mpImpl->meType = REV_UNKNOWN;
407 sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
408 if (nAction == -1)
409 return;
410
411 OUString aRefStr = rAttribs.getString(XML_ref, OUString());
413 if (!mpImpl->maRange.IsValid())
414 return;
415
416 switch (nAction)
417 {
418 case XML_insertRow:
419 mpImpl->meType = REV_INSERTROW;
420 mpImpl->maRange.aEnd.SetCol(getScDocument().MaxCol());
421 mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
422 mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
423 break;
424 default:
425 // Unknown action type. Ignore it.
426 return;
427 }
428
429 mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
430}
431
433{
434 switch (mpImpl->meType)
435 {
436 case REV_CELLCHANGE:
437 mpImpl->mrChangeTrack.AppendContentOnTheFly(
438 mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
439 break;
440 case REV_INSERTROW:
441 mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
442 break;
443 default:
444 ;
445 }
446}
447
448}
449
450/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr sal_Int8 header[]
SC_DLLPUBLIC ScRefFlags Parse(const OUString &, const ScDocument &, const Details &rDetails=detailsOOOa1, ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, sal_Int32 *pSheetEndPos=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1537
void SetTab(SCTAB nTabP)
Definition: address.hxx:295
void SetShowChanges(bool bFlag)
Definition: chgviset.hxx:78
SC_DLLPUBLIC void SetChangeTrack(std::unique_ptr< ScChangeTrack > pTrack)
only for import filter, deletes any existing ChangeTrack via EndChangeTracking() and takes ownership ...
Definition: documen2.cxx:288
SC_DLLPUBLIC ScFieldEditEngine & GetEditEngine()
Definition: documen2.cxx:477
SC_DLLPUBLIC void SetChangeViewSettings(const ScChangeViewSettings &rNew)
Definition: documen2.cxx:1136
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:586
std::optional< sal_Int32 > getInteger(sal_Int32 nAttrToken) const
std::optional< OUString > getString(sal_Int32 nAttrToken) const
std::optional< bool > getBool(sal_Int32 nAttrToken) const
std::optional< sal_Int32 > getToken(sal_Int32 nAttrToken) const
sal_Int32 getCurrentElement() const
OUString getFragmentPathFromRelId(const OUString &rRelId) const
static FastParser * createParser()
void importHeader(const AttributeList &rAttribs)
RevisionHeadersFragment(const WorkbookHelper &rHelper, const OUString &rFragmentPath)
virtual void onCharacters(const OUString &rChars) override
virtual void onEndElement() override
virtual void onStartElement(const AttributeList &rAttribs) override
virtual void finalizeImport() override
virtual oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, const AttributeList &rAttribs) override
virtual ~RevisionLogFragment() override
void importCommon(const AttributeList &rAttribs)
virtual oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, const AttributeList &rAttribs) override
virtual void finalizeImport() override
std::unique_ptr< Impl > mpImpl
void importRcc(const AttributeList &rAttribs)
RevisionLogFragment(const WorkbookHelper &rHelper, const OUString &rFragmentPath, ScChangeTrack &rChangeTrack)
virtual void onEndElement() override
virtual void onCharacters(const OUString &rChars) override
void importRrc(const AttributeList &rAttribs)
virtual void onStartElement(const AttributeList &rAttribs) override
Fragment handler derived from the WorkbookHelper helper class.
Helper class to provide access to global workbook data.
bool importOoxFragment(const rtl::Reference< oox::core::FragmentHandler > &rxHandler)
Imports a fragment using the passed fragment handler, which contains the full path to the fragment st...
static bool parseDateTime(css::util::DateTime &rDateTime, std::u16string_view rString)
SharedString intern(const OUString &rStr)
#define SAL_WARN(area, stream)
std::shared_ptr< RichString > RichStringRef
Definition: richstring.hxx:258
constexpr OUStringLiteral EMPTY
sal_Int32 mnSheetIndex
ScCellValue & mrCellValue
sal_Int32 mnType
OUString maUserName
DateTime maDateTime
ScAddress & mrPos
RichStringRef mxRichString
Store arbitrary cell value of any kind.
Definition: cellvalue.hxx:32
bool isEmpty() const
Definition: cellvalue.cxx:519
void set(double fValue)
Definition: cellvalue.cxx:329
std::map< OUString, RevisionMetadata > maRevData
Impl(ScChangeTrack &rChangeTrack)