Line data Source code
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>
16 : #include <oox/core/xmlfilterbase.hxx>
17 : #include <oox/core/fastparser.hxx>
18 : #include <svl/sharedstringpool.hxx>
19 : #include <sax/tools/converter.hxx>
20 : #include <editeng/editobj.hxx>
21 :
22 : #include <chgtrack.hxx>
23 : #include <document.hxx>
24 : #include <compiler.hxx>
25 : #include <editutil.hxx>
26 : #include <formulacell.hxx>
27 : #include <chgviset.hxx>
28 : #include <richstringcontext.hxx>
29 :
30 : #include <boost/scoped_ptr.hpp>
31 :
32 : #include <com/sun/star/util/DateTime.hpp>
33 :
34 : using namespace com::sun::star;
35 :
36 : namespace oox { namespace xls {
37 :
38 : namespace {
39 :
40 : enum RevisionType
41 : {
42 : REV_UNKNOWN = 0,
43 : REV_CELLCHANGE,
44 : REV_INSERTROW,
45 : REV_DELETEROW,
46 : REV_INSERTCOL,
47 : REV_DELETECOL
48 : };
49 :
50 : /**
51 : * For nc (new cell) or oc (old cell) elements under rcc (cell content
52 : * revision).
53 : */
54 56 : class RCCCellValueContext : public WorkbookContextBase
55 : {
56 : sal_Int32 mnSheetIndex;
57 : ScAddress& mrPos;
58 : ScCellValue& mrCellValue;
59 : sal_Int32 mnType;
60 :
61 : RichStringRef mxRichString;
62 :
63 : public:
64 28 : RCCCellValueContext(
65 : RevisionLogFragment& rParent, sal_Int32 nSheetIndex, ScAddress& rPos, ScCellValue& rCellValue ) :
66 : WorkbookContextBase(rParent),
67 : mnSheetIndex(nSheetIndex),
68 : mrPos(rPos),
69 : mrCellValue(rCellValue),
70 28 : mnType(-1) {}
71 :
72 : protected:
73 28 : virtual oox::core::ContextHandlerRef onCreateContext(
74 : sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) SAL_OVERRIDE
75 : {
76 28 : if (nElement == XLS_TOKEN(is))
77 : {
78 4 : mxRichString.reset(new RichString(*this));
79 4 : return new RichStringContext(*this, mxRichString);
80 : }
81 :
82 24 : return this;
83 : }
84 :
85 52 : virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE
86 : {
87 52 : switch (getCurrentElement())
88 : {
89 : case XLS_TOKEN(nc):
90 : case XLS_TOKEN(oc):
91 28 : importCell(rAttribs);
92 28 : break;
93 : default:
94 : ;
95 : }
96 52 : }
97 :
98 24 : virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE
99 : {
100 24 : switch (getCurrentElement())
101 : {
102 : case XLS_TOKEN(v):
103 : {
104 20 : if (mnType == XML_n || mnType == XML_b)
105 20 : mrCellValue.set(rChars.toDouble());
106 : }
107 20 : break;
108 : case XLS_TOKEN(t):
109 : {
110 0 : if (mnType == XML_inlineStr)
111 : {
112 0 : ScDocument& rDoc = getScDocument();
113 0 : svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
114 0 : mrCellValue.set(rPool.intern(rChars));
115 : }
116 : }
117 0 : break;
118 : case XLS_TOKEN(f):
119 : {
120 : // formula string
121 4 : ScDocument& rDoc = getScDocument();
122 4 : ScCompiler aComp(&rDoc, mrPos);
123 4 : aComp.SetGrammar(formula::FormulaGrammar::GRAM_OOXML);
124 4 : ScTokenArray* pArray = aComp.CompileString(rChars);
125 4 : if (!pArray)
126 0 : break;
127 :
128 4 : mrCellValue.set(new ScFormulaCell(&rDoc, mrPos, pArray));
129 : }
130 4 : break;
131 : default:
132 : ;
133 : }
134 24 : }
135 :
136 52 : virtual void onEndElement() SAL_OVERRIDE
137 : {
138 52 : switch (getCurrentElement())
139 : {
140 : case XLS_TOKEN(nc):
141 : case XLS_TOKEN(oc):
142 : {
143 28 : if (mrCellValue.isEmpty() && mxRichString)
144 : {
145 : // The value is a rich text string.
146 4 : ScDocument& rDoc = getScDocument();
147 4 : EditTextObject* pTextObj = mxRichString->convert(rDoc.GetEditEngine(), NULL);
148 4 : if (pTextObj)
149 : {
150 4 : svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
151 4 : pTextObj->NormalizeString(rPool);
152 4 : mrCellValue.set(pTextObj);
153 : }
154 : }
155 : }
156 28 : break;
157 : default:
158 : ;
159 : }
160 52 : }
161 :
162 : private:
163 28 : void importCell( const AttributeList& rAttribs )
164 : {
165 28 : mnType = rAttribs.getToken(XML_t, XML_n);
166 28 : OUString aRefStr = rAttribs.getString(XML_r, OUString());
167 28 : if (!aRefStr.isEmpty())
168 : {
169 28 : mrPos.Parse(aRefStr, NULL, formula::FormulaGrammar::CONV_XL_OOX);
170 28 : if (mnSheetIndex != -1)
171 28 : mrPos.SetTab(mnSheetIndex-1);
172 28 : }
173 28 : }
174 : };
175 :
176 48 : struct RevisionMetadata
177 : {
178 : OUString maUserName;
179 : DateTime maDateTime;
180 :
181 16 : RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
182 32 : RevisionMetadata( const RevisionMetadata& r ) :
183 32 : maUserName(r.maUserName), maDateTime(r.maDateTime) {}
184 : };
185 :
186 : }
187 :
188 : typedef std::map<OUString, RevisionMetadata> RevDataType;
189 :
190 4 : struct RevisionHeadersFragment::Impl
191 : {
192 : RevDataType maRevData;
193 :
194 4 : Impl() {}
195 : };
196 :
197 4 : RevisionHeadersFragment::RevisionHeadersFragment(
198 : const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
199 : WorkbookFragmentBase(rHelper, rFragmentPath),
200 4 : mpImpl(new Impl) {}
201 :
202 12 : RevisionHeadersFragment::~RevisionHeadersFragment()
203 : {
204 4 : delete mpImpl;
205 8 : }
206 :
207 84 : oox::core::ContextHandlerRef RevisionHeadersFragment::onCreateContext(
208 : sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
209 : {
210 84 : return this;
211 : }
212 :
213 84 : void RevisionHeadersFragment::onStartElement( const AttributeList& rAttribs )
214 : {
215 84 : switch (getCurrentElement())
216 : {
217 : case XLS_TOKEN(headers):
218 4 : importHeaders(rAttribs);
219 4 : break;
220 : case XLS_TOKEN(header):
221 16 : importHeader(rAttribs);
222 16 : break;
223 : case XLS_TOKEN(sheetIdMap):
224 16 : break;
225 : case XLS_TOKEN(sheetId):
226 48 : break;
227 : default:
228 : ;
229 : }
230 84 : }
231 :
232 0 : void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
233 :
234 84 : void RevisionHeadersFragment::onEndElement()
235 : {
236 84 : switch (getCurrentElement())
237 : {
238 : case XLS_TOKEN(headers):
239 4 : break;
240 : case XLS_TOKEN(header):
241 16 : break;
242 : case XLS_TOKEN(sheetIdMap):
243 16 : break;
244 : case XLS_TOKEN(sheetId):
245 48 : break;
246 : default:
247 : ;
248 : }
249 84 : }
250 :
251 4 : void RevisionHeadersFragment::finalizeImport()
252 : {
253 4 : ScDocument& rDoc = getScDocument();
254 4 : std::unique_ptr<ScChangeTrack> pCT(new ScChangeTrack(&rDoc));
255 8 : OUString aSelfUser = pCT->GetUser(); // owner of this document.
256 4 : pCT->SetUseFixDateTime(true);
257 :
258 4 : const oox::core::Relations& rRels = getRelations();
259 4 : RevDataType::const_iterator it = mpImpl->maRevData.begin(), itEnd = mpImpl->maRevData.end();
260 20 : for (; it != itEnd; ++it)
261 : {
262 16 : OUString aPath = rRels.getFragmentPathFromRelId(it->first);
263 16 : if (aPath.isEmpty())
264 0 : continue;
265 :
266 : // Parse each reivison log fragment.
267 16 : const RevisionMetadata& rData = it->second;
268 16 : pCT->SetUser(rData.maUserName);
269 16 : pCT->SetFixDateTimeLocal(rData.maDateTime);
270 32 : boost::scoped_ptr<oox::core::FastParser> xParser(getOoxFilter().createParser());
271 32 : rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
272 16 : importOoxFragment(xFragment, *xParser);
273 16 : }
274 :
275 4 : pCT->SetUser(aSelfUser); // set the default user to the document owner.
276 4 : pCT->SetUseFixDateTime(false);
277 4 : rDoc.SetChangeTrack(pCT.release());
278 :
279 : // Turn on visibility of tracked changes.
280 8 : ScChangeViewSettings aSettings;
281 4 : aSettings.SetShowChanges(true);
282 8 : rDoc.SetChangeViewSettings(aSettings);
283 4 : }
284 :
285 4 : void RevisionHeadersFragment::importHeaders( const AttributeList& /*rAttribs*/ )
286 : {
287 : // Nothing for now.
288 4 : }
289 :
290 16 : void RevisionHeadersFragment::importHeader( const AttributeList& rAttribs )
291 : {
292 16 : OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
293 16 : if (aRId.isEmpty())
294 : // All bets are off if we don't have a relation ID.
295 16 : return;
296 :
297 32 : RevisionMetadata aMetadata;
298 32 : OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
299 16 : if (!aDateTimeStr.isEmpty())
300 : {
301 16 : util::DateTime aDateTime;
302 16 : sax::Converter::parseDateTime(aDateTime, 0, aDateTimeStr);
303 16 : Date aDate(aDateTime.Day, aDateTime.Month, aDateTime.Year);
304 16 : tools::Time aTime(aDateTime.Hours, aDateTime.Minutes, aDateTime.Seconds, aDateTime.NanoSeconds);
305 16 : aMetadata.maDateTime.SetDate(aDate.GetDate());
306 16 : aMetadata.maDateTime.SetTime(aTime.GetTime());
307 : }
308 :
309 16 : aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
310 :
311 32 : mpImpl->maRevData.insert(RevDataType::value_type(aRId, aMetadata));
312 : }
313 :
314 16 : struct RevisionLogFragment::Impl
315 : {
316 : ScChangeTrack& mrChangeTrack;
317 :
318 : sal_Int32 mnRevIndex;
319 : sal_Int32 mnSheetIndex;
320 :
321 : RevisionType meType;
322 :
323 : // rcc
324 : ScAddress maOldCellPos;
325 : ScAddress maNewCellPos;
326 : ScCellValue maOldCellValue;
327 : ScCellValue maNewCellValue;
328 :
329 : // rrc
330 : ScRange maRange;
331 :
332 : bool mbEndOfList;
333 :
334 16 : Impl( ScChangeTrack& rChangeTrack ) :
335 : mrChangeTrack(rChangeTrack),
336 : mnRevIndex(-1),
337 : mnSheetIndex(-1),
338 : meType(REV_UNKNOWN),
339 16 : mbEndOfList(false) {}
340 : };
341 :
342 16 : RevisionLogFragment::RevisionLogFragment(
343 : const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
344 : WorkbookFragmentBase(rHelper, rFragmentPath),
345 16 : mpImpl(new Impl(rChangeTrack)) {}
346 :
347 48 : RevisionLogFragment::~RevisionLogFragment()
348 : {
349 16 : delete mpImpl;
350 32 : }
351 :
352 100 : oox::core::ContextHandlerRef RevisionLogFragment::onCreateContext(
353 : sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
354 : {
355 100 : switch (nElement)
356 : {
357 : case XLS_TOKEN(nc):
358 28 : return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
359 : case XLS_TOKEN(oc):
360 0 : return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
361 : default:
362 : ;
363 : }
364 72 : return this;
365 : }
366 :
367 72 : void RevisionLogFragment::onStartElement( const AttributeList& rAttribs )
368 : {
369 72 : switch (getCurrentElement())
370 : {
371 : case XLS_TOKEN(rcc):
372 28 : mpImpl->maNewCellPos.SetInvalid();
373 28 : mpImpl->maOldCellPos.SetInvalid();
374 28 : mpImpl->maNewCellValue.clear();
375 28 : mpImpl->maOldCellValue.clear();
376 28 : importRcc(rAttribs);
377 28 : break;
378 : case XLS_TOKEN(rrc):
379 24 : importRrc(rAttribs);
380 24 : break;
381 : default:
382 : ;
383 : }
384 72 : }
385 :
386 0 : void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
387 :
388 72 : void RevisionLogFragment::onEndElement()
389 : {
390 72 : switch (getCurrentElement())
391 : {
392 : case XLS_TOKEN(rcc):
393 : case XLS_TOKEN(rrc):
394 52 : pushRevision();
395 52 : break;
396 : default:
397 : ;
398 : }
399 72 : }
400 :
401 16 : void RevisionLogFragment::finalizeImport() {}
402 :
403 52 : void RevisionLogFragment::importCommon( const AttributeList& rAttribs )
404 : {
405 52 : mpImpl->mnRevIndex = rAttribs.getInteger(XML_rId, -1);
406 52 : mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
407 52 : }
408 :
409 28 : void RevisionLogFragment::importRcc( const AttributeList& rAttribs )
410 : {
411 28 : importCommon(rAttribs);
412 :
413 28 : mpImpl->meType = REV_CELLCHANGE;
414 28 : }
415 :
416 24 : void RevisionLogFragment::importRrc( const AttributeList& rAttribs )
417 : {
418 24 : importCommon(rAttribs);
419 :
420 24 : if (mpImpl->mnSheetIndex == -1)
421 : // invalid sheet index, or sheet index not given.
422 0 : return;
423 :
424 24 : mpImpl->meType = REV_UNKNOWN;
425 24 : sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
426 24 : if (nAction == -1)
427 0 : return;
428 :
429 24 : OUString aRefStr = rAttribs.getString(XML_ref, OUString());
430 24 : mpImpl->maRange.Parse(aRefStr, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
431 24 : if (!mpImpl->maRange.IsValid())
432 0 : return;
433 :
434 24 : switch (nAction)
435 : {
436 : case XML_insertRow:
437 24 : mpImpl->meType = REV_INSERTROW;
438 24 : mpImpl->maRange.aEnd.SetCol(MAXCOL);
439 24 : mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
440 24 : mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
441 24 : break;
442 : default:
443 : // Unknown action type. Ignore it.
444 0 : return;
445 : }
446 :
447 24 : mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
448 : }
449 :
450 52 : void RevisionLogFragment::pushRevision()
451 : {
452 52 : switch (mpImpl->meType)
453 : {
454 : case REV_CELLCHANGE:
455 : mpImpl->mrChangeTrack.AppendContentOnTheFly(
456 28 : mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
457 28 : break;
458 : case REV_INSERTROW:
459 24 : mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
460 24 : break;
461 : default:
462 : ;
463 : }
464 52 : }
465 :
466 48 : }}
467 :
468 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|