Update ooo320-m1
[ooovba.git] / xmloff / source / text / XMLRedlineExport.cxx
blob2a3da1c569025b6ad51caa92daf1e722707310f2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: XMLRedlineExport.cxx,v $
10 * $Revision: 1.20 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_xmloff.hxx"
33 #include "XMLRedlineExport.hxx"
34 #include <tools/debug.hxx>
35 #include <rtl/ustring.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/beans/UnknownPropertyException.hpp>
39 #include <com/sun/star/container/XEnumerationAccess.hpp>
41 #include <com/sun/star/container/XEnumeration.hpp>
42 #include <com/sun/star/document/XRedlinesSupplier.hpp>
43 #include <com/sun/star/text/XText.hpp>
44 #include <com/sun/star/text/XTextContent.hpp>
45 #include <com/sun/star/text/XTextSection.hpp>
46 #include <com/sun/star/util/DateTime.hpp>
47 #include <xmloff/xmltoken.hxx>
48 #include "xmlnmspe.hxx"
49 #include <xmloff/xmlexp.hxx>
50 #include <xmloff/xmluconv.hxx>
53 using namespace ::com::sun::star;
54 using namespace ::xmloff::token;
56 using ::com::sun::star::beans::PropertyValue;
57 using ::com::sun::star::beans::XPropertySet;
58 using ::com::sun::star::beans::UnknownPropertyException;
59 using ::com::sun::star::document::XRedlinesSupplier;
60 using ::com::sun::star::container::XEnumerationAccess;
61 using ::com::sun::star::container::XEnumeration;
62 using ::com::sun::star::text::XText;
63 using ::com::sun::star::text::XTextContent;
64 using ::com::sun::star::text::XTextSection;
65 using ::com::sun::star::uno::Any;
66 using ::com::sun::star::uno::Reference;
67 using ::com::sun::star::uno::Sequence;
68 using ::com::sun::star::util::DateTime;
69 using ::rtl::OUString;
70 using ::rtl::OUStringBuffer;
71 using ::std::list;
74 XMLRedlineExport::XMLRedlineExport(SvXMLExport& rExp)
75 : sDelete(RTL_CONSTASCII_USTRINGPARAM("Delete"))
76 , sDeletion(GetXMLToken(XML_DELETION))
77 , sFormat(RTL_CONSTASCII_USTRINGPARAM("Format"))
78 , sFormatChange(GetXMLToken(XML_FORMAT_CHANGE))
79 , sInsert(RTL_CONSTASCII_USTRINGPARAM("Insert"))
80 , sInsertion(GetXMLToken(XML_INSERTION))
81 , sIsCollapsed(RTL_CONSTASCII_USTRINGPARAM("IsCollapsed"))
82 , sIsStart(RTL_CONSTASCII_USTRINGPARAM("IsStart"))
83 , sRedlineAuthor(RTL_CONSTASCII_USTRINGPARAM("RedlineAuthor"))
84 , sRedlineComment(RTL_CONSTASCII_USTRINGPARAM("RedlineComment"))
85 , sRedlineDateTime(RTL_CONSTASCII_USTRINGPARAM("RedlineDateTime"))
86 , sRedlineSuccessorData(RTL_CONSTASCII_USTRINGPARAM("RedlineSuccessorData"))
87 , sRedlineText(RTL_CONSTASCII_USTRINGPARAM("RedlineText"))
88 , sRedlineType(RTL_CONSTASCII_USTRINGPARAM("RedlineType"))
89 , sStyle(RTL_CONSTASCII_USTRINGPARAM("Style"))
90 , sTextTable(RTL_CONSTASCII_USTRINGPARAM("TextTable"))
91 , sUnknownChange(RTL_CONSTASCII_USTRINGPARAM("UnknownChange"))
92 , sStartRedline(RTL_CONSTASCII_USTRINGPARAM("StartRedline"))
93 , sEndRedline(RTL_CONSTASCII_USTRINGPARAM("EndRedline"))
94 , sRedlineIdentifier(RTL_CONSTASCII_USTRINGPARAM("RedlineIdentifier"))
95 , sIsInHeaderFooter(RTL_CONSTASCII_USTRINGPARAM("IsInHeaderFooter"))
96 , sRedlineProtectionKey(RTL_CONSTASCII_USTRINGPARAM("RedlineProtectionKey"))
97 , sRecordChanges(RTL_CONSTASCII_USTRINGPARAM("RecordChanges"))
98 , sMergeLastPara(RTL_CONSTASCII_USTRINGPARAM("MergeLastPara"))
99 , sChangePrefix(RTL_CONSTASCII_USTRINGPARAM("ct"))
100 , rExport(rExp)
101 , pCurrentChangesList(NULL)
106 XMLRedlineExport::~XMLRedlineExport()
108 // delete changes lists
109 for( ChangesMapType::iterator aIter = aChangeMap.begin();
110 aIter != aChangeMap.end();
111 aIter++ )
113 delete aIter->second;
115 aChangeMap.clear();
119 void XMLRedlineExport::ExportChange(
120 const Reference<XPropertySet> & rPropSet,
121 sal_Bool bAutoStyle)
123 if (bAutoStyle)
125 // For the headers/footers, we have to collect the autostyles
126 // here. For the general case, however, it's better to collet
127 // the autostyles by iterating over the global redline
128 // list. So that's what we do: Here, we collect autostyles
129 // only if we have no current list of changes. For the
130 // main-document case, the autostyles are collected in
131 // ExportChangesListAutoStyles().
132 if (pCurrentChangesList != NULL)
133 ExportChangeAutoStyle(rPropSet);
135 else
137 ExportChangeInline(rPropSet);
142 void XMLRedlineExport::ExportChangesList(sal_Bool bAutoStyles)
144 if (bAutoStyles)
146 ExportChangesListAutoStyles();
148 else
150 ExportChangesListElements();
155 void XMLRedlineExport::ExportChangesList(
156 const Reference<XText> & rText,
157 sal_Bool bAutoStyles)
159 // in the header/footer case, auto styles are collected from the
160 // inline change elements.
161 if (bAutoStyles)
162 return;
164 // look for changes list for this XText
165 ChangesMapType::iterator aFind = aChangeMap.find(rText);
166 if (aFind != aChangeMap.end())
168 ChangesListType* pChangesList = aFind->second;
170 // export only if changes are found
171 if (pChangesList->size() > 0)
173 // changes container element
174 SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
175 XML_TRACKED_CHANGES,
176 sal_True, sal_True);
178 // iterate over changes list
179 for( ChangesListType::iterator aIter = pChangesList->begin();
180 aIter != pChangesList->end();
181 aIter++ )
183 ExportChangedRegion( *aIter );
186 // else: changes list empty -> ignore
188 // else: no changes list found -> empty
191 void XMLRedlineExport::SetCurrentXText(
192 const Reference<XText> & rText)
194 if (rText.is())
196 // look for appropriate list in map; use the found one, or create new
197 ChangesMapType::iterator aIter = aChangeMap.find(rText);
198 if (aIter == aChangeMap.end())
200 ChangesListType* pList = new ChangesListType;
201 aChangeMap[rText] = pList;
202 pCurrentChangesList = pList;
204 else
205 pCurrentChangesList = aIter->second;
207 else
209 // don't record changes
210 SetCurrentXText();
214 void XMLRedlineExport::SetCurrentXText()
216 pCurrentChangesList = NULL;
220 void XMLRedlineExport::ExportChangesListElements()
222 // get redlines (aka tracked changes) from the model
223 Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY);
224 if (xSupplier.is())
226 Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines();
228 // redline protection key
229 Reference<XPropertySet> aDocPropertySet( rExport.GetModel(),
230 uno::UNO_QUERY );
231 // redlining enabled?
232 sal_Bool bEnabled = *(sal_Bool*)aDocPropertySet->getPropertyValue(
233 sRecordChanges ).getValue();
235 // only export if we have redlines or attributes
236 if ( aEnumAccess->hasElements() || bEnabled )
239 // export only if we have changes, but tracking is not enabled
240 if ( !bEnabled != !aEnumAccess->hasElements() )
242 rExport.AddAttribute(
243 XML_NAMESPACE_TEXT, XML_TRACK_CHANGES,
244 bEnabled ? XML_TRUE : XML_FALSE );
247 // changes container element
248 SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
249 XML_TRACKED_CHANGES,
250 sal_True, sal_True);
252 // get enumeration and iterate over elements
253 Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration();
254 while (aEnum->hasMoreElements())
256 Any aAny = aEnum->nextElement();
257 Reference<XPropertySet> xPropSet;
258 aAny >>= xPropSet;
260 DBG_ASSERT(xPropSet.is(),
261 "can't get XPropertySet; skipping Redline");
262 if (xPropSet.is())
264 // export only if not in header or footer
265 // (those must be exported with their XText)
266 aAny = xPropSet->getPropertyValue(sIsInHeaderFooter);
267 if (! *(sal_Bool*)aAny.getValue())
269 // and finally, export change
270 ExportChangedRegion(xPropSet);
273 // else: no XPropertySet -> no export
276 // else: no redlines -> no export
278 // else: no XRedlineSupplier -> no export
281 void XMLRedlineExport::ExportChangeAutoStyle(
282 const Reference<XPropertySet> & rPropSet)
284 // record change (if changes should be recorded)
285 if (NULL != pCurrentChangesList)
287 // put redline in list if it's collapsed or the redline start
288 Any aIsStart = rPropSet->getPropertyValue(sIsStart);
289 Any aIsCollapsed = rPropSet->getPropertyValue(sIsCollapsed);
291 if ( *(sal_Bool*)aIsStart.getValue() ||
292 *(sal_Bool*)aIsCollapsed.getValue() )
293 pCurrentChangesList->push_back(rPropSet);
296 // get XText for export of redline auto styles
297 Any aAny = rPropSet->getPropertyValue(sRedlineText);
298 Reference<XText> xText;
299 aAny >>= xText;
300 if (xText.is())
302 // export the auto styles
303 rExport.GetTextParagraphExport()->collectTextAutoStyles(xText);
307 void XMLRedlineExport::ExportChangesListAutoStyles()
309 // get redlines (aka tracked changes) from the model
310 Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY);
311 if (xSupplier.is())
313 Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines();
315 // only export if we actually have redlines
316 if (aEnumAccess->hasElements())
318 // get enumeration and iterate over elements
319 Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration();
320 while (aEnum->hasMoreElements())
322 Any aAny = aEnum->nextElement();
323 Reference<XPropertySet> xPropSet;
324 aAny >>= xPropSet;
326 DBG_ASSERT(xPropSet.is(),
327 "can't get XPropertySet; skipping Redline");
328 if (xPropSet.is())
331 // export only if not in header or footer
332 // (those must be exported with their XText)
333 aAny = xPropSet->getPropertyValue(sIsInHeaderFooter);
334 if (! *(sal_Bool*)aAny.getValue())
336 ExportChangeAutoStyle(xPropSet);
344 void XMLRedlineExport::ExportChangeInline(
345 const Reference<XPropertySet> & rPropSet)
347 // determine element name (depending on collapsed, start/end)
348 enum XMLTokenEnum eElement = XML_TOKEN_INVALID;
349 Any aAny = rPropSet->getPropertyValue(sIsCollapsed);
350 sal_Bool bCollapsed = *(sal_Bool *)aAny.getValue();
351 sal_Bool bStart = sal_True; // ignored if bCollapsed = sal_True
352 if (bCollapsed)
354 eElement = XML_CHANGE;
356 else
358 aAny = rPropSet->getPropertyValue(sIsStart);
359 bStart = *(sal_Bool *)aAny.getValue();
360 eElement = bStart ? XML_CHANGE_START : XML_CHANGE_END;
363 if (XML_TOKEN_INVALID != eElement)
365 // we always need the ID
366 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID,
367 GetRedlineID(rPropSet));
369 // export the element (no whitespace because we're in the text body)
370 SvXMLElementExport aChangeElem(rExport, XML_NAMESPACE_TEXT,
371 eElement, sal_False, sal_False);
376 void XMLRedlineExport::ExportChangedRegion(
377 const Reference<XPropertySet> & rPropSet)
379 // Redline-ID
380 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_ID, GetRedlineID(rPropSet) );
382 // merge-last-paragraph
383 Any aAny = rPropSet->getPropertyValue(sMergeLastPara);
384 if( ! *(sal_Bool*)aAny.getValue() )
385 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_MERGE_LAST_PARAGRAPH,
386 XML_FALSE);
388 // export change region element
389 SvXMLElementExport aChangedRegion(rExport, XML_NAMESPACE_TEXT,
390 XML_CHANGED_REGION, sal_True, sal_True);
393 // scope for (first) change element
395 aAny = rPropSet->getPropertyValue(sRedlineType);
396 OUString sType;
397 aAny >>= sType;
398 SvXMLElementExport aChange(rExport, XML_NAMESPACE_TEXT,
399 ConvertTypeName(sType), sal_True, sal_True);
401 ExportChangeInfo(rPropSet);
403 // get XText from the redline and export (if the XText exists)
404 aAny = rPropSet->getPropertyValue(sRedlineText);
405 Reference<XText> xText;
406 aAny >>= xText;
407 if (xText.is())
409 rExport.GetTextParagraphExport()->exportText(xText);
410 // default parameters: bProgress, bExportParagraph ???
412 // else: no text interface -> content is inline and will
413 // be exported there
416 // changed change? Hierarchical changes can onl be two levels
417 // deep. Here we check for the second level.
418 aAny = rPropSet->getPropertyValue(sRedlineSuccessorData);
419 Sequence<PropertyValue> aSuccessorData;
420 aAny >>= aSuccessorData;
422 // if we actually got a hierarchical change, make element and
423 // process change info
424 if (aSuccessorData.getLength() > 0)
426 // The only change that can be "undone" is an insertion -
427 // after all, you can't re-insert an deletion, but you can
428 // delete an insertion. This assumption is asserted in
429 // ExportChangeInfo(Sequence<PropertyValue>&).
430 SvXMLElementExport aSecondChangeElem(
431 rExport, XML_NAMESPACE_TEXT, XML_INSERTION,
432 sal_True, sal_True);
434 ExportChangeInfo(aSuccessorData);
436 // else: no hierarchical change
440 const OUString XMLRedlineExport::ConvertTypeName(
441 const OUString& sApiName)
443 if (sApiName == sDelete)
445 return sDeletion;
447 else if (sApiName == sInsert)
449 return sInsertion;
451 else if (sApiName == sFormat)
453 return sFormatChange;
455 else
457 DBG_ERROR("unknown redline type");
458 return sUnknownChange;
463 /** Create a Redline-ID */
464 const OUString XMLRedlineExport::GetRedlineID(
465 const Reference<XPropertySet> & rPropSet)
467 Any aAny = rPropSet->getPropertyValue(sRedlineIdentifier);
468 OUString sTmp;
469 aAny >>= sTmp;
471 OUStringBuffer sBuf(sChangePrefix);
472 sBuf.append(sTmp);
473 return sBuf.makeStringAndClear();
477 void XMLRedlineExport::ExportChangeInfo(
478 const Reference<XPropertySet> & rPropSet)
481 SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE,
482 XML_CHANGE_INFO, sal_True, sal_True);
484 Any aAny = rPropSet->getPropertyValue(sRedlineAuthor);
485 OUString sTmp;
486 aAny >>= sTmp;
487 if (sTmp.getLength() > 0)
489 SvXMLElementExport aCreatorElem( rExport, XML_NAMESPACE_DC,
490 XML_CREATOR, sal_True,
491 sal_False );
492 rExport.Characters(sTmp);
495 aAny = rPropSet->getPropertyValue(sRedlineDateTime);
496 util::DateTime aDateTime;
497 aAny >>= aDateTime;
499 OUStringBuffer sBuf;
500 rExport.GetMM100UnitConverter().convertDateTime(sBuf, aDateTime);
501 SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC,
502 XML_DATE, sal_True,
503 sal_False );
504 rExport.Characters(sBuf.makeStringAndClear());
507 // comment as <text:p> sequence
508 aAny = rPropSet->getPropertyValue(sRedlineComment);
509 aAny >>= sTmp;
510 WriteComment( sTmp );
513 void XMLRedlineExport::ExportChangeInfo(
514 const Sequence<PropertyValue> & rPropertyValues)
516 OUString sComment;
518 sal_Int32 nCount = rPropertyValues.getLength();
519 for(sal_Int32 i = 0; i < nCount; i++)
521 const PropertyValue& rVal = rPropertyValues[i];
523 if( rVal.Name.equals(sRedlineAuthor) )
525 OUString sTmp;
526 rVal.Value >>= sTmp;
527 if (sTmp.getLength() > 0)
529 rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_AUTHOR, sTmp);
532 else if( rVal.Name.equals(sRedlineComment) )
534 rVal.Value >>= sComment;
536 else if( rVal.Name.equals(sRedlineDateTime) )
538 util::DateTime aDateTime;
539 rVal.Value >>= aDateTime;
540 OUStringBuffer sBuf;
541 rExport.GetMM100UnitConverter().convertDateTime(sBuf, aDateTime);
542 rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_DATE_TIME,
543 sBuf.makeStringAndClear());
545 else if( rVal.Name.equals(sRedlineType) )
547 // check if this is an insertion; cf. comment at calling location
548 OUString sTmp;
549 rVal.Value >>= sTmp;
550 DBG_ASSERT(sTmp.equals(sInsert),
551 "hierarchical change must be insertion");
553 // else: unknown value -> ignore
556 // finally write element
557 SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE,
558 XML_CHANGE_INFO, sal_True, sal_True);
560 WriteComment( sComment );
563 void XMLRedlineExport::ExportStartOrEndRedline(
564 const Reference<XPropertySet> & rPropSet,
565 sal_Bool bStart)
567 if( ! rPropSet.is() )
568 return;
570 // get appropriate (start or end) property
571 Any aAny;
574 aAny = rPropSet->getPropertyValue(bStart ? sStartRedline : sEndRedline);
576 catch( UnknownPropertyException e )
578 // If we don't have the property, there's nothing to do.
579 return;
582 Sequence<PropertyValue> aValues;
583 aAny >>= aValues;
584 const PropertyValue* pValues = aValues.getConstArray();
586 // seek for redline properties
587 sal_Bool bIsCollapsed = sal_False;
588 sal_Bool bIsStart = sal_True;
589 OUString sId;
590 sal_Bool bIdOK = sal_False; // have we seen an ID?
591 sal_Int32 nLength = aValues.getLength();
592 for(sal_Int32 i = 0; i < nLength; i++)
594 if (sRedlineIdentifier.equals(pValues[i].Name))
596 pValues[i].Value >>= sId;
597 bIdOK = sal_True;
599 else if (sIsCollapsed.equals(pValues[i].Name))
601 bIsCollapsed = *(sal_Bool*)pValues[i].Value.getValue();
603 else if (sIsStart.equals(pValues[i].Name))
605 bIsStart = *(sal_Bool*)pValues[i].Value.getValue();
609 if( bIdOK )
611 DBG_ASSERT( sId.getLength() > 0, "Redlines must have IDs" );
613 // TODO: use GetRedlineID or elimiate that function
614 OUStringBuffer sBuffer(sChangePrefix);
615 sBuffer.append(sId);
617 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID,
618 sBuffer.makeStringAndClear());
620 // export the element
621 // (whitespace because we're not inside paragraphs)
622 SvXMLElementExport aChangeElem(
623 rExport, XML_NAMESPACE_TEXT,
624 bIsCollapsed ? XML_CHANGE :
625 ( bIsStart ? XML_CHANGE_START : XML_CHANGE_END ),
626 sal_True, sal_True);
630 void XMLRedlineExport::ExportStartOrEndRedline(
631 const Reference<XTextContent> & rContent,
632 sal_Bool bStart)
634 Reference<XPropertySet> xPropSet(rContent, uno::UNO_QUERY);
635 if (xPropSet.is())
637 ExportStartOrEndRedline(xPropSet, bStart);
639 else
641 DBG_ERROR("XPropertySet expected");
645 void XMLRedlineExport::ExportStartOrEndRedline(
646 const Reference<XTextSection> & rSection,
647 sal_Bool bStart)
649 Reference<XPropertySet> xPropSet(rSection, uno::UNO_QUERY);
650 if (xPropSet.is())
652 ExportStartOrEndRedline(xPropSet, bStart);
654 else
656 DBG_ERROR("XPropertySet expected");
660 void XMLRedlineExport::WriteComment(const OUString& rComment)
662 if (rComment.getLength() > 0)
664 // iterate over all string-pieces separated by return (0x0a) and
665 // put each inside a paragraph element.
666 SvXMLTokenEnumerator aEnumerator(rComment, sal_Char(0x0a));
667 OUString aSubString;
668 while (aEnumerator.getNextToken(aSubString))
670 SvXMLElementExport aParagraph(
671 rExport, XML_NAMESPACE_TEXT, XML_P, sal_True, sal_False);
672 rExport.Characters(aSubString);