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