|  | /************************************************************** | 
|  | * | 
|  | * Licensed to the Apache Software Foundation (ASF) under one | 
|  | * or more contributor license agreements.  See the NOTICE file | 
|  | * distributed with this work for additional information | 
|  | * regarding copyright ownership.  The ASF licenses this file | 
|  | * to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance | 
|  | * with the License.  You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, | 
|  | * software distributed under the License is distributed on an | 
|  | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | * KIND, either express or implied.  See the License for the | 
|  | * specific language governing permissions and limitations | 
|  | * under the License. | 
|  | * | 
|  | *************************************************************/ | 
|  |  | 
|  |  | 
|  |  | 
|  | // MARKER(update_precomp.py): autogen include statement, do not remove | 
|  | #include "precompiled_xmloff.hxx" | 
|  | #include "XMLRedlineExport.hxx" | 
|  | #include <tools/debug.hxx> | 
|  | #include <rtl/ustring.hxx> | 
|  | #include <rtl/ustrbuf.hxx> | 
|  | #include <com/sun/star/beans/XPropertySet.hpp> | 
|  | #include <com/sun/star/beans/UnknownPropertyException.hpp> | 
|  | #include <com/sun/star/container/XEnumerationAccess.hpp> | 
|  |  | 
|  | #include <com/sun/star/container/XEnumeration.hpp> | 
|  | #include <com/sun/star/document/XRedlinesSupplier.hpp> | 
|  | #include <com/sun/star/text/XText.hpp> | 
|  | #include <com/sun/star/text/XTextContent.hpp> | 
|  | #include <com/sun/star/text/XTextSection.hpp> | 
|  | #include <com/sun/star/util/DateTime.hpp> | 
|  | #include <xmloff/xmltoken.hxx> | 
|  | #include "xmloff/xmlnmspe.hxx" | 
|  | #include <xmloff/xmlexp.hxx> | 
|  | #include <xmloff/xmluconv.hxx> | 
|  |  | 
|  |  | 
|  | using namespace ::com::sun::star; | 
|  | using namespace ::xmloff::token; | 
|  |  | 
|  | using ::com::sun::star::beans::PropertyValue; | 
|  | using ::com::sun::star::beans::XPropertySet; | 
|  | using ::com::sun::star::beans::UnknownPropertyException; | 
|  | using ::com::sun::star::document::XRedlinesSupplier; | 
|  | using ::com::sun::star::container::XEnumerationAccess; | 
|  | using ::com::sun::star::container::XEnumeration; | 
|  | using ::com::sun::star::text::XText; | 
|  | using ::com::sun::star::text::XTextContent; | 
|  | using ::com::sun::star::text::XTextSection; | 
|  | using ::com::sun::star::uno::Any; | 
|  | using ::com::sun::star::uno::Reference; | 
|  | using ::com::sun::star::uno::Sequence; | 
|  | using ::com::sun::star::util::DateTime; | 
|  | using ::rtl::OUString; | 
|  | using ::rtl::OUStringBuffer; | 
|  | using ::std::list; | 
|  |  | 
|  |  | 
|  | XMLRedlineExport::XMLRedlineExport(SvXMLExport& rExp) | 
|  | :	sDelete(RTL_CONSTASCII_USTRINGPARAM("Delete")) | 
|  | ,	sDeletion(GetXMLToken(XML_DELETION)) | 
|  | ,	sFormat(RTL_CONSTASCII_USTRINGPARAM("Format")) | 
|  | ,	sFormatChange(GetXMLToken(XML_FORMAT_CHANGE)) | 
|  | ,	sInsert(RTL_CONSTASCII_USTRINGPARAM("Insert")) | 
|  | ,	sInsertion(GetXMLToken(XML_INSERTION)) | 
|  | ,	sIsCollapsed(RTL_CONSTASCII_USTRINGPARAM("IsCollapsed")) | 
|  | ,	sIsStart(RTL_CONSTASCII_USTRINGPARAM("IsStart")) | 
|  | ,	sRedlineAuthor(RTL_CONSTASCII_USTRINGPARAM("RedlineAuthor")) | 
|  | ,	sRedlineComment(RTL_CONSTASCII_USTRINGPARAM("RedlineComment")) | 
|  | ,	sRedlineDateTime(RTL_CONSTASCII_USTRINGPARAM("RedlineDateTime")) | 
|  | ,	sRedlineSuccessorData(RTL_CONSTASCII_USTRINGPARAM("RedlineSuccessorData")) | 
|  | ,	sRedlineText(RTL_CONSTASCII_USTRINGPARAM("RedlineText")) | 
|  | ,	sRedlineType(RTL_CONSTASCII_USTRINGPARAM("RedlineType")) | 
|  | ,	sStyle(RTL_CONSTASCII_USTRINGPARAM("Style")) | 
|  | ,	sTextTable(RTL_CONSTASCII_USTRINGPARAM("TextTable")) | 
|  | ,	sUnknownChange(RTL_CONSTASCII_USTRINGPARAM("UnknownChange")) | 
|  | ,	sStartRedline(RTL_CONSTASCII_USTRINGPARAM("StartRedline")) | 
|  | ,	sEndRedline(RTL_CONSTASCII_USTRINGPARAM("EndRedline")) | 
|  | ,	sRedlineIdentifier(RTL_CONSTASCII_USTRINGPARAM("RedlineIdentifier")) | 
|  | ,	sIsInHeaderFooter(RTL_CONSTASCII_USTRINGPARAM("IsInHeaderFooter")) | 
|  | ,	sRedlineProtectionKey(RTL_CONSTASCII_USTRINGPARAM("RedlineProtectionKey")) | 
|  | ,	sRecordChanges(RTL_CONSTASCII_USTRINGPARAM("RecordChanges")) | 
|  | ,	sMergeLastPara(RTL_CONSTASCII_USTRINGPARAM("MergeLastPara")) | 
|  | ,	sChangePrefix(RTL_CONSTASCII_USTRINGPARAM("ct")) | 
|  | ,	rExport(rExp) | 
|  | ,	pCurrentChangesList(NULL) | 
|  | { | 
|  | } | 
|  |  | 
|  |  | 
|  | XMLRedlineExport::~XMLRedlineExport() | 
|  | { | 
|  | // delete changes lists | 
|  | for( ChangesMapType::iterator aIter = aChangeMap.begin(); | 
|  | aIter != aChangeMap.end(); | 
|  | aIter++ ) | 
|  | { | 
|  | delete aIter->second; | 
|  | } | 
|  | aChangeMap.clear(); | 
|  | } | 
|  |  | 
|  |  | 
|  | void XMLRedlineExport::ExportChange( | 
|  | const Reference<XPropertySet> & rPropSet, | 
|  | sal_Bool bAutoStyle) | 
|  | { | 
|  | if (bAutoStyle) | 
|  | { | 
|  | // For the headers/footers, we have to collect the autostyles | 
|  | // here.  For the general case, however, it's better to collet | 
|  | // the autostyles by iterating over the global redline | 
|  | // list. So that's what we do: Here, we collect autostyles | 
|  | // only if we have no current list of changes. For the | 
|  | // main-document case, the autostyles are collected in | 
|  | // ExportChangesListAutoStyles(). | 
|  | if (pCurrentChangesList != NULL) | 
|  | ExportChangeAutoStyle(rPropSet); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExportChangeInline(rPropSet); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void XMLRedlineExport::ExportChangesList(sal_Bool bAutoStyles) | 
|  | { | 
|  | if (bAutoStyles) | 
|  | { | 
|  | ExportChangesListAutoStyles(); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExportChangesListElements(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void XMLRedlineExport::ExportChangesList( | 
|  | const Reference<XText> & rText, | 
|  | sal_Bool bAutoStyles) | 
|  | { | 
|  | // in the header/footer case, auto styles are collected from the | 
|  | // inline change elements. | 
|  | if (bAutoStyles) | 
|  | return; | 
|  |  | 
|  | // look for changes list for this XText | 
|  | ChangesMapType::iterator aFind = aChangeMap.find(rText); | 
|  | if (aFind != aChangeMap.end()) | 
|  | { | 
|  | ChangesListType* pChangesList = aFind->second; | 
|  |  | 
|  | // export only if changes are found | 
|  | if (pChangesList->size() > 0) | 
|  | { | 
|  | // changes container element | 
|  | SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT, | 
|  | XML_TRACKED_CHANGES, | 
|  | sal_True, sal_True); | 
|  |  | 
|  | // iterate over changes list | 
|  | for( ChangesListType::iterator aIter = pChangesList->begin(); | 
|  | aIter != pChangesList->end(); | 
|  | aIter++ ) | 
|  | { | 
|  | ExportChangedRegion( *aIter ); | 
|  | } | 
|  | } | 
|  | // else: changes list empty -> ignore | 
|  | } | 
|  | // else: no changes list found -> empty | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::SetCurrentXText( | 
|  | const Reference<XText> & rText) | 
|  | { | 
|  | if (rText.is()) | 
|  | { | 
|  | // look for appropriate list in map; use the found one, or create new | 
|  | ChangesMapType::iterator aIter = aChangeMap.find(rText); | 
|  | if (aIter == aChangeMap.end()) | 
|  | { | 
|  | ChangesListType* pList = new ChangesListType; | 
|  | aChangeMap[rText] = pList; | 
|  | pCurrentChangesList = pList; | 
|  | } | 
|  | else | 
|  | pCurrentChangesList = aIter->second; | 
|  | } | 
|  | else | 
|  | { | 
|  | // don't record changes | 
|  | SetCurrentXText(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::SetCurrentXText() | 
|  | { | 
|  | pCurrentChangesList = NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | void XMLRedlineExport::ExportChangesListElements() | 
|  | { | 
|  | // get redlines (aka tracked changes) from the model | 
|  | Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY); | 
|  | if (xSupplier.is()) | 
|  | { | 
|  | Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines(); | 
|  |  | 
|  | // redline protection key | 
|  | Reference<XPropertySet> aDocPropertySet( rExport.GetModel(), | 
|  | uno::UNO_QUERY ); | 
|  | // redlining enabled? | 
|  | sal_Bool bEnabled = *(sal_Bool*)aDocPropertySet->getPropertyValue( | 
|  | sRecordChanges ).getValue(); | 
|  |  | 
|  | // only export if we have redlines or attributes | 
|  | if ( aEnumAccess->hasElements() || bEnabled ) | 
|  | { | 
|  |  | 
|  | // export only if we have changes, but tracking is not enabled | 
|  | if ( !bEnabled != !aEnumAccess->hasElements() ) | 
|  | { | 
|  | rExport.AddAttribute( | 
|  | XML_NAMESPACE_TEXT, XML_TRACK_CHANGES, | 
|  | bEnabled ? XML_TRUE : XML_FALSE ); | 
|  | } | 
|  |  | 
|  | // changes container element | 
|  | SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT, | 
|  | XML_TRACKED_CHANGES, | 
|  | sal_True, sal_True); | 
|  |  | 
|  | // get enumeration and iterate over elements | 
|  | Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration(); | 
|  | while (aEnum->hasMoreElements()) | 
|  | { | 
|  | Any aAny = aEnum->nextElement(); | 
|  | Reference<XPropertySet> xPropSet; | 
|  | aAny >>= xPropSet; | 
|  |  | 
|  | DBG_ASSERT(xPropSet.is(), | 
|  | "can't get XPropertySet; skipping Redline"); | 
|  | if (xPropSet.is()) | 
|  | { | 
|  | // export only if not in header or footer | 
|  | // (those must be exported with their XText) | 
|  | aAny = xPropSet->getPropertyValue(sIsInHeaderFooter); | 
|  | if (! *(sal_Bool*)aAny.getValue()) | 
|  | { | 
|  | // and finally, export change | 
|  | ExportChangedRegion(xPropSet); | 
|  | } | 
|  | } | 
|  | // else: no XPropertySet -> no export | 
|  | } | 
|  | } | 
|  | // else: no redlines -> no export | 
|  | } | 
|  | // else: no XRedlineSupplier -> no export | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportChangeAutoStyle( | 
|  | const Reference<XPropertySet> & rPropSet) | 
|  | { | 
|  | // record change (if changes should be recorded) | 
|  | if (NULL != pCurrentChangesList) | 
|  | { | 
|  | // put redline in list if it's collapsed or the redline start | 
|  | Any aIsStart = rPropSet->getPropertyValue(sIsStart); | 
|  | Any aIsCollapsed = rPropSet->getPropertyValue(sIsCollapsed); | 
|  |  | 
|  | if ( *(sal_Bool*)aIsStart.getValue() || | 
|  | *(sal_Bool*)aIsCollapsed.getValue() ) | 
|  | pCurrentChangesList->push_back(rPropSet); | 
|  | } | 
|  |  | 
|  | // get XText for export of redline auto styles | 
|  | Any aAny = rPropSet->getPropertyValue(sRedlineText); | 
|  | Reference<XText> xText; | 
|  | aAny >>= xText; | 
|  | if (xText.is()) | 
|  | { | 
|  | // export the auto styles | 
|  | rExport.GetTextParagraphExport()->collectTextAutoStyles(xText); | 
|  | } | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportChangesListAutoStyles() | 
|  | { | 
|  | // get redlines (aka tracked changes) from the model | 
|  | Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY); | 
|  | if (xSupplier.is()) | 
|  | { | 
|  | Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines(); | 
|  |  | 
|  | // only export if we actually have redlines | 
|  | if (aEnumAccess->hasElements()) | 
|  | { | 
|  | // get enumeration and iterate over elements | 
|  | Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration(); | 
|  | while (aEnum->hasMoreElements()) | 
|  | { | 
|  | Any aAny = aEnum->nextElement(); | 
|  | Reference<XPropertySet> xPropSet; | 
|  | aAny >>= xPropSet; | 
|  |  | 
|  | DBG_ASSERT(xPropSet.is(), | 
|  | "can't get XPropertySet; skipping Redline"); | 
|  | if (xPropSet.is()) | 
|  | { | 
|  |  | 
|  | // export only if not in header or footer | 
|  | // (those must be exported with their XText) | 
|  | aAny = xPropSet->getPropertyValue(sIsInHeaderFooter); | 
|  | if (! *(sal_Bool*)aAny.getValue()) | 
|  | { | 
|  | ExportChangeAutoStyle(xPropSet); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportChangeInline( | 
|  | const Reference<XPropertySet> & rPropSet) | 
|  | { | 
|  | // determine element name (depending on collapsed, start/end) | 
|  | enum XMLTokenEnum eElement = XML_TOKEN_INVALID; | 
|  | Any aAny = rPropSet->getPropertyValue(sIsCollapsed); | 
|  | sal_Bool bCollapsed = *(sal_Bool *)aAny.getValue(); | 
|  | sal_Bool bStart = sal_True;	// ignored if bCollapsed = sal_True | 
|  | if (bCollapsed) | 
|  | { | 
|  | eElement = XML_CHANGE; | 
|  | } | 
|  | else | 
|  | { | 
|  | aAny = rPropSet->getPropertyValue(sIsStart); | 
|  | bStart = *(sal_Bool *)aAny.getValue(); | 
|  | eElement = bStart ? XML_CHANGE_START : XML_CHANGE_END; | 
|  | } | 
|  |  | 
|  | if (XML_TOKEN_INVALID != eElement) | 
|  | { | 
|  | // we always need the ID | 
|  | rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID, | 
|  | GetRedlineID(rPropSet)); | 
|  |  | 
|  | // export the element (no whitespace because we're in the text body) | 
|  | SvXMLElementExport aChangeElem(rExport, XML_NAMESPACE_TEXT, | 
|  | eElement, sal_False, sal_False); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void XMLRedlineExport::ExportChangedRegion( | 
|  | const Reference<XPropertySet> & rPropSet) | 
|  | { | 
|  | // Redline-ID | 
|  | rExport.AddAttributeIdLegacy(XML_NAMESPACE_TEXT, GetRedlineID(rPropSet)); | 
|  |  | 
|  | // merge-last-paragraph | 
|  | Any aAny = rPropSet->getPropertyValue(sMergeLastPara); | 
|  | if( ! *(sal_Bool*)aAny.getValue() ) | 
|  | rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_MERGE_LAST_PARAGRAPH, | 
|  | XML_FALSE); | 
|  |  | 
|  | // export change region element | 
|  | SvXMLElementExport aChangedRegion(rExport, XML_NAMESPACE_TEXT, | 
|  | XML_CHANGED_REGION, sal_True, sal_True); | 
|  |  | 
|  |  | 
|  | // scope for (first) change element | 
|  | { | 
|  | aAny = rPropSet->getPropertyValue(sRedlineType); | 
|  | OUString sType; | 
|  | aAny >>= sType; | 
|  | SvXMLElementExport aChange(rExport, XML_NAMESPACE_TEXT, | 
|  | ConvertTypeName(sType), sal_True, sal_True); | 
|  |  | 
|  | ExportChangeInfo(rPropSet); | 
|  |  | 
|  | // get XText from the redline and export (if the XText exists) | 
|  | aAny = rPropSet->getPropertyValue(sRedlineText); | 
|  | Reference<XText> xText; | 
|  | aAny >>= xText; | 
|  | if (xText.is()) | 
|  | { | 
|  | rExport.GetTextParagraphExport()->exportText(xText); | 
|  | // default parameters: bProgress, bExportParagraph ??? | 
|  | } | 
|  | // else: no text interface -> content is inline and will | 
|  | //       be exported there | 
|  | } | 
|  |  | 
|  | // changed change? Hierarchical changes can onl be two levels | 
|  | // deep. Here we check for the second level. | 
|  | aAny = rPropSet->getPropertyValue(sRedlineSuccessorData); | 
|  | Sequence<PropertyValue> aSuccessorData; | 
|  | aAny >>= aSuccessorData; | 
|  |  | 
|  | // if we actually got a hierarchical change, make element and | 
|  | // process change info | 
|  | if (aSuccessorData.getLength() > 0) | 
|  | { | 
|  | // The only change that can be "undone" is an insertion - | 
|  | // after all, you can't re-insert an deletion, but you can | 
|  | // delete an insertion. This assumption is asserted in | 
|  | // ExportChangeInfo(Sequence<PropertyValue>&). | 
|  | SvXMLElementExport aSecondChangeElem( | 
|  | rExport, XML_NAMESPACE_TEXT, XML_INSERTION, | 
|  | sal_True, sal_True); | 
|  |  | 
|  | ExportChangeInfo(aSuccessorData); | 
|  | } | 
|  | // else: no hierarchical change | 
|  | } | 
|  |  | 
|  |  | 
|  | const OUString XMLRedlineExport::ConvertTypeName( | 
|  | const OUString& sApiName) | 
|  | { | 
|  | if (sApiName == sDelete) | 
|  | { | 
|  | return sDeletion; | 
|  | } | 
|  | else if (sApiName == sInsert) | 
|  | { | 
|  | return sInsertion; | 
|  | } | 
|  | else if (sApiName == sFormat) | 
|  | { | 
|  | return sFormatChange; | 
|  | } | 
|  | else | 
|  | { | 
|  | DBG_ERROR("unknown redline type"); | 
|  | return sUnknownChange; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /** Create a Redline-ID */ | 
|  | const OUString XMLRedlineExport::GetRedlineID( | 
|  | const Reference<XPropertySet> & rPropSet) | 
|  | { | 
|  | Any aAny = rPropSet->getPropertyValue(sRedlineIdentifier); | 
|  | OUString sTmp; | 
|  | aAny >>= sTmp; | 
|  |  | 
|  | OUStringBuffer sBuf(sChangePrefix); | 
|  | sBuf.append(sTmp); | 
|  | return sBuf.makeStringAndClear(); | 
|  | } | 
|  |  | 
|  |  | 
|  | void XMLRedlineExport::ExportChangeInfo( | 
|  | const Reference<XPropertySet> & rPropSet) | 
|  | { | 
|  |  | 
|  | SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE, | 
|  | XML_CHANGE_INFO, sal_True, sal_True); | 
|  |  | 
|  | Any aAny = rPropSet->getPropertyValue(sRedlineAuthor); | 
|  | OUString sTmp; | 
|  | aAny >>= sTmp; | 
|  | if (sTmp.getLength() > 0) | 
|  | { | 
|  | SvXMLElementExport aCreatorElem( rExport, XML_NAMESPACE_DC, | 
|  | XML_CREATOR, sal_True, | 
|  | sal_False ); | 
|  | rExport.Characters(sTmp); | 
|  | } | 
|  |  | 
|  | aAny = rPropSet->getPropertyValue(sRedlineDateTime); | 
|  | util::DateTime aDateTime; | 
|  | aAny >>= aDateTime; | 
|  | { | 
|  | OUStringBuffer sBuf; | 
|  | rExport.GetMM100UnitConverter().convertDateTime(sBuf, aDateTime); | 
|  | SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC, | 
|  | XML_DATE, sal_True, | 
|  | sal_False ); | 
|  | rExport.Characters(sBuf.makeStringAndClear()); | 
|  | } | 
|  |  | 
|  | // comment as <text:p> sequence | 
|  | aAny = rPropSet->getPropertyValue(sRedlineComment); | 
|  | aAny >>= sTmp; | 
|  | WriteComment( sTmp ); | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportChangeInfo( | 
|  | const Sequence<PropertyValue> & rPropertyValues) | 
|  | { | 
|  | OUString sComment; | 
|  |  | 
|  | sal_Int32 nCount = rPropertyValues.getLength(); | 
|  | for(sal_Int32 i = 0; i < nCount; i++) | 
|  | { | 
|  | const PropertyValue& rVal = rPropertyValues[i]; | 
|  |  | 
|  | if( rVal.Name.equals(sRedlineAuthor) ) | 
|  | { | 
|  | OUString sTmp; | 
|  | rVal.Value >>= sTmp; | 
|  | if (sTmp.getLength() > 0) | 
|  | { | 
|  | rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_AUTHOR, sTmp); | 
|  | } | 
|  | } | 
|  | else if( rVal.Name.equals(sRedlineComment) ) | 
|  | { | 
|  | rVal.Value >>= sComment; | 
|  | } | 
|  | else if( rVal.Name.equals(sRedlineDateTime) ) | 
|  | { | 
|  | util::DateTime aDateTime; | 
|  | rVal.Value >>= aDateTime; | 
|  | OUStringBuffer sBuf; | 
|  | rExport.GetMM100UnitConverter().convertDateTime(sBuf, aDateTime); | 
|  | rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_DATE_TIME, | 
|  | sBuf.makeStringAndClear()); | 
|  | } | 
|  | else if( rVal.Name.equals(sRedlineType) ) | 
|  | { | 
|  | // check if this is an insertion; cf. comment at calling location | 
|  | OUString sTmp; | 
|  | rVal.Value >>= sTmp; | 
|  | DBG_ASSERT(sTmp.equals(sInsert), | 
|  | "hierarchical change must be insertion"); | 
|  | } | 
|  | // else: unknown value -> ignore | 
|  | } | 
|  |  | 
|  | // finally write element | 
|  | SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE, | 
|  | XML_CHANGE_INFO, sal_True, sal_True); | 
|  |  | 
|  | WriteComment( sComment ); | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportStartOrEndRedline( | 
|  | const Reference<XPropertySet> & rPropSet, | 
|  | sal_Bool bStart) | 
|  | { | 
|  | if( ! rPropSet.is() ) | 
|  | return; | 
|  |  | 
|  | // get appropriate (start or end) property | 
|  | Any aAny; | 
|  | try | 
|  | { | 
|  | aAny = rPropSet->getPropertyValue(bStart ? sStartRedline : sEndRedline); | 
|  | } | 
|  | catch( UnknownPropertyException e ) | 
|  | { | 
|  | // If we don't have the property, there's nothing to do. | 
|  | return; | 
|  | } | 
|  |  | 
|  | Sequence<PropertyValue> aValues; | 
|  | aAny >>= aValues; | 
|  | const PropertyValue* pValues = aValues.getConstArray(); | 
|  |  | 
|  | // seek for redline properties | 
|  | sal_Bool bIsCollapsed = sal_False; | 
|  | sal_Bool bIsStart = sal_True; | 
|  | OUString sId; | 
|  | sal_Bool bIdOK = sal_False; // have we seen an ID? | 
|  | sal_Int32 nLength = aValues.getLength(); | 
|  | for(sal_Int32 i = 0; i < nLength; i++) | 
|  | { | 
|  | if (sRedlineIdentifier.equals(pValues[i].Name)) | 
|  | { | 
|  | pValues[i].Value >>= sId; | 
|  | bIdOK = sal_True; | 
|  | } | 
|  | else if (sIsCollapsed.equals(pValues[i].Name)) | 
|  | { | 
|  | bIsCollapsed = *(sal_Bool*)pValues[i].Value.getValue(); | 
|  | } | 
|  | else if (sIsStart.equals(pValues[i].Name)) | 
|  | { | 
|  | bIsStart = *(sal_Bool*)pValues[i].Value.getValue(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if( bIdOK ) | 
|  | { | 
|  | DBG_ASSERT( sId.getLength() > 0, "Redlines must have IDs" ); | 
|  |  | 
|  | // TODO: use GetRedlineID or elimiate that function | 
|  | OUStringBuffer sBuffer(sChangePrefix); | 
|  | sBuffer.append(sId); | 
|  |  | 
|  | rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID, | 
|  | sBuffer.makeStringAndClear()); | 
|  |  | 
|  | // export the element | 
|  | // (whitespace because we're not inside paragraphs) | 
|  | SvXMLElementExport aChangeElem( | 
|  | rExport, XML_NAMESPACE_TEXT, | 
|  | bIsCollapsed ? XML_CHANGE : | 
|  | ( bIsStart ? XML_CHANGE_START : XML_CHANGE_END ), | 
|  | sal_True, sal_True); | 
|  | } | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportStartOrEndRedline( | 
|  | const Reference<XTextContent> & rContent, | 
|  | sal_Bool bStart) | 
|  | { | 
|  | Reference<XPropertySet> xPropSet(rContent, uno::UNO_QUERY); | 
|  | if (xPropSet.is()) | 
|  | { | 
|  | ExportStartOrEndRedline(xPropSet, bStart); | 
|  | } | 
|  | else | 
|  | { | 
|  | DBG_ERROR("XPropertySet expected"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::ExportStartOrEndRedline( | 
|  | const Reference<XTextSection> & rSection, | 
|  | sal_Bool bStart) | 
|  | { | 
|  | Reference<XPropertySet> xPropSet(rSection, uno::UNO_QUERY); | 
|  | if (xPropSet.is()) | 
|  | { | 
|  | ExportStartOrEndRedline(xPropSet, bStart); | 
|  | } | 
|  | else | 
|  | { | 
|  | DBG_ERROR("XPropertySet expected"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void XMLRedlineExport::WriteComment(const OUString& rComment) | 
|  | { | 
|  | if (rComment.getLength() > 0) | 
|  | { | 
|  | // iterate over all string-pieces separated by return (0x0a) and | 
|  | // put each inside a paragraph element. | 
|  | SvXMLTokenEnumerator aEnumerator(rComment, sal_Char(0x0a)); | 
|  | OUString aSubString; | 
|  | while (aEnumerator.getNextToken(aSubString)) | 
|  | { | 
|  | SvXMLElementExport aParagraph( | 
|  | rExport, XML_NAMESPACE_TEXT, XML_P, sal_True, sal_False); | 
|  | rExport.Characters(aSubString); | 
|  | } | 
|  | } | 
|  | } |