1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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(
58 XMLTextImportHelper
& rHlp
,
60 const OUString
& rLocalName
) :
61 SvXMLImportContext(rImport
, nPrefix
, rLocalName
),
67 void XMLFieldParamImportContext::StartElement(const ::com::sun::star::uno::Reference
< ::com::sun::star::xml::sax::XAttributeList
> & xAttrList
)
69 SvXMLImport
& rImport
= GetImport();
73 sal_Int16 nLength
= xAttrList
->getLength();
74 for(sal_Int16 nAttr
= 0; nAttr
< nLength
; nAttr
++)
77 sal_uInt16 nPrefix
= rImport
.GetNamespaceMap().
78 GetKeyByAttrName( xAttrList
->getNameByIndex(nAttr
),
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
,
104 const OUString
& rLocalName
)
105 : SvXMLImportContext(rImport
, nPrefix
, rLocalName
)
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")
138 else if (name
== ODF_FORMCHECKBOX
)
140 if (name
== ODF_FORMDROPDOWN
||
141 name
== "ecma.office-open-xml.field.FORMDROPDOWN")
142 return sFormDropDown
;
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
);
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())
192 if (SvXMLUnitConverter::convertEnum(nTmp
, GetLocalName(),
195 switch ((lcl_MarkType
)nTmp
)
198 // export point reference mark
199 CreateAndInsertMark(GetImport(),
202 m_rHelper
.GetCursorAsRange()->getStart(),
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
)),
216 m_rHelper
.GetCursorAsRange()->getStart(),
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();
232 case TypeFieldmarkStart
:
233 case TypeBookmarkStart
:
234 // save XTextRange for later construction of bookmark
236 std::shared_ptr
< ::xmloff::ParsedRDFaAttributes
>
238 if (m_bHaveAbout
&& (TypeBookmarkStart
239 == static_cast<lcl_MarkType
>(nTmp
)))
242 GetImport().GetRDFaImportHelper().ParseRDFa(
243 m_sAbout
, m_sProperty
,
244 m_sContent
, m_sDatatype
);
246 m_rHelper
.InsertBookmarkStartRange(
248 m_rHelper
.GetCursorAsRange()->getStart(),
249 m_sXmlId
, xRDFaAttributes
);
253 case TypeFieldmarkEnd
:
254 case TypeBookmarkEnd
:
256 // get old range, and construct
257 Reference
<XTextRange
> xStartRange
;
258 std::shared_ptr
< ::xmloff::ParsedRDFaAttributes
>
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(
275 xInsertionCursor
->gotoRange(xStartRange
, sal_True
);
276 } catch (uno::Exception
&) {
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");
305 Reference
<XInterface
> xContent
;
309 xContent
= CreateAndInsertMark(GetImport(),
311 ? OUString(sAPI_fieldmark
)
312 : OUString(sAPI_bookmark
)),
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!
344 case TypeReferenceStart
:
345 case TypeReferenceEnd
:
346 OSL_FAIL("reference start/end are handled in txtparai !");
350 OSL_FAIL("unknown mark type");
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
)
374 const Reference
<XMultiServiceFactory
> xFactory(rImport
.GetModel(),
376 Reference
<XInterface
> xIfc
;
380 xIfc
= xFactory
->createInstance(sServiceName
);
384 OSL_FAIL("CreateAndInsertMark: cannot create service?");
388 // set name (unless there is no name (text:meta))
389 const Reference
<XNamed
> xNamed(xIfc
, UNO_QUERY
);
392 xNamed
->setName(sMarkName
);
396 if (!sMarkName
.isEmpty())
398 OSL_FAIL("name given, but XNamed not supported?");
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
);
419 catch (com::sun::star::lang::IllegalArgumentException
&)
421 OSL_FAIL("CreateAndInsertMark: cannot insert?");
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
++)
440 const sal_uInt16 nPrefix
= rImport
.GetNamespaceMap().
441 GetKeyByAttrName( xAttrList
->getNameByIndex(nAttr
),
444 if ( (XML_NAMESPACE_TEXT
== nPrefix
) &&
445 IsXMLToken(sLocalName
, XML_NAME
) )
447 m_sBookmarkName
= xAttrList
->getValueByIndex(nAttr
);
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
)
458 if ( IsXMLToken( sLocalName
, XML_ABOUT
) )
460 m_sAbout
= xAttrList
->getValueByIndex(nAttr
);
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
);
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */