bump product version to 5.0.4.1
[LibreOffice.git] / xmloff / source / text / XMLTextMarkImportContext.cxx
blobf0d34582f1801f0855069796485c75d750808aed
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "XMLTextMarkImportContext.hxx"
23 #include <rtl/ustring.hxx>
24 #include <osl/diagnose.h>
25 #include <tools/debug.hxx>
26 #include <xmloff/xmluconv.hxx>
27 #include <xmloff/xmltoken.hxx>
28 #include <xmloff/xmlimp.hxx>
29 #include <xmloff/nmspmap.hxx>
30 #include <xmloff/xmlnmspe.hxx>
31 #include <xmloff/odffields.hxx>
32 #include <com/sun/star/xml/sax/XAttributeList.hpp>
33 #include <com/sun/star/text/XTextContent.hpp>
34 #include <com/sun/star/beans/XPropertySet.hpp>
35 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
36 #include <com/sun/star/container/XNamed.hpp>
37 #include <com/sun/star/rdf/XMetadatable.hpp>
39 #include <com/sun/star/text/XFormField.hpp>
41 #include "RDFaImportHelper.hxx"
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::text;
47 using namespace ::com::sun::star::uno;
48 using namespace ::com::sun::star::beans;
49 using namespace ::com::sun::star::lang;
50 using namespace ::com::sun::star::container;
51 using namespace ::com::sun::star::xml::sax;
52 using namespace ::xmloff::token;
56 XMLFieldParamImportContext::XMLFieldParamImportContext(
57 SvXMLImport& rImport,
58 XMLTextImportHelper& rHlp,
59 sal_uInt16 nPrefix,
60 const OUString& rLocalName ) :
61 SvXMLImportContext(rImport, nPrefix, rLocalName),
62 rHelper(rHlp)
67 void XMLFieldParamImportContext::StartElement(const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList> & xAttrList)
69 SvXMLImport& rImport = GetImport();
70 OUString sName;
71 OUString sValue;
73 sal_Int16 nLength = xAttrList->getLength();
74 for(sal_Int16 nAttr = 0; nAttr < nLength; nAttr++)
76 OUString sLocalName;
77 sal_uInt16 nPrefix = rImport.GetNamespaceMap().
78 GetKeyByAttrName( xAttrList->getNameByIndex(nAttr),
79 &sLocalName );
81 if ( (XML_NAMESPACE_FIELD == nPrefix) &&
82 IsXMLToken(sLocalName, XML_NAME) )
84 sName = xAttrList->getValueByIndex(nAttr);
86 if ( (XML_NAMESPACE_FIELD == nPrefix) &&
87 IsXMLToken(sLocalName, XML_VALUE) )
89 sValue = xAttrList->getValueByIndex(nAttr);
92 if (rHelper.hasCurrentFieldCtx() && !sName.isEmpty()) {
93 rHelper.addFieldParam(sName, sValue);
98 TYPEINIT1( XMLTextMarkImportContext, SvXMLImportContext);
100 XMLTextMarkImportContext::XMLTextMarkImportContext(
101 SvXMLImport& rImport,
102 XMLTextImportHelper& rHlp,
103 sal_uInt16 nPrefix,
104 const OUString& rLocalName )
105 : SvXMLImportContext(rImport, nPrefix, rLocalName)
106 , m_rHelper(rHlp)
107 , m_bHaveAbout(false)
111 enum lcl_MarkType { TypeReference, TypeReferenceStart, TypeReferenceEnd,
112 TypeBookmark, TypeBookmarkStart, TypeBookmarkEnd,
113 TypeFieldmark, TypeFieldmarkStart, TypeFieldmarkEnd
116 static SvXMLEnumMapEntry const lcl_aMarkTypeMap[] =
118 { XML_REFERENCE_MARK, TypeReference },
119 { XML_REFERENCE_MARK_START, TypeReferenceStart },
120 { XML_REFERENCE_MARK_END, TypeReferenceEnd },
121 { XML_BOOKMARK, TypeBookmark },
122 { XML_BOOKMARK_START, TypeBookmarkStart },
123 { XML_BOOKMARK_END, TypeBookmarkEnd },
124 { XML_FIELDMARK, TypeFieldmark },
125 { XML_FIELDMARK_START, TypeFieldmarkStart },
126 { XML_FIELDMARK_END, TypeFieldmarkEnd },
127 { XML_TOKEN_INVALID, 0 },
131 static const char *lcl_getFormFieldmarkName(OUString &name)
133 static const char sCheckbox[]=ODF_FORMCHECKBOX;
134 static const char sFormDropDown[]=ODF_FORMDROPDOWN;
135 if (name == "msoffice.field.FORMCHECKBOX" ||
136 name == "ecma.office-open-xml.field.FORMCHECKBOX")
137 return sCheckbox;
138 else if (name == ODF_FORMCHECKBOX)
139 return sCheckbox;
140 if (name == ODF_FORMDROPDOWN ||
141 name == "ecma.office-open-xml.field.FORMDROPDOWN")
142 return sFormDropDown;
143 else
144 return NULL;
147 static OUString lcl_getFieldmarkName(OUString const& name)
149 if (name == "msoffice.field.FORMTEXT" ||
150 name == "ecma.office-open-xml.field.FORMTEXT")
151 return OUString(ODF_FORMTEXT);
152 else
153 return name;
157 void XMLTextMarkImportContext::StartElement(
158 const Reference<XAttributeList> & xAttrList)
160 if (!FindName(GetImport(), xAttrList))
162 m_sBookmarkName.clear();
165 if (IsXMLToken(GetLocalName(), XML_FIELDMARK_END))
167 m_sBookmarkName = m_rHelper.FindActiveBookmarkName();
170 if (IsXMLToken(GetLocalName(), XML_FIELDMARK_START) || IsXMLToken(GetLocalName(), XML_FIELDMARK))
172 if (m_sBookmarkName.isEmpty())
174 m_sBookmarkName = "Unknown";
176 m_rHelper.pushFieldCtx( m_sBookmarkName, m_sFieldName );
180 void XMLTextMarkImportContext::EndElement()
182 SvXMLImportContext::EndElement();
184 static const char sAPI_reference_mark[] = "com.sun.star.text.ReferenceMark";
185 static const char sAPI_bookmark[] = "com.sun.star.text.Bookmark";
186 static const char sAPI_fieldmark[] = "com.sun.star.text.Fieldmark";
187 static const char sAPI_formfieldmark[] = "com.sun.star.text.FormFieldmark";
189 if (!m_sBookmarkName.isEmpty())
191 sal_uInt16 nTmp;
192 if (SvXMLUnitConverter::convertEnum(nTmp, GetLocalName(),
193 lcl_aMarkTypeMap))
195 switch ((lcl_MarkType)nTmp)
197 case TypeReference:
198 // export point reference mark
199 CreateAndInsertMark(GetImport(),
200 sAPI_reference_mark,
201 m_sBookmarkName,
202 m_rHelper.GetCursorAsRange()->getStart(),
203 OUString());
204 break;
206 case TypeFieldmark:
207 case TypeBookmark:
209 const char *formFieldmarkName=lcl_getFormFieldmarkName(m_sFieldName);
210 bool bImportAsField=((lcl_MarkType)nTmp==TypeFieldmark && formFieldmarkName!=NULL); //@TODO handle abbreviation cases..
211 // export point bookmark
212 const Reference<XInterface> xContent(
213 CreateAndInsertMark(GetImport(),
214 (bImportAsField ? OUString(sAPI_formfieldmark) : OUString(sAPI_bookmark)),
215 m_sBookmarkName,
216 m_rHelper.GetCursorAsRange()->getStart(),
217 m_sXmlId) );
218 if ((lcl_MarkType)nTmp==TypeFieldmark) {
219 if (xContent.is() && bImportAsField) {
220 // setup fieldmark...
221 Reference< ::com::sun::star::text::XFormField> xFormField(xContent, UNO_QUERY);
222 xFormField->setFieldType(OUString::createFromAscii(formFieldmarkName));
223 if (xFormField.is() && m_rHelper.hasCurrentFieldCtx()) {
224 m_rHelper.setCurrentFieldParamsTo(xFormField);
227 m_rHelper.popFieldCtx();
230 break;
232 case TypeFieldmarkStart:
233 case TypeBookmarkStart:
234 // save XTextRange for later construction of bookmark
236 std::shared_ptr< ::xmloff::ParsedRDFaAttributes >
237 xRDFaAttributes;
238 if (m_bHaveAbout && (TypeBookmarkStart
239 == static_cast<lcl_MarkType>(nTmp)))
241 xRDFaAttributes =
242 GetImport().GetRDFaImportHelper().ParseRDFa(
243 m_sAbout, m_sProperty,
244 m_sContent, m_sDatatype);
246 m_rHelper.InsertBookmarkStartRange(
247 m_sBookmarkName,
248 m_rHelper.GetCursorAsRange()->getStart(),
249 m_sXmlId, xRDFaAttributes);
251 break;
253 case TypeFieldmarkEnd:
254 case TypeBookmarkEnd:
256 // get old range, and construct
257 Reference<XTextRange> xStartRange;
258 std::shared_ptr< ::xmloff::ParsedRDFaAttributes >
259 xRDFaAttributes;
260 if (m_rHelper.FindAndRemoveBookmarkStartRange(
261 m_sBookmarkName, xStartRange,
262 m_sXmlId, xRDFaAttributes))
264 Reference<XTextRange> xEndRange(
265 m_rHelper.GetCursorAsRange()->getStart());
267 // check if beginning and end are in same XText
268 if (xStartRange->getText() == xEndRange->getText())
270 // create range for insertion
271 Reference<XTextCursor> xInsertionCursor =
272 m_rHelper.GetText()->createTextCursorByRange(
273 xEndRange);
274 try {
275 xInsertionCursor->gotoRange(xStartRange, sal_True);
276 } catch (uno::Exception&) {
277 OSL_ENSURE(false,
278 "cannot go to end position of bookmark");
281 //DBG_ASSERT(! xInsertionCursor->isCollapsed(),
282 // "we want no point mark");
283 // can't assert, because someone could
284 // create a file with subsequence
285 // start/end elements
287 bool bImportAsField=((lcl_MarkType)nTmp==TypeFieldmarkEnd && m_rHelper.hasCurrentFieldCtx());
289 // fdo#86795 check if it's actually a checkbox first
290 bool isInvalid(false);
291 OUString fieldmarkTypeName;
292 if (bImportAsField && m_rHelper.hasCurrentFieldCtx())
295 OUString const type(m_rHelper.getCurrentFieldType());
296 fieldmarkTypeName = lcl_getFieldmarkName(type);
297 if (fieldmarkTypeName == ODF_FORMCHECKBOX ||
298 fieldmarkTypeName == ODF_FORMDROPDOWN)
299 { // sw can't handle checkbox with start+end
300 SAL_INFO("xmloff.text", "invalid fieldmark-start/fieldmark-end ignored");
301 isInvalid = true;
305 Reference<XInterface> xContent;
306 if (!isInvalid)
308 // insert reference
309 xContent = CreateAndInsertMark(GetImport(),
310 (bImportAsField
311 ? OUString(sAPI_fieldmark)
312 : OUString(sAPI_bookmark)),
313 m_sBookmarkName,
314 xInsertionCursor,
315 m_sXmlId);
316 if (xRDFaAttributes)
318 const Reference<rdf::XMetadatable>
319 xMeta(xContent, UNO_QUERY);
320 GetImport().GetRDFaImportHelper().AddRDFa(
321 xMeta, xRDFaAttributes);
325 if ((lcl_MarkType)nTmp==TypeFieldmarkEnd) {
326 if (xContent.is() && bImportAsField) {
327 // setup fieldmark...
328 Reference< ::com::sun::star::text::XFormField> xFormField(xContent, UNO_QUERY);
329 if (xFormField.is() && m_rHelper.hasCurrentFieldCtx()) {
331 xFormField->setFieldType(fieldmarkTypeName);
332 m_rHelper.setCurrentFieldParamsTo(xFormField);
335 m_rHelper.popFieldCtx();
338 // else: beginning/end in different XText -> ignore!
340 // else: no start found -> ignore!
341 break;
344 case TypeReferenceStart:
345 case TypeReferenceEnd:
346 OSL_FAIL("reference start/end are handled in txtparai !");
347 break;
349 default:
350 OSL_FAIL("unknown mark type");
351 break;
357 SvXMLImportContext *XMLTextMarkImportContext::CreateChildContext( sal_uInt16 nPrefix,
358 const OUString& rLocalName,
359 const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList >& )
361 return new XMLFieldParamImportContext(GetImport(), m_rHelper,
362 nPrefix, rLocalName);
366 Reference<XTextContent> XMLTextMarkImportContext::CreateAndInsertMark(
367 SvXMLImport& rImport,
368 const OUString& sServiceName,
369 const OUString& sMarkName,
370 const Reference<XTextRange> & rRange,
371 const OUString& i_rXmlId)
373 // create mark
374 const Reference<XMultiServiceFactory> xFactory(rImport.GetModel(),
375 UNO_QUERY);
376 Reference<XInterface> xIfc;
378 if (xFactory.is())
380 xIfc = xFactory->createInstance(sServiceName);
382 if (!xIfc.is())
384 OSL_FAIL("CreateAndInsertMark: cannot create service?");
385 return 0;
388 // set name (unless there is no name (text:meta))
389 const Reference<XNamed> xNamed(xIfc, UNO_QUERY);
390 if (xNamed.is())
392 xNamed->setName(sMarkName);
394 else
396 if (!sMarkName.isEmpty())
398 OSL_FAIL("name given, but XNamed not supported?");
399 return 0;
403 // cast to XTextContent and attach to document
404 const Reference<XTextContent> xTextContent(xIfc, UNO_QUERY);
405 if (xTextContent.is())
409 // if inserting marks, bAbsorb==sal_False will cause
410 // collapsing of the given XTextRange.
411 rImport.GetTextImport()->GetText()->insertTextContent(rRange,
412 xTextContent, sal_True);
414 // xml:id for RDF metadata -- after insertion!
415 rImport.SetXmlId(xIfc, i_rXmlId);
417 return xTextContent;
419 catch (com::sun::star::lang::IllegalArgumentException &)
421 OSL_FAIL("CreateAndInsertMark: cannot insert?");
422 return 0;
426 return 0;
429 bool XMLTextMarkImportContext::FindName(
430 SvXMLImport& rImport,
431 const Reference<XAttributeList> & xAttrList)
433 bool bNameOK = false;
435 // find name attribute first
436 const sal_Int16 nLength = xAttrList->getLength();
437 for(sal_Int16 nAttr = 0; nAttr < nLength; nAttr++)
439 OUString sLocalName;
440 const sal_uInt16 nPrefix = rImport.GetNamespaceMap().
441 GetKeyByAttrName( xAttrList->getNameByIndex(nAttr),
442 &sLocalName );
444 if ( (XML_NAMESPACE_TEXT == nPrefix) &&
445 IsXMLToken(sLocalName, XML_NAME) )
447 m_sBookmarkName = xAttrList->getValueByIndex(nAttr);
448 bNameOK = true;
450 else if ( (XML_NAMESPACE_XML == nPrefix) &&
451 IsXMLToken(sLocalName, XML_ID) )
453 m_sXmlId = xAttrList->getValueByIndex(nAttr);
455 else if ( XML_NAMESPACE_XHTML == nPrefix )
457 // RDFa
458 if ( IsXMLToken( sLocalName, XML_ABOUT) )
460 m_sAbout = xAttrList->getValueByIndex(nAttr);
461 m_bHaveAbout = true;
463 else if ( IsXMLToken( sLocalName, XML_PROPERTY) )
465 m_sProperty = xAttrList->getValueByIndex(nAttr);
467 else if ( IsXMLToken( sLocalName, XML_CONTENT) )
469 m_sContent = xAttrList->getValueByIndex(nAttr);
471 else if ( IsXMLToken( sLocalName, XML_DATATYPE) )
473 m_sDatatype = xAttrList->getValueByIndex(nAttr);
476 else if ( (XML_NAMESPACE_FIELD == nPrefix) &&
477 IsXMLToken(sLocalName, XML_TYPE) )
479 m_sFieldName = xAttrList->getValueByIndex(nAttr);
483 return bNameOK;
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */