diff options
Diffstat (limited to 'xmloff/source/meta')
-rw-r--r-- | xmloff/source/meta/MetaExportComponent.cxx | 181 | ||||
-rw-r--r-- | xmloff/source/meta/MetaImportComponent.cxx | 97 | ||||
-rw-r--r-- | xmloff/source/meta/xmlmetae.cxx | 480 | ||||
-rw-r--r-- | xmloff/source/meta/xmlmetai.cxx | 320 | ||||
-rw-r--r-- | xmloff/source/meta/xmlversion.cxx | 429 |
5 files changed, 1507 insertions, 0 deletions
diff --git a/xmloff/source/meta/MetaExportComponent.cxx b/xmloff/source/meta/MetaExportComponent.cxx new file mode 100644 index 0000000000..b5ad3177e8 --- /dev/null +++ b/xmloff/source/meta/MetaExportComponent.cxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <MetaExportComponent.hxx> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <osl/diagnose.h> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlmetae.hxx> +#include <PropertySetMerger.hxx> + +#include <unotools/docinfohelper.hxx> + + +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +XMLMetaExportComponent::XMLMetaExportComponent( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + OUString const & implementationName, SvXMLExportFlags nFlags ) +: SvXMLExport( xContext, implementationName, util::MeasureUnit::CM, XML_TEXT, nFlags ) +{ +} + +XMLMetaExportComponent::~XMLMetaExportComponent() +{ +} + +void SAL_CALL XMLMetaExportComponent::setSourceDocument( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent >& xDoc ) +{ + try + { + SvXMLExport::setSourceDocument( xDoc ); + } + catch( lang::IllegalArgumentException& ) + { + // allow to use document properties service without model access + // this is required for document properties exporter + mxDocProps = + uno::Reference< document::XDocumentProperties >::query( xDoc ); + if( !mxDocProps.is() ) + throw lang::IllegalArgumentException(); + } +} + +ErrCode XMLMetaExportComponent::exportDoc( enum XMLTokenEnum ) +{ + uno::Reference< xml::sax::XDocumentHandler > xDocHandler = GetDocHandler(); + + if( !(getExportFlags() & SvXMLExportFlags::OASIS) ) + { + uno::Reference< uno::XComponentContext > xContext = getComponentContext(); + try + { + static const ::comphelper::PropertyMapEntry aInfoMap[] = + { + { OUString("Class"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + }; + uno::Reference< beans::XPropertySet > xConvPropSet( + ::comphelper::GenericPropertySet_CreateInstance( + new ::comphelper::PropertySetInfo( aInfoMap ) ) ); + + xConvPropSet->setPropertyValue("Class", uno::Any(GetXMLToken( XML_TEXT )) ); + + uno::Reference< beans::XPropertySet > xPropSet = + getExportInfo().is() + ? PropertySetMerger_CreateInstance( getExportInfo(), + xConvPropSet ) + : getExportInfo(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xDocHandler), uno::Any(xPropSet), + uno::Any(GetModel()) }; + + // get filter component + xDocHandler.set( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.Oasis2OOoTransformer", aArgs, xContext), + uno::UNO_QUERY_THROW ); + + SetDocHandler( xDocHandler ); + } + catch( css::uno::Exception& ) + { + OSL_FAIL( "Cannot instantiate com.sun.star.comp.Oasis2OOoTransformer!"); + } + } + + + xDocHandler->startDocument(); + + addChaffWhenEncryptedStorage(); + + { + + const SvXMLNamespaceMap& rMap = GetNamespaceMap(); + sal_uInt16 nPos = rMap.GetFirstKey(); + while( USHRT_MAX != nPos ) + { + GetAttrList().AddAttribute( rMap.GetAttrNameByKey( nPos ), rMap.GetNameByKey( nPos ) ); + nPos = GetNamespaceMap().GetNextKey( nPos ); + } + + const char*const pVersion = GetODFVersionAttributeValue(); + + if( pVersion ) + AddAttribute( XML_NAMESPACE_OFFICE, XML_VERSION, + OUString::createFromAscii(pVersion) ); + + SvXMLElementExport aDocElem( *this, XML_NAMESPACE_OFFICE, XML_DOCUMENT_META, + true, true ); + + // NB: office:meta is now written by _ExportMeta + ExportMeta_(); + } + xDocHandler->endDocument(); + return ERRCODE_NONE; +} + +void XMLMetaExportComponent::ExportMeta_() +{ + if (mxDocProps.is()) { + OUString generator( ::utl::DocInfoHelper::GetGeneratorString() ); + // update generator here + mxDocProps->setGenerator(generator); + rtl::Reference<SvXMLMetaExport> pMeta = new SvXMLMetaExport(*this, mxDocProps); + pMeta->Export(); + } else { + SvXMLExport::ExportMeta_(); + } +} + +// methods without content: +void XMLMetaExportComponent::ExportAutoStyles_() {} +void XMLMetaExportComponent::ExportMasterStyles_() {} +void XMLMetaExportComponent::ExportContent_() {} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +XMLMetaExportComponent_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new XMLMetaExportComponent(context, "XMLMetaExportComponent", SvXMLExportFlags::META|SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +XMLMetaExportOOo_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire( + new XMLMetaExportComponent(context, "XMLMetaExportOOo", SvXMLExportFlags::META)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/meta/MetaImportComponent.cxx b/xmloff/source/meta/MetaImportComponent.cxx new file mode 100644 index 0000000000..9d507ab0f2 --- /dev/null +++ b/xmloff/source/meta/MetaImportComponent.cxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/xmlimp.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +namespace { + +class XMLMetaImportComponent : public SvXMLImport +{ +private: + css::uno::Reference< css::document::XDocumentProperties> mxDocProps; + +public: + // XMLMetaImportComponent() throw(); + explicit XMLMetaImportComponent( + const css::uno::Reference< css::uno::XComponentContext >& xContext + ); + +protected: + + virtual SvXMLImportContext *CreateFastContext( sal_Int32 nElement, + const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override; + + // XImporter + virtual void SAL_CALL setTargetDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override; +}; + +} + +// global functions to support the component + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +XMLMetaImportComponent_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new XMLMetaImportComponent(context)); +} + +XMLMetaImportComponent::XMLMetaImportComponent( + const uno::Reference< uno::XComponentContext >& xContext) + : SvXMLImport(xContext, "XMLMetaImportComponent") +{ +} + +SvXMLImportContext *XMLMetaImportComponent::CreateFastContext( sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + if (nElement == XML_ELEMENT( OFFICE, XML_DOCUMENT_META )) + { + if (!mxDocProps.is()) { + throw uno::RuntimeException( + "XMLMetaImportComponent::CreateFastContext: setTargetDocument " + "has not been called", *this); + } + return new SvXMLMetaDocumentContext( + *this, mxDocProps); + } + return nullptr; +} + +void SAL_CALL XMLMetaImportComponent::setTargetDocument( + const uno::Reference< lang::XComponent >& xDoc ) +{ + mxDocProps.set( xDoc, uno::UNO_QUERY ); + if( !mxDocProps.is() ) + throw lang::IllegalArgumentException( + "XMLMetaImportComponent::setTargetDocument: argument is no " + "XDocumentProperties", uno::Reference<uno::XInterface>(*this), 0); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/meta/xmlmetae.cxx b/xmloff/source/meta/xmlmetae.cxx new file mode 100644 index 0000000000..395772ab2a --- /dev/null +++ b/xmloff/source/meta/xmlmetae.cxx @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <i18nlangtag/languagetag.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <utility> +#include <xmloff/xmlmetae.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlnamespace.hxx> + +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/beans/StringPair.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Duration.hpp> +#include <com/sun/star/xml/sax/XSAXSerializable.hpp> + +#include <sax/tools/converter.hxx> + +#include <comphelper/sequence.hxx> +#include <unotools/docinfohelper.hxx> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +static void lcl_AddTwoDigits( OUStringBuffer& rStr, sal_Int32 nVal ) +{ + if ( nVal < 10 ) + rStr.append( '0' ); + rStr.append( nVal ); +} + +OUString +SvXMLMetaExport::GetISODateTimeString( const util::DateTime& rDateTime ) +{ + // return ISO date string "YYYY-MM-DDThh:mm:ss" + + OUStringBuffer sTmp = + OUString::number( static_cast<sal_Int32>(rDateTime.Year) ) + + "-"; + lcl_AddTwoDigits( sTmp, rDateTime.Month ); + sTmp.append( '-' ); + lcl_AddTwoDigits( sTmp, rDateTime.Day ); + sTmp.append( 'T' ); + lcl_AddTwoDigits( sTmp, rDateTime.Hours ); + sTmp.append( ':' ); + lcl_AddTwoDigits( sTmp, rDateTime.Minutes ); + sTmp.append( ':' ); + lcl_AddTwoDigits( sTmp, rDateTime.Seconds ); + + return sTmp.makeStringAndClear(); +} + +void SvXMLMetaExport::SimpleStringElement( const OUString& rText, + sal_uInt16 nNamespace, enum XMLTokenEnum eElementName ) +{ + if ( !rText.isEmpty() ) { + SvXMLElementExport aElem( mrExport, nNamespace, eElementName, + true, false ); + mrExport.Characters( rText ); + } +} + +void SvXMLMetaExport::SimpleDateTimeElement( const util::DateTime & rDate, + sal_uInt16 nNamespace, enum XMLTokenEnum eElementName ) +{ + if (rDate.Month != 0) { // invalid dates are 0-0-0 + OUString sValue = GetISODateTimeString( rDate ); + if ( !sValue.isEmpty() ) { + SvXMLElementExport aElem( mrExport, nNamespace, eElementName, + true, false ); + mrExport.Characters( sValue ); + } + } +} + +void SvXMLMetaExport::MExport_() +{ + // generator + { + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_GENERATOR, + true, true ); + mrExport.Characters( ::utl::DocInfoHelper::GetGeneratorString() ); + } + + // document title + SimpleStringElement ( mxDocProps->getTitle(), + XML_NAMESPACE_DC, XML_TITLE ); + + // description + SimpleStringElement ( mxDocProps->getDescription(), + XML_NAMESPACE_DC, XML_DESCRIPTION ); + + // subject + SimpleStringElement ( mxDocProps->getSubject(), + XML_NAMESPACE_DC, XML_SUBJECT ); + + // created... + SimpleStringElement ( mxDocProps->getAuthor(), + XML_NAMESPACE_META, XML_INITIAL_CREATOR ); + SimpleDateTimeElement( mxDocProps->getCreationDate(), + XML_NAMESPACE_META, XML_CREATION_DATE ); + + // modified... + SimpleStringElement ( mxDocProps->getModifiedBy(), + XML_NAMESPACE_DC, XML_CREATOR ); + SimpleDateTimeElement( mxDocProps->getModificationDate(), + XML_NAMESPACE_DC, XML_DATE ); + + // printed... + SimpleStringElement ( mxDocProps->getPrintedBy(), + XML_NAMESPACE_META, XML_PRINTED_BY ); + SimpleDateTimeElement( mxDocProps->getPrintDate(), + XML_NAMESPACE_META, XML_PRINT_DATE ); + + // keywords + const uno::Sequence< OUString > keywords = mxDocProps->getKeywords(); + for (const auto& rKeyword : keywords) { + SvXMLElementExport aKwElem( mrExport, XML_NAMESPACE_META, XML_KEYWORD, + true, false ); + mrExport.Characters( rKeyword ); + } + + // document language + { + OUString sValue = LanguageTag( mxDocProps->getLanguage()).getBcp47( false); + if (!sValue.isEmpty()) { + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DC, XML_LANGUAGE, + true, false ); + mrExport.Characters( sValue ); + } + } + + // editing cycles + { + SvXMLElementExport aElem( mrExport, + XML_NAMESPACE_META, XML_EDITING_CYCLES, + true, false ); + mrExport.Characters( OUString::number( + mxDocProps->getEditingCycles() ) ); + } + + // editing duration + // property is a int32 (seconds) + { + sal_Int32 secs = mxDocProps->getEditingDuration(); + SvXMLElementExport aElem( mrExport, + XML_NAMESPACE_META, XML_EDITING_DURATION, + true, false ); + OUStringBuffer buf; + ::sax::Converter::convertDuration(buf, util::Duration( + false, 0, 0, 0, secs/3600, (secs%3600)/60, secs%60, 0)); + mrExport.Characters(buf.makeStringAndClear()); + } + + // default target + const OUString sDefTarget = mxDocProps->getDefaultTarget(); + if ( !sDefTarget.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, + sDefTarget ); + + //! define strings for xlink:show values + const XMLTokenEnum eShow = sDefTarget == "_blank" ? XML_NEW : XML_REPLACE; + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, eShow ); + + SvXMLElementExport aElem( mrExport, + XML_NAMESPACE_META,XML_HYPERLINK_BEHAVIOUR, + true, false ); + } + + // auto-reload + const OUString sReloadURL = mxDocProps->getAutoloadURL(); + const sal_Int32 sReloadDelay = mxDocProps->getAutoloadSecs(); + if (sReloadDelay != 0 || !sReloadURL.isEmpty()) + { + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, + mrExport.GetRelativeReference( sReloadURL ) ); + + OUStringBuffer buf; + ::sax::Converter::convertDuration(buf, util::Duration(false, 0, 0, 0, + sReloadDelay/3600, (sReloadDelay%3600)/60, sReloadDelay%60, 0)); + mrExport.AddAttribute( XML_NAMESPACE_META, XML_DELAY, + buf.makeStringAndClear()); + + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_AUTO_RELOAD, + true, false ); + } + + // template + const OUString sTplPath = mxDocProps->getTemplateURL(); + if ( !sTplPath.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST ); + + // template URL + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, + mrExport.GetRelativeReference(sTplPath) ); + + // template name + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TITLE, + mxDocProps->getTemplateName() ); + + // template date + mrExport.AddAttribute( XML_NAMESPACE_META, XML_DATE, + GetISODateTimeString( mxDocProps->getTemplateDate() ) ); + + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_TEMPLATE, + true, false ); + } + + // user defined fields + uno::Reference< beans::XPropertyAccess > xUserDefined( + mxDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW); + const uno::Sequence< beans::PropertyValue > props = + xUserDefined->getPropertyValues(); + for (const auto& rProp : props) { + OUStringBuffer sValueBuffer; + OUStringBuffer sType; + if (!::sax::Converter::convertAny(sValueBuffer, sType, rProp.Value)) + { + continue; + } + mrExport.AddAttribute( XML_NAMESPACE_META, XML_NAME, rProp.Name ); + mrExport.AddAttribute( XML_NAMESPACE_META, XML_VALUE_TYPE, + sType.makeStringAndClear() ); + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, + XML_USER_DEFINED, true, false ); + mrExport.Characters( sValueBuffer.makeStringAndClear() ); + } + + const uno::Sequence< beans::NamedValue > aDocStatistic = + mxDocProps->getDocumentStatistics(); + // write document statistic if there is any provided + if ( !aDocStatistic.hasElements() ) + return; + + for ( const auto& rDocStat : aDocStatistic ) + { + sal_Int32 nValue = 0; + if ( rDocStat.Value >>= nValue ) + { + OUString aValue = OUString::number( nValue ); + if ( rDocStat.Name == "TableCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_TABLE_COUNT, aValue ); + else if ( rDocStat.Name == "ObjectCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_OBJECT_COUNT, aValue ); + else if ( rDocStat.Name == "ImageCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_IMAGE_COUNT, aValue ); + else if ( rDocStat.Name == "PageCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_PAGE_COUNT, aValue ); + else if ( rDocStat.Name == "ParagraphCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_PARAGRAPH_COUNT, aValue ); + else if ( rDocStat.Name == "WordCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_WORD_COUNT, aValue ); + else if ( rDocStat.Name == "CharacterCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_CHARACTER_COUNT, aValue ); + else if ( rDocStat.Name == "CellCount" ) + mrExport.AddAttribute( + XML_NAMESPACE_META, XML_CELL_COUNT, aValue ); + else + { + SAL_WARN("xmloff", "Unknown statistic value!"); + } + } + } + SvXMLElementExport aElem( mrExport, + XML_NAMESPACE_META, XML_DOCUMENT_STATISTIC, true, true ); +} + +const char s_xmlns[] = "xmlns"; +const char s_xmlns2[] = "xmlns:"; +const char s_meta[] = "meta:"; +const char s_href[] = "xlink:href"; + +SvXMLMetaExport::SvXMLMetaExport( + SvXMLExport& i_rExp, + uno::Reference<document::XDocumentProperties> i_rDocProps ) : + mrExport( i_rExp ), + mxDocProps(std::move( i_rDocProps )), + m_level( 0 ) +{ + assert(mxDocProps.is()); +} + +SvXMLMetaExport::~SvXMLMetaExport() +{ +} + +void SvXMLMetaExport::Export() +{ + uno::Reference< xml::sax::XSAXSerializable> xSAXable(mxDocProps, + uno::UNO_QUERY); + if (xSAXable.is()) { + ::std::vector< beans::StringPair > namespaces; + const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap()); + for (sal_uInt16 key = rNsMap.GetFirstKey(); + key != USHRT_MAX; key = rNsMap.GetNextKey(key)) { + beans::StringPair ns; + const OUString attrname = rNsMap.GetAttrNameByKey(key); + if (!attrname.startsWith(s_xmlns2, &ns.First) + || attrname == s_xmlns) // default initialized empty string + { + assert(!"namespace attribute not starting with xmlns unexpected"); + } + ns.Second = rNsMap.GetNameByKey(key); + namespaces.push_back(ns); + } + xSAXable->serialize(this, comphelper::containerToSequence(namespaces)); + } else { + // office:meta + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_OFFICE, XML_META, + true, true ); + // fall back to using public interface of XDocumentProperties + MExport_(); + } +} + +// css::xml::sax::XDocumentHandler: +void SAL_CALL +SvXMLMetaExport::startDocument() +{ + // ignore: has already been done by SvXMLExport::exportDoc + assert(m_level == 0 && "SvXMLMetaExport: level error"); +} + +void SAL_CALL +SvXMLMetaExport::endDocument() +{ + // ignore: will be done by SvXMLExport::exportDoc + assert(m_level == 0 && "SvXMLMetaExport: level error"); +} + +// unfortunately, this method contains far too much ugly namespace mangling. +void SAL_CALL +SvXMLMetaExport::startElement(const OUString & i_rName, + const uno::Reference< xml::sax::XAttributeList > & i_xAttribs) +{ + + if (m_level == 0) { + // namespace decls: default ones have been written at the root element + // non-default ones must be preserved here + const sal_Int16 nCount = i_xAttribs->getLength(); + for (sal_Int16 i = 0; i < nCount; ++i) { + const OUString name(i_xAttribs->getNameByIndex(i)); + if (name.startsWith(s_xmlns)) { + bool found(false); + const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap()); + for (sal_uInt16 key = rNsMap.GetFirstKey(); + key != USHRT_MAX; key = rNsMap.GetNextKey(key)) { + if (name == rNsMap.GetAttrNameByKey(key)) { + found = true; + break; + } + } + if (!found) { + m_preservedNSs.emplace_back(name, + i_xAttribs->getValueByIndex(i)); + } + } + } + // ignore the root: it has been written already + ++m_level; + return; + } + + if (m_level == 1) { + // attach preserved namespace decls from root node here + for (const auto& rPreservedNS : m_preservedNSs) { + const OUString ns(rPreservedNS.First); + bool found(false); + // but only if it is not already there + const sal_Int16 nCount = i_xAttribs->getLength(); + for (sal_Int16 i = 0; i < nCount; ++i) { + const OUString name(i_xAttribs->getNameByIndex(i)); + if (ns == name) { + found = true; + break; + } + } + if (!found) { + mrExport.AddAttribute(ns, rPreservedNS.Second); + } + } + } + + // attach the attributes + if (i_rName.startsWith(s_meta)) { + // special handling for all elements that may have + // xlink:href attributes; these must be made relative + const sal_Int16 nLength = i_xAttribs->getLength(); + for (sal_Int16 i = 0; i < nLength; ++i) { + const OUString name (i_xAttribs->getNameByIndex (i)); + OUString value(i_xAttribs->getValueByIndex(i)); + if (name.startsWith(s_href)) { + value = mrExport.GetRelativeReference(value); + } + mrExport.AddAttribute(name, value); + } + } else { + const sal_Int16 nLength = i_xAttribs->getLength(); + for (sal_Int16 i = 0; i < nLength; ++i) { + const OUString name (i_xAttribs->getNameByIndex(i)); + const OUString value (i_xAttribs->getValueByIndex(i)); + mrExport.AddAttribute(name, value); + } + } + + // finally, start the element + // #i107240# no whitespace here, because the DOM may already contain + // whitespace, which is not cleared when loading and thus accumulates. + mrExport.StartElement(i_rName, m_level <= 1); + ++m_level; +} + +void SAL_CALL +SvXMLMetaExport::endElement(const OUString & i_rName) +{ + --m_level; + if (m_level == 0) { + // ignore the root; see startElement + return; + } + assert(m_level >= 0 && "SvXMLMetaExport: level error"); + mrExport.EndElement(i_rName, false); +} + +void SAL_CALL +SvXMLMetaExport::characters(const OUString & i_rChars) +{ + mrExport.Characters(i_rChars); +} + +void SAL_CALL +SvXMLMetaExport::ignorableWhitespace(const OUString & /*i_rWhitespaces*/) +{ + mrExport.IgnorableWhitespace(/*i_rWhitespaces*/); +} + +void SAL_CALL +SvXMLMetaExport::processingInstruction(const OUString &, + const OUString &) +{ + // ignore; the exporter cannot handle these +} + +void SAL_CALL +SvXMLMetaExport::setDocumentLocator(const uno::Reference<xml::sax::XLocator>&) +{ + // nothing to do here, move along... +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/meta/xmlmetai.cxx b/xmloff/source/meta/xmlmetai.cxx new file mode 100644 index 0000000000..09324ba8b2 --- /dev/null +++ b/xmloff/source/meta/xmlmetai.cxx @@ -0,0 +1,320 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/xml/dom/SAXDocumentBuilder.hpp> +#include <com/sun/star/xml/dom/XSAXDocumentBuilder2.hpp> +#include <com/sun/star/xml/xpath/XPathAPI.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> +#include <utility> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlnamespace.hxx> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +namespace { + +/// builds a DOM tree from SAX events, by forwarding to SAXDocumentBuilder +class XMLDocumentBuilderContext : public SvXMLImportContext +{ +private: + css::uno::Reference< css::xml::dom::XSAXDocumentBuilder2> mxDocBuilder; + SvXMLMetaDocumentContext *const m_pTopLevel; + +public: + XMLDocumentBuilderContext(SvXMLImport& rImport, sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList, + css::uno::Reference<css::xml::dom::XSAXDocumentBuilder2> xDocBuilder, + SvXMLMetaDocumentContext * pTopLevel); + + virtual void SAL_CALL characters( const OUString& aChars ) override; + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override; + + virtual void SAL_CALL startUnknownElement( const OUString& Namespace, const OUString& Name, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; + + virtual void SAL_CALL endUnknownElement( const OUString& Namespace, const OUString& Name ) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + +}; + +} + +XMLDocumentBuilderContext::XMLDocumentBuilderContext(SvXMLImport& rImport, + sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>&, + uno::Reference<xml::dom::XSAXDocumentBuilder2> xDocBuilder, + SvXMLMetaDocumentContext *const pTopLevel) + : SvXMLImportContext(rImport) + , mxDocBuilder(std::move(xDocBuilder)) + , m_pTopLevel(pTopLevel) +{ +} + +void SAL_CALL XMLDocumentBuilderContext::startFastElement( sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& xAttribs ) +{ + mxDocBuilder->startFastElement(nElement, xAttribs); +} + +void SAL_CALL XMLDocumentBuilderContext::endFastElement( sal_Int32 nElement ) +{ + mxDocBuilder->endFastElement(nElement); + if (m_pTopLevel) + { + // call this here because in the flat ODF case the top-level + // endFastElement is called only at the very end of the document, + // which is too late to init BuildId + m_pTopLevel->FinishMetaElement(); + } +} + +void SAL_CALL XMLDocumentBuilderContext::startUnknownElement( const OUString& rNamespace, + const OUString& rName, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + mxDocBuilder->startUnknownElement(rNamespace, rName, xAttrList); +} + +void SAL_CALL XMLDocumentBuilderContext::endUnknownElement( const OUString& rNamespace, const OUString& rName ) +{ + mxDocBuilder->endUnknownElement(rNamespace, rName); +} + +void SAL_CALL XMLDocumentBuilderContext::characters( const OUString& rChars ) +{ + mxDocBuilder->characters(rChars); +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL XMLDocumentBuilderContext::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + return new XMLDocumentBuilderContext(GetImport(), nElement, xAttrList, mxDocBuilder, nullptr); +} + +static void +lcl_initDocumentProperties(SvXMLImport & rImport, + uno::Reference<xml::dom::XSAXDocumentBuilder2> const& xDocBuilder, + uno::Reference<document::XDocumentProperties> const& xDocProps) +{ + uno::Reference< lang::XInitialization > const xInit(xDocProps, + uno::UNO_QUERY_THROW); + try { + xInit->initialize({ uno::Any(xDocBuilder->getDocument()) }); + rImport.SetStatistics(xDocProps->getDocumentStatistics()); + // convert all URLs from relative to absolute + xDocProps->setTemplateURL(rImport.GetAbsoluteReference( + xDocProps->getTemplateURL())); + xDocProps->setAutoloadURL(rImport.GetAbsoluteReference( + xDocProps->getAutoloadURL())); + SvXMLMetaDocumentContext::setBuildId( + xDocProps->getGenerator(), rImport.getImportInfo()); + } catch (const uno::RuntimeException&) { + throw; + } catch (const uno::Exception&) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "SvXMLMetaDocumentContext::initDocumentProperties: " + "properties init exception", + rImport, anyEx); + } +} + +static void +lcl_initGenerator(SvXMLImport & rImport, + uno::Reference<xml::dom::XSAXDocumentBuilder2> const& xDocBuilder) +{ + uno::Reference< xml::dom::XDocument > const xDoc(xDocBuilder->getDocument(), + uno::UNO_SET_THROW); + try { + uno::Reference< xml::xpath::XXPathAPI > const xPath = xml::xpath::XPathAPI::create( + rImport.GetComponentContext() ); + xPath->registerNS(GetXMLToken(XML_NP_OFFICE),GetXMLToken(XML_N_OFFICE)); + xPath->registerNS(GetXMLToken(XML_NP_META), GetXMLToken(XML_N_META)); + + uno::Reference< xml::xpath::XXPathObject > const xObj( + xPath->eval(xDoc, "string(/office:document-meta/office:meta/meta:generator)"), + uno::UNO_SET_THROW); + OUString const value(xObj->getString()); + SvXMLMetaDocumentContext::setBuildId(value, rImport.getImportInfo()); + } catch (const uno::RuntimeException&) { + throw; + } catch (const uno::Exception&) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "SvXMLMetaDocumentContext::initGenerator: exception", + rImport, anyEx); + } +} + +SvXMLMetaDocumentContext::SvXMLMetaDocumentContext(SvXMLImport& rImport, + uno::Reference<document::XDocumentProperties> xDocProps) : + SvXMLImportContext( rImport ), + mxDocProps(std::move(xDocProps)), + mxDocBuilder( + xml::dom::SAXDocumentBuilder::create( + comphelper::getProcessComponentContext())) +{ +// #i103539#: must always read meta.xml for generator, xDocProps unwanted then +// OSL_ENSURE(xDocProps.is(), "SvXMLMetaDocumentContext: no document props"); +} + +SvXMLMetaDocumentContext::~SvXMLMetaDocumentContext() +{ +} + +void SAL_CALL SvXMLMetaDocumentContext::startFastElement(sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + mxDocBuilder->startDocument(); + // hardcode office:document-meta (necessary in case of flat file ODF) + mxDocBuilder->startFastElement(XML_ELEMENT(OFFICE, XML_DOCUMENT_META), xAttrList); +} + +void SvXMLMetaDocumentContext::FinishMetaElement() +{ + // hardcode office:document-meta (necessary in case of flat file ODF) + mxDocBuilder->endFastElement(XML_ELEMENT(OFFICE, XML_DOCUMENT_META)); + mxDocBuilder->endDocument(); + if (mxDocProps.is()) + { + lcl_initDocumentProperties(GetImport(), mxDocBuilder, mxDocProps); + } + else + { + lcl_initGenerator(GetImport(), mxDocBuilder); + } +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SvXMLMetaDocumentContext::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + if ( nElement == XML_ELEMENT(OFFICE, XML_META) ) + return new XMLDocumentBuilderContext( + GetImport(), nElement, xAttrList, mxDocBuilder, this); + return nullptr; +} + +void SvXMLMetaDocumentContext::setBuildId(std::u16string_view i_rBuildId, const uno::Reference<beans::XPropertySet>& xImportInfo ) +{ + OUString sBuildId; + // skip to second product + size_t nBegin = i_rBuildId.find( ' ' ); + if ( nBegin != std::u16string_view::npos ) + { + // skip to build information + nBegin = i_rBuildId.find( '/', nBegin ); + if ( nBegin != std::u16string_view::npos ) + { + size_t nEnd = i_rBuildId.find( 'm', nBegin ); + if ( nEnd != std::u16string_view::npos ) + { + OUStringBuffer sBuffer( + i_rBuildId.substr( nBegin+1, nEnd-nBegin-1 ) ); + static constexpr OUString sBuildCompare( + u"$Build-"_ustr ); + nBegin = i_rBuildId.find( sBuildCompare, nEnd ); + if ( nBegin != std::u16string_view::npos ) + { + sBuffer.append( '$' ); + sBuffer.append( i_rBuildId.substr(nBegin + sBuildCompare.getLength()) ); + sBuildId = sBuffer.makeStringAndClear(); + } + } + } + } + + if ( sBuildId.isEmpty() ) + { + if ( o3tl::starts_with(i_rBuildId, u"StarOffice 7") + || o3tl::starts_with(i_rBuildId, u"StarSuite 7") + || o3tl::starts_with(i_rBuildId, u"StarOffice 6") + || o3tl::starts_with(i_rBuildId, u"StarSuite 6") + || o3tl::starts_with(i_rBuildId, u"OpenOffice.org 1")) + { + sBuildId = "645$8687"; + } + else if (o3tl::starts_with(i_rBuildId, u"NeoOffice/2")) + { + sBuildId = "680$9134"; // fake NeoOffice as OpenOffice.org 2.2 release + } + } + + // "LibreOffice_project" was hard-coded since LO 3.3.0 + // see utl::DocInfoHelper::GetGeneratorString() + if (i_rBuildId.find(u"LibreOffice_project/") != std::u16string_view::npos) + { + OUStringBuffer sNumber; + size_t const firstSlash = i_rBuildId.find('/'); + assert(firstSlash != std::u16string_view::npos); + for (size_t i = firstSlash + 1; i < i_rBuildId.size(); ++i) + { + if (rtl::isAsciiDigit(i_rBuildId[i]) || '.' == i_rBuildId[i]) + { + sNumber.append(i_rBuildId[i]); + } + else + { + break; + } + } + if (!sNumber.isEmpty()) + { + sBuildId += ";" + sNumber; + } + } + + if ( sBuildId.isEmpty() ) + return; + + try + { + if( xImportInfo.is() ) + { + static constexpr OUString aPropName(u"BuildId"_ustr); + uno::Reference< beans::XPropertySetInfo > xSetInfo( + xImportInfo->getPropertySetInfo()); + if( xSetInfo.is() && xSetInfo->hasPropertyByName( aPropName ) ) + xImportInfo->setPropertyValue( aPropName, uno::Any( sBuildId ) ); + } + } + catch(const uno::Exception&) + { + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/meta/xmlversion.cxx b/xmloff/source/meta/xmlversion.cxx new file mode 100644 index 0000000000..1554fff9aa --- /dev/null +++ b/xmloff/source/meta/xmlversion.cxx @@ -0,0 +1,429 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <com/sun/star/embed/ElementModes.hpp> +#include <xmlversion.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlmetae.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <xmloff/xmltoken.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <cppuhelper/supportsservice.hxx> + +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +constexpr OUString XMLN_VERSIONSLIST = u"VersionList.xml"_ustr; + +XMLVersionListExport::XMLVersionListExport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Sequence < css::util::RevisionTag >& rVersions, + const OUString &rFileName, + Reference< XDocumentHandler > const &rHandler ) +: SvXMLExport( rContext, "", rFileName, util::MeasureUnit::CM, rHandler ), + maVersions( rVersions ) +{ + GetNamespaceMap_().AddAtIndex( xmloff::token::GetXMLToken(xmloff::token::XML_NP_DC), + xmloff::token::GetXMLToken(xmloff::token::XML_N_DC), XML_NAMESPACE_DC ); + GetNamespaceMap_().AddAtIndex( xmloff::token::GetXMLToken(xmloff::token::XML_NP_VERSIONS_LIST), + xmloff::token::GetXMLToken(xmloff::token::XML_N_VERSIONS_LIST), XML_NAMESPACE_FRAMEWORK ); +} + +ErrCode XMLVersionListExport::exportDoc( enum ::xmloff::token::XMLTokenEnum ) +{ + GetDocHandler()->startDocument(); + + addChaffWhenEncryptedStorage(); + + sal_uInt16 nPos = SvXMLNamespaceMap::GetIndexByKey( XML_NAMESPACE_DC ); + + AddAttribute( XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByIndex( nPos ), + GetNamespaceMap_().GetNameByIndex ( nPos ) ); + + nPos = SvXMLNamespaceMap::GetIndexByKey( XML_NAMESPACE_FRAMEWORK ); + AddAttribute( XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByIndex( nPos ), + GetNamespaceMap_().GetNameByIndex ( nPos ) ); + + { + // the following object will write all collected attributes in its dtor + SvXMLElementExport aRoot( *this, XML_NAMESPACE_FRAMEWORK, xmloff::token::XML_VERSION_LIST, true, true ); + + for ( const util::RevisionTag& rInfo : maVersions ) + { + AddAttribute( XML_NAMESPACE_FRAMEWORK, + xmloff::token::XML_TITLE, + rInfo.Identifier ); + AddAttribute( XML_NAMESPACE_FRAMEWORK, + xmloff::token::XML_COMMENT, + rInfo.Comment ); + AddAttribute( XML_NAMESPACE_FRAMEWORK, + xmloff::token::XML_CREATOR, + rInfo.Author ); + + OUString aDateStr = + SvXMLMetaExport::GetISODateTimeString( rInfo.TimeStamp ); + + AddAttribute( XML_NAMESPACE_DC, xmloff::token::XML_DATE_TIME, aDateStr ); + + // the following object will write all collected attributes in its dtor + SvXMLElementExport aEntry( *this, XML_NAMESPACE_FRAMEWORK, xmloff::token::XML_VERSION_ENTRY, true, true ); + } + } + GetDocHandler()->endDocument(); + return ERRCODE_NONE; +} + +XMLVersionListImport::XMLVersionListImport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + css::uno::Sequence < css::util::RevisionTag >& rVersions ) +: SvXMLImport(rContext, ""), + maVersions( rVersions ) +{ +} + +XMLVersionListImport::~XMLVersionListImport() noexcept +{} + +SvXMLImportContext *XMLVersionListImport::CreateFastContext( sal_Int32 nElement, + const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + SvXMLImportContext *pContext = nullptr; + + if ( nElement == XML_ELEMENT(VERSIONS_LIST, xmloff::token::XML_VERSION_LIST) ) + { + pContext = new XMLVersionListContext( *this ); + } + + return pContext; +} + +XMLVersionListContext::XMLVersionListContext( XMLVersionListImport& rImport) + : SvXMLImportContext( rImport ) +{ +} + +XMLVersionListContext::~XMLVersionListContext() +{} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL +XMLVersionListContext::createFastChildContext(sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList) +{ + SvXMLImportContext *pContext = nullptr; + + if ( nElement == XML_ELEMENT(FRAMEWORK, xmloff::token::XML_VERSION_ENTRY) + || nElement == XML_ELEMENT(VERSIONS_LIST, xmloff::token::XML_VERSION_ENTRY) ) + { + pContext = new XMLVersionContext( GetImport(), xAttrList ); + } + + return pContext; +} + +XMLVersionContext::XMLVersionContext( XMLVersionListImport& rImport, + const Reference< XFastAttributeList > & xAttrList ) + : SvXMLImportContext( rImport ) +{ + sax_fastparser::FastAttributeList& rAttribList = + sax_fastparser::castToFastAttributeList( xAttrList ); + if ( rAttribList.getFastAttributeTokens().empty() ) + return; + util::RevisionTag aInfo; + for (auto &aIter : rAttribList) + { + switch( aIter.getToken() ) + { + case XML_ELEMENT(FRAMEWORK, xmloff::token::XML_TITLE): + case XML_ELEMENT(VERSIONS_LIST, xmloff::token::XML_TITLE): + { + aInfo.Identifier = aIter.toString(); + break; + } + case XML_ELEMENT(FRAMEWORK, xmloff::token::XML_COMMENT): + case XML_ELEMENT(VERSIONS_LIST, xmloff::token::XML_COMMENT): + { + aInfo.Comment = aIter.toString(); + break; + } + case XML_ELEMENT(FRAMEWORK, xmloff::token::XML_CREATOR): + case XML_ELEMENT(VERSIONS_LIST, xmloff::token::XML_CREATOR): + { + aInfo.Author = aIter.toString(); + break; + } + case XML_ELEMENT(DC, xmloff::token::XML_DATE_TIME): + { + util::DateTime aTime; + if ( ParseISODateTimeString( aIter.toString(), aTime ) ) + aInfo.TimeStamp = aTime; + break; + } + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + break; + } + } + + uno::Sequence < util::RevisionTag >& aList = rImport.GetList(); + sal_Int32 nLength = aList.getLength(); + aList.realloc( nLength+1 ); + aList.getArray()[nLength] = aInfo; +} + +XMLVersionContext::~XMLVersionContext() +{} + +bool XMLVersionContext::ParseISODateTimeString( + std::u16string_view rString, + util::DateTime& rDateTime ) +{ + bool bSuccess = true; + + std::u16string_view aDateStr, aTimeStr; + size_t nPos = rString.find( 'T' ); + if ( nPos != std::u16string_view::npos ) + { + aDateStr = rString.substr( 0, nPos ); + aTimeStr = rString.substr( nPos + 1 ); + } + else + aDateStr = rString; // no separator: only date part + + sal_Int32 nYear = 0; + sal_Int32 nMonth = 1; + sal_Int32 nDay = 1; + sal_Int32 nHour = 0; + sal_Int32 nMin = 0; + sal_Int32 nSec = 0; + + auto pStr = aDateStr.begin(); + sal_Int32 nDateTokens = 1; + while ( pStr != aDateStr.end() ) + { + if ( *pStr == '-' ) + nDateTokens++; + pStr++; + } + if ( nDateTokens > 3 || aDateStr.empty() ) + bSuccess = false; + else + { + sal_Int32 n = 0; + nYear = o3tl::toInt32(o3tl::getToken(aDateStr, 0, '-', n )); + if ( nYear > 9999 ) + bSuccess = false; + else if ( nDateTokens >= 2 ) + { + nMonth = o3tl::toInt32(o3tl::getToken(aDateStr, 0, '-', n )); + if ( nMonth > 12 ) + bSuccess = false; + else if ( nDateTokens >= 3 ) + { + nDay = o3tl::toInt32(o3tl::getToken(aDateStr, 0, '-', n )); + if ( nDay > 31 ) + bSuccess = false; + } + } + } + + if ( bSuccess && !aTimeStr.empty() ) // time is optional + { + pStr = aTimeStr.begin(); + sal_Int32 nTimeTokens = 1; + while ( pStr != aTimeStr.end() ) + { + if ( *pStr == ':' ) + nTimeTokens++; + pStr++; + } + if ( nTimeTokens > 3 ) + bSuccess = false; + else + { + sal_Int32 n = 0; + nHour = o3tl::toInt32(o3tl::getToken(aTimeStr, 0, ':', n )); + if ( nHour > 23 ) + bSuccess = false; + else if ( nTimeTokens >= 2 ) + { + nMin = o3tl::toInt32(o3tl::getToken(aTimeStr, 0, ':', n )); + if ( nMin > 59 ) + bSuccess = false; + else if ( nTimeTokens >= 3 ) + { + nSec = o3tl::toInt32(o3tl::getToken(aTimeStr, 0, ':', n )); + if ( nSec > 59 ) + bSuccess = false; + } + } + } + } + + if ( bSuccess ) + { + rDateTime.Day = sal::static_int_cast< sal_uInt16 >(nDay); + rDateTime.Month = sal::static_int_cast< sal_uInt16 >(nMonth); + rDateTime.Year = sal::static_int_cast< sal_uInt16 >(nYear); + rDateTime.Hours = sal::static_int_cast< sal_uInt16 >(nHour); + rDateTime.Minutes = sal::static_int_cast< sal_uInt16 >(nMin); + rDateTime.Seconds = sal::static_int_cast< sal_uInt16 >(nSec); + } + + return bSuccess; +} + +void SAL_CALL XMLVersionListPersistence::store( const uno::Reference< embed::XStorage >& xRoot, const uno::Sequence< util::RevisionTag >& rVersions ) +{ + // no storage, no version list! + if ( !xRoot.is() ) + return; + + // get the services needed for writing the xml data + Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + Reference< XWriter > xWriter = Writer::create(xContext); + + // check whether there's already a sub storage with the version info + // and delete it + OUString sVerName( XMLN_VERSIONSLIST ); + + try { + // open (create) the sub storage with the version info + uno::Reference< io::XStream > xVerStream = xRoot->openStreamElement( + sVerName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + if ( !xVerStream.is() ) + throw uno::RuntimeException(); + + Reference< io::XOutputStream > xOut = xVerStream->getOutputStream(); + if ( !xOut.is() ) + throw uno::RuntimeException("The stream was successfully opened for writing already!"); + + xWriter->setOutputStream(xOut); + + rtl::Reference< XMLVersionListExport > xExp( new XMLVersionListExport( xContext, rVersions, sVerName, xWriter ) ); + + xExp->exportDoc( ::xmloff::token::XML_VERSION ); + + xVerStream.clear(); // use refcounting for now to dispose + } + catch( uno::Exception& ) + { + // TODO: error handling + } +} + +uno::Sequence< util::RevisionTag > SAL_CALL XMLVersionListPersistence::load( const uno::Reference< embed::XStorage >& xRoot ) +{ + css::uno::Sequence < css::util::RevisionTag > aVersions; + + const OUString sDocName( XMLN_VERSIONSLIST ); + + try { + if ( xRoot.is() && xRoot->hasByName( sDocName ) && xRoot->isStreamElement( sDocName ) ) + { + Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + + InputSource aParserInput; + + uno::Reference< beans::XPropertySet > xProps( xRoot, uno::UNO_QUERY ); + OSL_ENSURE( xProps.is(), "Storage must implement XPropertySet!" ); + if ( xProps.is() ) + { + try { + xProps->getPropertyValue("URL") >>= aParserInput.sSystemId; + } + catch( uno::Exception& ) + {} + } + + uno::Reference< io::XStream > xDocStream = xRoot->openStreamElement( + sDocName, + embed::ElementModes::READ ); + if ( !xDocStream.is() ) + throw uno::RuntimeException(); + + aParserInput.aInputStream = xDocStream->getInputStream(); + OSL_ENSURE( aParserInput.aInputStream.is(), + "The stream was successfully opened for reading, the input part must be accessible!" ); + if ( !aParserInput.aInputStream.is() ) + throw uno::RuntimeException(); + + // get filter + rtl::Reference< XMLVersionListImport > xImport = new XMLVersionListImport( xContext, aVersions ); + + // parse + try + { + xImport->parseStream( aParserInput ); + } + catch( SAXParseException& ) {} + catch( SAXException& ) {} + catch( io::IOException& ) {} + } + } + catch( uno::Exception& ) + { + // TODO: error handling + } + + return aVersions; +} + +OUString XMLVersionListPersistence::getImplementationName() +{ + return "XMLVersionListPersistence"; +} + +sal_Bool XMLVersionListPersistence::supportsService( + OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> +XMLVersionListPersistence::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.document.DocumentRevisionListPersistence"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +XMLVersionListPersistence_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new XMLVersionListPersistence); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |