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