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>
15 #include <oox/core/relations.hxx>
17 #include <oox/core/fastparser.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>
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 
36 using namespace com::sun::star;
37 
38 namespace oox::xls {
39 
40 namespace {
41 
42 enum RevisionType
43 {
44  REV_UNKNOWN = 0,
45  REV_CELLCHANGE,
46  REV_INSERTROW
47 };
48 
53 class RCCCellValueContext : public WorkbookContextBase
54 {
55  sal_Int32 mnSheetIndex;
58  sal_Int32 mnType;
59 
61 
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) {}
70 
71 protected:
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>(*this);
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  {
141  if (mrCellValue.isEmpty() && mxRichString)
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(pTextObj.release());
151  }
152  }
153  }
154  break;
155  default:
156  ;
157  }
158  }
159 
160 private:
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)
169  mrPos.SetTab(mnSheetIndex-1);
170  }
171  }
172 };
173 
174 struct RevisionMetadata
175 {
176  OUString maUserName;
178 
179  RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
180  RevisionMetadata( const RevisionMetadata& r ) :
181  maUserName(r.maUserName), maDateTime(r.maDateTime) {}
182 };
183 
184 }
185 
187 {
188  std::map<OUString, RevisionMetadata> maRevData;
189 
190  Impl() {}
191 };
192 
193 RevisionHeadersFragment::RevisionHeadersFragment(
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 
226 void 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  sax::Converter::parseDateTime(aDateTime, aDateTimeStr);
290  Date aDate(aDateTime);
291  tools::Time aTime(aDateTime);
292  aMetadata.maDateTime.SetDate(aDate.GetDate());
293  aMetadata.maDateTime.SetTime(aTime.GetTime());
294  }
295 
296  aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
297 
298  mpImpl->maRevData.emplace(aRId, aMetadata);
299 }
300 
302 {
304 
305  sal_Int32 mnSheetIndex;
306 
307  RevisionType meType;
308 
309  // rcc
314 
315  // rrc
317 
319 
320  explicit Impl( ScChangeTrack& rChangeTrack ) :
321  mrChangeTrack(rChangeTrack),
322  mnSheetIndex(-1),
323  meType(REV_UNKNOWN),
324  mbEndOfList(false) {}
325 };
326 
328  const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
329  WorkbookFragmentBase(rHelper, rFragmentPath),
330  mpImpl(new Impl(rChangeTrack)) {}
331 
333 {
334 }
335 
337  sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
338 {
339  switch (nElement)
340  {
341  case XLS_TOKEN(nc):
342  return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
343  case XLS_TOKEN(oc):
344  return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
345  default:
346  ;
347  }
348  return this;
349 }
350 
352 {
353  switch (getCurrentElement())
354  {
355  case XLS_TOKEN(rcc):
356  mpImpl->maNewCellPos.SetInvalid();
357  mpImpl->maOldCellPos.SetInvalid();
358  mpImpl->maNewCellValue.clear();
359  mpImpl->maOldCellValue.clear();
360  importRcc(rAttribs);
361  break;
362  case XLS_TOKEN(rrc):
363  importRrc(rAttribs);
364  break;
365  default:
366  ;
367  }
368 }
369 
370 void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
371 
373 {
374  switch (getCurrentElement())
375  {
376  case XLS_TOKEN(rcc):
377  case XLS_TOKEN(rrc):
378  pushRevision();
379  break;
380  default:
381  ;
382  }
383 }
384 
386 
388 {
389  mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
390 }
391 
393 {
394  importCommon(rAttribs);
395 
396  mpImpl->meType = REV_CELLCHANGE;
397 }
398 
400 {
401  importCommon(rAttribs);
402 
403  if (mpImpl->mnSheetIndex == -1)
404  // invalid sheet index, or sheet index not given.
405  return;
406 
407  mpImpl->meType = REV_UNKNOWN;
408  sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
409  if (nAction == -1)
410  return;
411 
412  OUString aRefStr = rAttribs.getString(XML_ref, OUString());
413  mpImpl->maRange.Parse(aRefStr, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
414  if (!mpImpl->maRange.IsValid())
415  return;
416 
417  switch (nAction)
418  {
419  case XML_insertRow:
420  mpImpl->meType = REV_INSERTROW;
421  mpImpl->maRange.aEnd.SetCol(getScDocument().MaxCol());
422  mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
423  mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
424  break;
425  default:
426  // Unknown action type. Ignore it.
427  return;
428  }
429 
430  mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
431 }
432 
434 {
435  switch (mpImpl->meType)
436  {
437  case REV_CELLCHANGE:
438  mpImpl->mrChangeTrack.AppendContentOnTheFly(
439  mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
440  break;
441  case REV_INSERTROW:
442  mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
443  break;
444  default:
445  ;
446  }
447 }
448 
449 }
450 
451 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Helper class to provide access to global workbook data.
void set(double fValue)
Definition: cellvalue.cxx:295
void importRrc(const AttributeList &rAttribs)
SharedString intern(const OUString &rStr)
void importRcc(const AttributeList &rAttribs)
virtual void onEndElement() override
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:562
ScCellValue & mrCellValue
OptValue< bool > getBool(sal_Int32 nAttrToken) const
SC_DLLPUBLIC void SetChangeViewSettings(const ScChangeViewSettings &rNew)
Definition: documen2.cxx:1108
static bool parseDateTime(css::util::DateTime &rDateTime, std::u16string_view rString)
Impl(ScChangeTrack &rChangeTrack)
OptValue< sal_Int32 > getInteger(sal_Int32 nAttrToken) const
const ContentProperties & rData
OptValue< OUString > getString(sal_Int32 nAttrToken) const
virtual void onStartElement(const AttributeList &rAttribs) override
ScAddress & mrPos
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...
Store arbitrary cell value of any kind.
Definition: cellvalue.hxx:36
virtual oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, const AttributeList &rAttribs) override
std::shared_ptr< RichString > RichStringRef
Definition: richstring.hxx:261
Fragment handler derived from the WorkbookHelper helper class.
const sal_Int8 header[]
virtual void onCharacters(const OUString &rChars) override
sal_Int32 GetDate() const
virtual void finalizeImport() override
void SetTab(SCTAB nTabP)
Definition: address.hxx:283
sal_Int32 mnType
DateTime maDateTime
std::map< OUString, RevisionMetadata > maRevData
sal_Int32 getCurrentElement() const
virtual oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, const AttributeList &rAttribs) override
sal_Int32 mnSheetIndex
RevisionLogFragment(const WorkbookHelper &rHelper, const OUString &rFragmentPath, ScChangeTrack &rChangeTrack)
virtual void finalizeImport() override
virtual void onCharacters(const OUString &rChars) override
OUString getFragmentPathFromRelId(const OUString &rRelId) const
virtual void onEndElement() override
bool isEmpty() const
Definition: cellvalue.cxx:499
RichStringRef mxRichString
virtual ~RevisionLogFragment() override
virtual void onStartElement(const AttributeList &rAttribs) override
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:271
void importHeader(const AttributeList &rAttribs)
sal_Int64 GetTime() const
std::unique_ptr< Impl > mpImpl
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:1548
static FastParser * createParser()
OUString maUserName
SC_DLLPUBLIC ScFieldEditEngine & GetEditEngine()
Definition: documen2.cxx:453
OptValue< sal_Int32 > getToken(sal_Int32 nAttrToken) const
void importCommon(const AttributeList &rAttribs)
void SetShowChanges(bool bFlag)
Definition: chgviset.hxx:79