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