xref: /trunk/main/xmloff/source/text/XMLTextMarkImportContext.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmloff.hxx"
30 
31 
32 #include "XMLTextMarkImportContext.hxx"
33 
34 
35 #include <rtl/ustring.hxx>
36 #include <tools/debug.hxx>
37 #include <xmloff/xmluconv.hxx>
38 #include <xmloff/xmltoken.hxx>
39 #include <xmloff/xmlimp.hxx>
40 #include <xmloff/nmspmap.hxx>
41 #include "xmloff/xmlnmspe.hxx"
42 #include <xmloff/odffields.hxx>
43 #include <com/sun/star/xml/sax/XAttributeList.hpp>
44 #include <com/sun/star/text/XTextContent.hpp>
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
47 #include <com/sun/star/container/XNamed.hpp>
48 #include <com/sun/star/rdf/XMetadatable.hpp>
49 
50 #include <com/sun/star/text/XFormField.hpp>
51 
52 #include "RDFaImportHelper.hxx"
53 
54 
55 using ::rtl::OUString;
56 using ::rtl::OUStringBuffer;
57 
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::text;
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::beans;
62 using namespace ::com::sun::star::lang;
63 using namespace ::com::sun::star::container;
64 using namespace ::com::sun::star::xml::sax;
65 using namespace ::xmloff::token;
66 
67 
68 XMLFieldParamImportContext::XMLFieldParamImportContext(
69     SvXMLImport& rImport,
70     XMLTextImportHelper& rHlp,
71     sal_uInt16 nPrefix,
72     const OUString& rLocalName ) :
73         SvXMLImportContext(rImport, nPrefix, rLocalName),
74         rHelper(rHlp)
75 {
76 }
77 
78 
79 void XMLFieldParamImportContext::StartElement(const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList> & xAttrList)
80 {
81     SvXMLImport& rImport = GetImport();
82     ::rtl::OUString sName;
83     ::rtl::OUString sValue;
84 
85     sal_Int16 nLength = xAttrList->getLength();
86     for(sal_Int16 nAttr = 0; nAttr < nLength; nAttr++)
87     {
88         OUString sLocalName;
89         sal_uInt16 nPrefix = rImport.GetNamespaceMap().
90             GetKeyByAttrName( xAttrList->getNameByIndex(nAttr),
91                               &sLocalName );
92 
93         if ( (XML_NAMESPACE_FIELD == nPrefix) &&
94              IsXMLToken(sLocalName, XML_NAME)   )
95         {
96             sName = xAttrList->getValueByIndex(nAttr);
97         }
98         if ( (XML_NAMESPACE_FIELD == nPrefix) &&
99              IsXMLToken(sLocalName, XML_VALUE)   )
100         {
101             sValue = xAttrList->getValueByIndex(nAttr);
102         }
103     }
104     if (rHelper.hasCurrentFieldCtx() && sName.getLength()>0) {
105         rHelper.addFieldParam(sName, sValue);
106     }
107 }
108 
109 
110 TYPEINIT1( XMLTextMarkImportContext, SvXMLImportContext);
111 
112 XMLTextMarkImportContext::XMLTextMarkImportContext(
113     SvXMLImport& rImport,
114     XMLTextImportHelper& rHlp,
115     sal_uInt16 nPrefix,
116     const OUString& rLocalName )
117     : SvXMLImportContext(rImport, nPrefix, rLocalName)
118     , m_rHelper(rHlp)
119     , m_bHaveAbout(false)
120 {
121 }
122 
123 enum lcl_MarkType { TypeReference, TypeReferenceStart, TypeReferenceEnd,
124                     TypeBookmark, TypeBookmarkStart, TypeBookmarkEnd,
125                     TypeFieldmark, TypeFieldmarkStart, TypeFieldmarkEnd
126                   };
127 
128 static SvXMLEnumMapEntry __READONLY_DATA lcl_aMarkTypeMap[] =
129 {
130     { XML_REFERENCE_MARK,           TypeReference },
131     { XML_REFERENCE_MARK_START,     TypeReferenceStart },
132     { XML_REFERENCE_MARK_END,       TypeReferenceEnd },
133     { XML_BOOKMARK,                 TypeBookmark },
134     { XML_BOOKMARK_START,           TypeBookmarkStart },
135     { XML_BOOKMARK_END,             TypeBookmarkEnd },
136     { XML_FIELDMARK,                TypeFieldmark },
137     { XML_FIELDMARK_START,          TypeFieldmarkStart },
138     { XML_FIELDMARK_END,            TypeFieldmarkEnd },
139     { XML_TOKEN_INVALID,            0 },
140 };
141 
142 
143 static const char *lcl_getFormFieldmarkName(rtl::OUString &name)
144 {
145     static const char sCheckbox[]=ODF_FORMCHECKBOX;
146     static const char sFormDropDown[]=ODF_FORMDROPDOWN;
147     if (name.compareToAscii("msoffice.field.FORMCHECKBOX")==0)
148         return sCheckbox;
149     else if (name.compareToAscii(ODF_FORMCHECKBOX)==0)
150         return sCheckbox;
151     if (name.compareToAscii(ODF_FORMDROPDOWN)==0)
152         return sFormDropDown;
153     else
154         return NULL;
155 }
156 
157 static rtl::OUString lcl_getFieldmarkName(rtl::OUString &name)
158 {
159     static const char sFormtext[]=ODF_FORMTEXT;
160     if (name.compareToAscii("msoffice.field.FORMTEXT")==0)
161         return rtl::OUString::createFromAscii(sFormtext);
162     else if (name.compareToAscii(ODF_FORMTEXT)==0)
163         return rtl::OUString::createFromAscii(sFormtext);
164     else
165         return name;
166 }
167 
168 
169 void XMLTextMarkImportContext::StartElement(
170     const Reference<XAttributeList> & xAttrList)
171 {
172     if (!FindName(GetImport(), xAttrList))
173     {
174         m_sBookmarkName = OUString();
175     }
176 
177     if (IsXMLToken(GetLocalName(), XML_FIELDMARK_END))
178     {
179         m_sBookmarkName = m_rHelper.FindActiveBookmarkName();
180     }
181 
182     if (IsXMLToken(GetLocalName(), XML_FIELDMARK_START) || IsXMLToken(GetLocalName(), XML_FIELDMARK))
183     {
184         if (m_sBookmarkName.getLength() == 0)
185         {
186             m_sBookmarkName = ::rtl::OUString::createFromAscii("Unknown");
187         }
188         m_rHelper.pushFieldCtx( m_sBookmarkName, m_sFieldName );
189     }
190 }
191 
192 void XMLTextMarkImportContext::EndElement()
193 {
194     SvXMLImportContext::EndElement();
195 
196     static const OUString sAPI_reference_mark(
197         RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.ReferenceMark"));
198     static const OUString sAPI_bookmark(
199         RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.Bookmark"));
200     static const OUString sAPI_fieldmark(
201         RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.Fieldmark"));
202     static const OUString sAPI_formfieldmark(
203         RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.FormFieldmark"));
204 
205     if (m_sBookmarkName.getLength() > 0)
206     {
207         sal_uInt16 nTmp;
208         if (SvXMLUnitConverter::convertEnum(nTmp, GetLocalName(),
209                                             lcl_aMarkTypeMap))
210         {
211             switch ((lcl_MarkType)nTmp)
212             {
213                 case TypeReference:
214                     // export point reference mark
215                     CreateAndInsertMark(GetImport(),
216                         sAPI_reference_mark,
217                         m_sBookmarkName,
218                         m_rHelper.GetCursorAsRange()->getStart(),
219                         ::rtl::OUString());
220                     break;
221 
222                 case TypeFieldmark:
223                 case TypeBookmark:
224                     {
225                         const char *formFieldmarkName=lcl_getFormFieldmarkName(m_sFieldName);
226                         bool bImportAsField=((lcl_MarkType)nTmp==TypeFieldmark && formFieldmarkName!=NULL); //@TODO handle abbreviation cases..
227                         // export point bookmark
228                         const Reference<XInterface> xContent(
229                             CreateAndInsertMark(GetImport(),
230                                         (bImportAsField?sAPI_formfieldmark:sAPI_bookmark),
231                                 m_sBookmarkName,
232                                 m_rHelper.GetCursorAsRange()->getStart(),
233                                 m_sXmlId) );
234                         if ((lcl_MarkType)nTmp==TypeFieldmark) {
235                             if (xContent.is() && bImportAsField) {
236                                 // setup fieldmark...
237                                 Reference< ::com::sun::star::text::XFormField> xFormField(xContent, UNO_QUERY);
238                                 xFormField->setFieldType(rtl::OUString::createFromAscii(formFieldmarkName));
239                                 if (xFormField.is() && m_rHelper.hasCurrentFieldCtx()) {
240                                     m_rHelper.setCurrentFieldParamsTo(xFormField);
241                                 }
242                             }
243                             m_rHelper.popFieldCtx();
244                         }
245                     }
246                     break;
247 
248                 case TypeFieldmarkStart:
249                 case TypeBookmarkStart:
250                     // save XTextRange for later construction of bookmark
251                     {
252                         ::boost::shared_ptr< ::xmloff::ParsedRDFaAttributes >
253                             pRDFaAttributes;
254                         if (m_bHaveAbout && (TypeBookmarkStart
255                                 == static_cast<lcl_MarkType>(nTmp)))
256                         {
257                             pRDFaAttributes =
258                                 GetImport().GetRDFaImportHelper().ParseRDFa(
259                                     m_sAbout, m_sProperty,
260                                     m_sContent, m_sDatatype);
261                         }
262                         m_rHelper.InsertBookmarkStartRange(
263                             m_sBookmarkName,
264                             m_rHelper.GetCursorAsRange()->getStart(),
265                             m_sXmlId, pRDFaAttributes);
266                     }
267                     break;
268 
269                 case TypeFieldmarkEnd:
270                 case TypeBookmarkEnd:
271                 {
272                     // get old range, and construct
273                     Reference<XTextRange> xStartRange;
274                     ::boost::shared_ptr< ::xmloff::ParsedRDFaAttributes >
275                         pRDFaAttributes;
276                     if (m_rHelper.FindAndRemoveBookmarkStartRange(
277                             m_sBookmarkName, xStartRange,
278                             m_sXmlId, pRDFaAttributes))
279                     {
280                         Reference<XTextRange> xEndRange(
281                             m_rHelper.GetCursorAsRange()->getStart());
282 
283                         // check if beginning and end are in same XText
284                         if (xStartRange->getText() == xEndRange->getText())
285                         {
286                             // create range for insertion
287                             Reference<XTextCursor> xInsertionCursor =
288                                 m_rHelper.GetText()->createTextCursorByRange(
289                                     xEndRange);
290                             try {
291                             xInsertionCursor->gotoRange(xStartRange, sal_True);
292                             } catch (uno::Exception&) {
293                                 OSL_ENSURE(false,
294                                     "cannot go to end position of bookmark");
295                             }
296 
297                             //DBG_ASSERT(! xInsertionCursor->isCollapsed(),
298                             //              "we want no point mark");
299                             // can't assert, because someone could
300                             // create a file with subsequence
301                             // start/end elements
302 
303                             Reference<XTextRange> xInsertionRange(
304                                 xInsertionCursor, UNO_QUERY);
305 
306                             bool bImportAsField=((lcl_MarkType)nTmp==TypeFieldmarkEnd && m_rHelper.hasCurrentFieldCtx());
307 
308                             // insert reference
309                             const Reference<XInterface> xContent(
310                                 CreateAndInsertMark(GetImport(),
311                                                 (bImportAsField?sAPI_fieldmark:sAPI_bookmark),
312                                     m_sBookmarkName,
313                                     xInsertionRange,
314                                     m_sXmlId) );
315                             if (pRDFaAttributes)
316                             {
317                                 const Reference<rdf::XMetadatable>
318                                     xMeta(xContent, UNO_QUERY);
319                                 GetImport().GetRDFaImportHelper().AddRDFa(
320                                     xMeta, pRDFaAttributes);
321                             }
322 
323                             if ((lcl_MarkType)nTmp==TypeFieldmarkEnd) {
324                                 if (xContent.is() && bImportAsField) {
325                                     // setup fieldmark...
326                                     Reference< ::com::sun::star::text::XFormField> xFormField(xContent, UNO_QUERY);
327                                     if (xFormField.is() && m_rHelper.hasCurrentFieldCtx()) {
328                                         rtl::OUString givenTypeName=m_rHelper.getCurrentFieldType();
329                                         rtl::OUString fieldmarkTypeName=lcl_getFieldmarkName(givenTypeName);
330 
331                                         xFormField->setFieldType(fieldmarkTypeName);
332                                         m_rHelper.setCurrentFieldParamsTo(xFormField);
333                                     }
334                                 }
335                                 m_rHelper.popFieldCtx();
336                             }
337                         }
338                         // else: beginning/end in different XText -> ignore!
339                     }
340                     // else: no start found -> ignore!
341                     break;
342                 }
343 
344                 case TypeReferenceStart:
345                 case TypeReferenceEnd:
346                     DBG_ERROR("reference start/end are handled in txtparai !");
347                     break;
348 
349                 default:
350                     DBG_ERROR("unknown mark type");
351                     break;
352             }
353         }
354     }
355 }
356 
357 SvXMLImportContext *XMLTextMarkImportContext::CreateChildContext( sal_uInt16 nPrefix,
358                                         const ::rtl::OUString& rLocalName,
359                                         const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList >&  )
360 {
361     return new XMLFieldParamImportContext(GetImport(), m_rHelper,
362                 nPrefix, rLocalName);
363 }
364 
365 
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)
372 {
373     // create mark
374     const Reference<XMultiServiceFactory> xFactory(rImport.GetModel(),
375         UNO_QUERY);
376     Reference<XInterface> xIfc;
377 
378     if (xFactory.is())
379     {
380         xIfc = xFactory->createInstance(sServiceName);
381 
382         if (!xIfc.is())
383         {
384             OSL_ENSURE(false, "CreateAndInsertMark: cannot create service?");
385             return 0;
386         }
387 
388         // set name (unless there is no name (text:meta))
389         const Reference<XNamed> xNamed(xIfc, UNO_QUERY);
390         if (xNamed.is())
391         {
392             xNamed->setName(sMarkName);
393         }
394         else
395         {
396             if (sMarkName.getLength())
397             {
398                 OSL_ENSURE(false, "name given, but XNamed not supported?");
399                 return 0;
400             }
401         }
402 
403         // cast to XTextContent and attach to document
404         const Reference<XTextContent> xTextContent(xIfc, UNO_QUERY);
405         if (xTextContent.is())
406         {
407             try
408             {
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);
413 
414                 // xml:id for RDF metadata -- after insertion!
415                 rImport.SetXmlId(xIfc, i_rXmlId);
416 
417                 return xTextContent;
418             }
419             catch (com::sun::star::lang::IllegalArgumentException &)
420             {
421                 OSL_ENSURE(false, "CreateAndInsertMark: cannot insert?");
422                 return 0;
423             }
424         }
425     }
426     return 0;
427 }
428 
429 sal_Bool XMLTextMarkImportContext::FindName(
430     SvXMLImport& rImport,
431     const Reference<XAttributeList> & xAttrList)
432 {
433     sal_Bool bNameOK = sal_False;
434 
435     // find name attribute first
436     const sal_Int16 nLength = xAttrList->getLength();
437     for(sal_Int16 nAttr = 0; nAttr < nLength; nAttr++)
438     {
439         OUString sLocalName;
440         const sal_uInt16 nPrefix = rImport.GetNamespaceMap().
441             GetKeyByAttrName( xAttrList->getNameByIndex(nAttr),
442                               &sLocalName );
443 
444         if ( (XML_NAMESPACE_TEXT == nPrefix) &&
445              IsXMLToken(sLocalName, XML_NAME)   )
446         {
447             m_sBookmarkName = xAttrList->getValueByIndex(nAttr);
448             bNameOK = sal_True;
449         }
450         else if ( (XML_NAMESPACE_XML == nPrefix) &&
451              IsXMLToken(sLocalName, XML_ID)   )
452         {
453             m_sXmlId = xAttrList->getValueByIndex(nAttr);
454         }
455         else if ( XML_NAMESPACE_XHTML == nPrefix )
456         {
457             // RDFa
458             if ( IsXMLToken( sLocalName, XML_ABOUT) )
459             {
460                 m_sAbout = xAttrList->getValueByIndex(nAttr);
461                 m_bHaveAbout = true;
462             }
463             else if ( IsXMLToken( sLocalName, XML_PROPERTY) )
464             {
465                 m_sProperty = xAttrList->getValueByIndex(nAttr);
466             }
467             else if ( IsXMLToken( sLocalName, XML_CONTENT) )
468             {
469                 m_sContent = xAttrList->getValueByIndex(nAttr);
470             }
471             else if ( IsXMLToken( sLocalName, XML_DATATYPE) )
472             {
473                 m_sDatatype = xAttrList->getValueByIndex(nAttr);
474             }
475         }
476         else if ( (XML_NAMESPACE_FIELD == nPrefix) &&
477              IsXMLToken(sLocalName, XML_TYPE)   )
478         {
479             m_sFieldName = xAttrList->getValueByIndex(nAttr);
480         }
481     }
482 
483     return bNameOK;
484 }
485 
486